UNPKG

vjsrouter

Version:

A modern, file-system based router for vanilla JavaScript with SSR support.

101 lines (87 loc) 4.08 kB
// File: src/utils/DOMUtils.js import { logger, LogLevel } from './Logger.js'; /** * @description A collection of utility functions for safe and efficient DOM manipulation. * These functions are designed to be robust, handling potential null elements gracefully * and providing clear logging for debugging purposes. */ export const DOMUtils = { /** * @description Safely clears all child nodes from a given parent element. * This is more performant than setting `innerHTML = ''` for complex DOM trees * as it avoids reparsing the HTML and properly removes event listeners on child nodes. * * @param {HTMLElement|null} parentElement - The DOM element to clear. * @returns {boolean} - True if the element was cleared successfully, false otherwise. */ clearElement(parentElement) { const source = 'DOMUtils.clearElement'; if (!(parentElement instanceof HTMLElement)) { logger.warn(source, 'Provided argument is not a valid HTMLElement. Cannot clear.', { parentElement }); return false; } logger.debug(source, 'Clearing element...', { element: parentElement }); while (parentElement.firstChild) { parentElement.removeChild(parentElement.firstChild); } logger.debug(source, 'Element cleared successfully.'); return true; }, /** * @description Appends a child element to a parent element with safety checks. * * @param {HTMLElement|null} parentElement - The element to append the child to. * @param {Node|null} childElement - The node (e.g., an HTMLElement, TextNode) to append. * @returns {boolean} - True if the child was appended successfully, false otherwise. */ appendChild(parentElement, childElement) { const source = 'DOMUtils.appendChild'; if (!(parentElement instanceof HTMLElement)) { logger.error(source, 'Parent element is not a valid HTMLElement. Cannot append child.', { parentElement }); return false; } if (!(childElement instanceof Node)) { logger.error(source, 'Child element is not a valid Node. Cannot append.', { childElement }); return false; } logger.debug(source, 'Appending child to parent.', { parent: parentElement, child: childElement }); parentElement.appendChild(childElement); return true; }, /** * @description Sets the document's title, including a fallback for safety. * * @param {string} title - The title to set. If empty or not a string, a default title will be used. */ setDocumentTitle(title) { const source = 'DOMUtils.setDocumentTitle'; if (typeof title === 'string' && title.trim() !== '') { document.title = title; logger.debug(source, `Document title set to "${title}".`); } else { document.title = 'vjsrouter App'; logger.warn(source, `Provided title was invalid. Using default title.`, { providedTitle: title }); } }, /** * @description Finds the closest ancestor of an element that matches a given selector. * This is particularly useful for event delegation, for example, finding if a click * happened inside a link, even if the target was a nested element like a <span> or <img>. * * @param {EventTarget|null} startElement - The element to start the search from (e.g., event.target). * @param {string} selector - The CSS selector to find (e.g., 'a[href]'). * @returns {HTMLElement|null} - The matching ancestor element, or null if no match is found. */ findClosestAncestor(startElement, selector) { const source = 'DOMUtils.findClosestAncestor'; if (!(startElement instanceof Element)) { logger.debug(source, 'Provided startElement is not a valid Element. Cannot find ancestor.', { startElement }); return null; } // The `closest()` method is a modern, efficient, and built-in way to do this. // It starts with the element itself and travels up the DOM tree. const matchingElement = startElement.closest(selector); logger.debug(source, `Search for selector "${selector}" found:`, { matchingElement }); return matchingElement; } };