export function isTextNode(node: Node): node is Text { return node.nodeType === Node.TEXT_NODE } export function isCommentNode(node: Node): node is Comment { return node.nodeType === Node.COMMENT_NODE } export function isElementNode(node: Node): node is Element { return node.nodeType === Node.ELEMENT_NODE } export function isNodeShadowHost(node: Node): node is Element & { shadowRoot: ShadowRoot } { return isElementNode(node) && Boolean(node.shadowRoot) } export function isNodeShadowRoot(node: Node): node is ShadowRoot { const shadowRoot = node as ShadowRoot return !!shadowRoot.host && shadowRoot.nodeType === Node.DOCUMENT_FRAGMENT_NODE && isElementNode(shadowRoot.host) } export function hasChildNodes(node: Node) { return node.childNodes.length > 0 || isNodeShadowHost(node) } export function forEachChildNodes(node: Node, callback: (child: Node) => void) { let child = node.firstChild while (child) { callback(child) child = child.nextSibling } if (isNodeShadowHost(node)) { callback(node.shadowRoot) } } /** * Return `host` in case if the current node is a shadow root otherwise will return the `parentNode` */ export function getParentNode(node: Node): Node | null { return isNodeShadowRoot(node) ? node.host : node.parentNode } /** * Return the parent element, crossing shadow DOM boundaries. * If the element is a direct child of a shadow root, returns the shadow host. */ export function getParentElement(element: Element): Element | null { if (element.parentElement) { return element.parentElement } const parentNode = element.parentNode if (parentNode && isNodeShadowRoot(parentNode)) { return parentNode.host } return null }