您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Useful library for dealing with the DOM.
当前为
此脚本不应直接安装,它是供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.gf.qytechs.cn/scripts/405802/819174/Monkey%20DOM.js
// ==UserScript== // @name Monkey DOM // @namespace https://rafaelgssa.gitlab.io/monkey-scripts // @version 1.1.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 MonkeyUtils */ /** * @typedef {(element?: Element) => void} ElementCallback * @typedef {(node: Node) => void} NodeCallback * @typedef {InsertPosition | 'atouter' | 'atinner'} ExtendedInsertPosition * @typedef {[ElementTag, ElementAttributes | null, ElementChildren | null]} InsertNodeStructure * @typedef {keyof HTMLElementTagNameMap} ElementTag * @typedef {Partial<HTMLElementTagNameMap[ElementTag] & { ref: NodeCallback }>} ElementAttributes * @typedef {[ElementTag, ElementAttributes | null, [ElementTag, ElementAttributes | null, [ElementTag, ElementAttributes | null, [ElementTag, ElementAttributes | null, [ElementTag, ElementAttributes | null, [ElementTag, ElementAttributes | null, [ElementTag, ElementAttributes | null, [ElementTag, ElementAttributes | null, [ElementTag, ElementAttributes | null, [ElementTag, ElementAttributes | null, null][] | string | null][] | string | null][] | string | null][] | string | null][] | string | null][] | string | null][] | string | null][] | string | null][] | string | null][] | string} ElementChildren Because JSDoc doesn't support circular references, we limit the type check to the first 10 levels. */ // eslint-disable-next-line const MonkeyDom = (() => { 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 nodes in reference to an element based on array structures. * @param {Element} reference The element to use as reference. * @param {ExtendedInsertPosition} position Where to insert the nodes. * @param {InsertNodeStructure[]} structures The structures to use. * @returns {Node[] | null} The inserted nodes 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. * let pElelement; * let elements = DOM.insertNode(document.body, 'beforeend', [ * ['div', { className: 'example', onclick: () => {} }, [ * ['p', { ref: (node) => pElement = node }, 'Example'] * ]], * ['span', null, 'Example'] * ]); * * @example * // Using array destructuring. * // 'divElement' will contain the DIV element and 'spanElement' will contain the SPAN element. * let pElelement; * let [divElement, spanElement] = DOM.insertNode(document.body, 'beforeend', [ * ['div', { className: 'example', onclick: () => {} }, [ * ['p', { ref: (node) => pElement = node }, 'Example'] * ]], * ['span', null, 'Example'] * ]); */ const insertNodes = (reference, position, structures) => { const fragment = _buildFragment(structures); if (!fragment) { return null; } const nodes = 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 null; } return nodes; }; /** * Builds a fragment from array structures. * @param {InsertNodeStructure[]} structures * @returns {DocumentFragment | null} The built fragment, if successful. */ const _buildFragment = (structures) => { if (!Array.isArray(structures)) { return null; } const filteredStructures = structures.filter(MonkeyUtils.isSet); if (!Array.isArray(filteredStructures[0])) { return null; } const fragment = document.createDocumentFragment(); for (const structure of filteredStructures) { const node = _buildNode(structure); if (node) { fragment.appendChild(node); } } return fragment; }; /** * Builds a node from an array structure. * @param {InsertNodeStructure} structure * @returns {Node | undefined} The built node, if successful. */ const _buildNode = ([tag, attributes, children]) => { const node = document.createElement(tag); if (attributes) { _setNodeAttributes(node, attributes); } if (children) { _appendNodeChildren(node, children); } return node; }; /** * Sets attributes for a node. * @param {Node} node * @param {ElementAttributes} attributes */ const _setNodeAttributes = (node, attributes) => { const filteredAttributes = Object.entries(attributes).filter(([, value]) => MonkeyUtils.isSet(value) ); for (const [key, value] of filteredAttributes) { if (key === 'ref' && typeof value === 'function') { value(node); } else if (key.startsWith('on') && typeof value === 'function') { const eventType = key.slice(2); node.addEventListener(eventType, value); } else if (typeof value === 'object') { _setNodeProperties(node, key, value); } else { // @ts-ignore node[key] = value; } } }; /** * Sets properties for the attribute of a node. * @param {Node} node * @param {string} attribute * @param {Object} properties */ const _setNodeProperties = (node, attribute, properties) => { const filteredProperties = Object.entries(properties).filter(([, value]) => MonkeyUtils.isSet(value) ); for (const [key, value] of filteredProperties) { // @ts-ignore node[attribute][key] = value; } }; /** * Appends children to a node. * @param {Node} node * @param {ElementChildren} children */ const _appendNodeChildren = (node, children) => { if (Array.isArray(children)) { const fragment = _buildFragment(children); if (fragment) { node.appendChild(fragment); } } else if (typeof children === 'string') { const textNode = document.createTextNode(children); node.appendChild(textNode); } }; /** * Observes a node for mutations. * @param {Node} node The node to observe. * @param {NodeCallback} callback The callback to call with each mutation. * @returns {MutationObserver} The observer. */ const observeNode = (node, callback) => { const observer = new MutationObserver((mutations) => _processNodeMutations(mutations, callback) ); observer.observe(node, { attributes: true, childList: true, subtree: true, }); return observer; }; /** * @param {MutationRecord[]} mutations * @param {NodeCallback} callback */ const _processNodeMutations = (mutations, callback) => { for (const mutation of mutations) { 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, insertNodes, observeNode, parse, }; })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址