@schukai/monster
Version:
Monster is a simple library for creating fast, robust and lightweight websites.
141 lines (125 loc) • 4.11 kB
JavaScript
/**
* Copyright © schukai GmbH and all contributing authors, {{copyRightYear}}. All rights reserved.
* Node module: @schukai/monster
*
* This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3).
* The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html
*
* For those who do not wish to adhere to the AGPLv3, a commercial license is available.
* Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms.
* For more information about purchasing a commercial license, please contact schukai GmbH.
*
* SPDX-License-Identifier: AGPL-3.0
*/
import { isArray, isObject } from "../types/is.mjs";
import { validateInstance, validateString } from "../types/validate.mjs";
import { getDocument } from "./util.mjs";
export { fireEvent, fireCustomEvent, findTargetElementFromEvent };
/**
* The function sends an event
*
* @param {Element | Node | HTMLCollection | NodeList} element
* @param {string} type
* @return {void}
* @license AGPLv3
* @since 1.10.0
* @copyright schukai GmbH
* @throws {TypeError} value is not an instance of HTMLElement or HTMLCollection
* @summary Construct and send and event
*/
function fireEvent(element, type) {
const document = getDocument();
if (element instanceof HTMLElement) {
if (type === "click") {
element.click();
return;
}
// https://developer.mozilla.org/en-US/docs/Web/API/Event/Event
const event = new Event(validateString(type), {
bubbles: true,
cancelable: true,
composed: true,
});
element.dispatchEvent(event);
} else if (element instanceof HTMLCollection || element instanceof NodeList) {
for (const e of element) {
fireEvent(e, type);
}
} else {
throw new TypeError(
"value is not an instance of HTMLElement or HTMLCollection",
);
}
}
/**
* You can call the function via the monster namespace `new Monster.DOM.fireCustomEvent()`.
*
* @param {Element | Node | HTMLCollection | NodeList} element
* @param {string} type
* @param {object} detail
* @return {void}
* @license AGPLv3
* @since 1.29.0
* @copyright schukai GmbH
* @throws {TypeError} value is not an instance of HTMLElement or HTMLCollection
* @summary Construct and send and event
*/
function fireCustomEvent(element, type, detail) {
if (element instanceof HTMLElement) {
if (!isObject(detail)) {
detail = { detail };
}
const event = new CustomEvent(validateString(type), {
bubbles: true,
cancelable: true,
composed: true,
detail,
});
element.dispatchEvent(event);
} else if (element instanceof HTMLCollection || element instanceof NodeList) {
for (const e of element) {
fireCustomEvent(e, type, detail);
}
} else {
throw new TypeError(
"element is not an instance of HTMLElement or HTMLCollection",
);
}
}
/**
* This function gets the path `Event.composedPath()` from an event and tries to find the next element
* up the tree `element.closest()` with the attribute and value. If no value, or a value that is undefined or null,
* is specified, only the attribute is searched.
*
* @license AGPLv3
* @since 1.14.0
* @param {Event} event
* @param {string} attributeName
* @param {string|null|undefined} attributeValue
* @throws {Error} unsupported event
* @throws {TypeError} value is not a string
* @throws {TypeError} value is not an instance of HTMLElement
* @summary Help function to find the appropriate control
*/
function findTargetElementFromEvent(event, attributeName, attributeValue) {
validateInstance(event, Event);
if (typeof event.composedPath !== "function") {
throw new Error("unsupported event");
}
const path = event.composedPath();
// closest cannot be used here, because closest is not correct for slotted elements
if (isArray(path)) {
for (let i = 0; i < path.length; i++) {
const o = path[i];
if (
o instanceof HTMLElement &&
o.hasAttribute(attributeName) &&
(attributeValue === undefined ||
o.getAttribute(attributeName) === attributeValue)
) {
return o;
}
}
}
return undefined;
}