/** * Copyright schukai GmbH and contributors 2023. All Rights Reserved. * Node module: @schukai/monster * This file is licensed under the AGPLv3 License. * License text available at https://www.gnu.org/licenses/agpl-3.0.en.html */ 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 * @memberOf Monster.DOM * @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 * @memberOf Monster.DOM * @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( "value 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 * @memberOf Monster.DOM * @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; }