您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Useful library for dealing with the DOM.
当前为
此脚本不应直接安装,它是供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.gf.qytechs.cn/scripts/405802/820354/DOM.js
// ==UserScript== // @name DOM // @namespace https://rafaelgssa.gitlab.io/monkey-scripts // @version 4.0.1 // @author rafaelgssa // @description Useful library for dealing with the DOM. // @match *://*/* // @require https://gf.qytechs.cn/scripts/405813-monkey-utils/code/Monkey%20Utils.js // ==/UserScript== /* global Utils */ /** * @typedef {(element?: Element) => void} ElementCallback * * @typedef {InsertPosition | 'atouter' | 'atinner'} ExtendedInsertPosition * * @typedef {ElementArrayConstructor<ElementArrayBase, 8>} ElementArray Any higher than 8 is too deep and does not work. * * **The definition for ElementArrayConstructor is in DOM.d.ts, as it is too complex for JSDoc:** * declare type ElementArrayConstructor<T extends [any, any] | string, N extends number> = T extends [ * infer A, * infer B * ] * ? { * done: [A, B, string | null]; * recurse: [ * A, * B, * ElementArrayConstructor<ElementArrayBase, ElementArrayDepth[N]>[] | string | null * ]; * }[N extends 0 ? 'done' : 'recurse'] * : T extends string * ? T * : never; * * @typedef {[never, 0, 1, 2, 3, 4, 5, 6, 7]} ElementArrayDepth * * @typedef {{ [K in ElementTag]: [K, ElementAttributes<K> | null] }[ElementTag] | string} ElementArrayBase * * @typedef {keyof HTMLElementTagNameMap} ElementTag * * @typedef {ElementArray[] | string} ElementArrayChildren * * @typedef {Object} MutationTypes * @property {boolean} [attributes] * @property {boolean} [childList] * @property {boolean} [subtree] * * @typedef {(node: Node) => void} NodeCallback */ /** * @template {ElementTag} T * @typedef {{ [K in keyof ExtendedElement<T>]?: Partial<ExtendedElement<T>[K]> | null }} ElementAttributes */ /** * @template {ElementTag} T * @typedef {HTMLElementTagNameMap[T] & { ref: NodeCallback }} ExtendedElement */ // eslint-disable-next-line const DOM = (() => { const _parser = new DOMParser(); /** * Waits for an element. * @param {string} selectors The selectors to query for the element. * @param {number} timeout How long to wait for the element in seconds. Defaults to 60 (1 minute). * @returns {Promise<Element | undefined>} The element, if found. */ const dynamicQuerySelector = (selectors, timeout = 60) => { return new Promise((resolve) => _checkElementExists(selectors, resolve, timeout)); }; /** * @param {string} selectors * @param {ElementCallback} callback * @param {number} timeout */ const _checkElementExists = (selectors, callback, timeout = 60) => { const element = document.querySelector(selectors); if (element) { callback(element); } else if (timeout > 0) { window.setTimeout(_checkElementExists, 1000, selectors, callback, timeout - 1); } else { callback(); } }; /** * Inserts elements in reference to another element based on element arrays. * @param {Element} reference The element to use as reference. * @param {ExtendedInsertPosition} position Where to insert the elements. * @param {ElementArray[]} arrays The arrays to use. * @returns {HTMLElement[] | []} The inserted elements from the root level, if successful. * * @example * // 'pElement' will contain the P element. * // 'elements' will be an array containing the DIV and the SPAN elements, in this order, if successful. * let pElement; * const elements = DOM.insertElement(document.body, 'beforeend', [ * ['div', { className: 'hello', onclick: () => {} }, [ * 'Hello, ', // This is added as a text node. * ['p', { ref: (ref) => pElement = ref }, 'John'], * '!' // This is added as a text node. * ]], * ['span', null, 'How are you?'] * ]); * * @example * // Using array destructuring. * // 'divElement' will contain the DIV element and 'spanElement' will contain the SPAN element, if successful. * let pElement; * const [divElement, spanElement] = DOM.insertElement(document.body, 'beforeend', [ * ['div', { className: 'hello', onclick: () => {} }, [ * 'Hello, ', // This is added as a text node. * ['p', { ref: (ref) => pElement = ref }, 'John'], * '!' // This is added as a text node. * ]], * ['span', null, 'How are you?'] * ]); */ const insertElements = (reference, position, arrays) => { const fragment = _buildFragment(arrays); if (!fragment) { return []; } const elements = /** @type {HTMLElement[]} */ (Array.from(fragment.children)); const referenceParent = reference.parentElement; switch (position) { case 'beforebegin': if (referenceParent) { referenceParent.insertBefore(fragment, reference); } break; case 'afterbegin': reference.insertBefore(fragment, reference.firstElementChild); break; case 'beforeend': reference.appendChild(fragment); break; case 'afterend': if (referenceParent) { referenceParent.insertBefore(fragment, reference.nextElementSibling); } break; case 'atouter': if (referenceParent) { referenceParent.insertBefore(fragment, reference.nextElementSibling); reference.remove(); } break; case 'atinner': reference.innerHTML = ''; reference.appendChild(fragment); break; // no default } if (fragment.children.length > 0) { return []; } return elements; }; /** * Builds a fragment from element arrays. * @param {ElementArray[]} arrays * @returns {DocumentFragment | null} The built fragment, if successful. */ const _buildFragment = (arrays) => { if (!Array.isArray(arrays)) { return null; } // @ts-ignore const filteredArrays = arrays.filter(Utils.isSet); const fragment = document.createDocumentFragment(); for (const array of filteredArrays) { const element = _buildElement(array); fragment.appendChild(element); } return fragment; }; /** * Builds an element from an element array. * @param {ElementArray} array * @returns {HTMLElement} The built element. */ const _buildElement = (array) => { if (typeof array === 'string') { const textNode = document.createTextNode(array); return /** @type {HTMLElement} */ (/** @type {unknown} */ (textNode)); } const [tag, attributes, children] = array; const element = document.createElement(tag); if (attributes) { _setElementAttributes(element, attributes); } if (children) { _appendElementChildren(element, children); } return element; }; /** * Sets attributes for an element. * @template {ElementTag} T * @param {HTMLElement} element * @param {ElementAttributes<T>} attributes */ const _setElementAttributes = (element, attributes) => { const filteredAttributes = Object.entries(attributes).filter(([, value]) => Utils.isSet(value)); for (const [key, value] of filteredAttributes) { if (key === 'ref' && typeof value === 'function') { value(element); } else if (key.startsWith('on') && typeof value === 'function') { const eventType = key.slice(2); element.addEventListener(eventType, value); } else if (typeof value === 'object') { _setElementProperties(element, key, value); } else { // @ts-ignore element[key] = value; } } }; /** * Sets properties for the attribute of an element. * @param {HTMLElement} element * @param {string} attribute * @param {Object} properties */ const _setElementProperties = (element, attribute, properties) => { const filteredProperties = Object.entries(properties).filter(([, value]) => Utils.isSet(value)); for (const [key, value] of filteredProperties) { // @ts-ignore element[attribute][key] = value; } }; /** * Appends children to an element from an element array. * @param {HTMLElement} element * @param {ElementArrayChildren} children */ const _appendElementChildren = (element, children) => { if (Array.isArray(children)) { const fragment = _buildFragment(children); if (fragment) { element.appendChild(fragment); } } else if (typeof children === 'string') { const textNode = document.createTextNode(children); element.appendChild(textNode); } }; /** * Observes a node for mutations. * @param {Node} node The node to observe. * @param {MutationTypes | null} types The types of mutations to observe. Defaults to child list of the node and all its descendants. * @param {NodeCallback} callback The callback to call with each updated / added node. * @returns {MutationObserver} The observer. */ const observeNode = (node, types, callback) => { const observer = new MutationObserver((mutations) => _processNodeMutations(mutations, callback) ); observer.observe( node, types || { childList: true, subtree: true, } ); return observer; }; /** * @param {MutationRecord[]} mutations * @param {NodeCallback} callback */ const _processNodeMutations = (mutations, callback) => { for (const mutation of mutations) { if (mutation.type === 'attributes') { callback(mutation.target); } else { mutation.addedNodes.forEach(callback); } } }; /** * Parses an HTML string into a DOM. * @param {string} html The HTML string to parse. * @returns {Document} The parsed DOM. */ const parse = (html) => { return _parser.parseFromString(html, 'text/html'); }; return { dynamicQuerySelector, insertElements, observeNode, parse, }; })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址