此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.greasyfork.cloud/scripts/454354/1125168/yssWaitForNode.js
您需要先安装一款用户样式管理器扩展(如 Stylus)后才能安装此样式。
您需要先安装一款用户样式管理器扩展(如 Stylus)后才能安装此样式。
您需要先安装一款用户样式管理器扩展(如 Stylus)后才能安装此样式。
您需要先安装一款用户样式管理器扩展后才能安装此样式。
您需要先安装一款用户样式管理器扩展后才能安装此样式。
您需要先安装一款用户样式管理器扩展后才能安装此样式。
(我已经安装了用户样式管理器,让我安装!)
// ==UserScript==
// @name yssWaitForNode
// @namespace https://ysslang.com/
// @version 1.2.2
// @description Be smart!
// @author ysslang
// @match *://*/*
// @supportURL https://greasyfork.org/scripts/454354
// @run-at document-start
// @grant none
// ==/UserScript==
/* Done
- 修改属性默认值写法, 支持配置false内容;
- 修改parentEl默认值, 由body改为document, 从而支持初始化页面前无body时执行;
*/
class WaitForNode {
#currentURL;
#observerList = [];
#waitList = [];
constructor() { this.#currentURL = window.location.href; }
#checkIfUrlMatch(matcher) {
var result = false;
if (matcher === undefined || matcher === null) result = false;
if (typeof matcher === "string") result = new RegExp(matcher.trim()).test(this.#currentURL);
if (matcher instanceof RegExp) result = matcher.test(this.#currentURL);
return result;
}
#determineParentElement(arg) {
var result = document;
if (typeof arg === "string" && document.querySelector(arg)) result = document.querySelector(arg);
if (arg instanceof Element) result = arg;
return result;
}
#mergeOptions(options) {
options = options || {};
var result = {
immediate: [options.immediate, options.imdt, true].find((e) => typeof(e) !== 'undefined'),
recursive: [options.recursive, options.rcs, true].find((e) => typeof(e) !== 'undefined'),
once: [options.once, false].find((e) => typeof(e) !== 'undefined'),
subtree: [options.subtree, options.sbt, true].find((e) => typeof(e) !== 'undefined'),
childList: [options.childList, options.cld, true].find((e) => typeof(e) !== 'undefined'),
parentEl: this.#determineParentElement(options.parent),
};
return result;
}
#extractMatchedElements(mutations, selector, recursive) {
const matchedElements = [];
for (const { addedNodes } of mutations) {
for (const node of addedNodes) {
if (!node.tagName) continue;
else if (node.matches(selector)) matchedElements.push(node);
else if (recursive && node.firstElementChild) matchedElements.push(...node.querySelectorAll(selector));
}
}
return matchedElements;
}
#on(selector, callback, options) {
if (options.immediate) {
[...options.parentEl.querySelectorAll(selector)].forEach(callback);
}
const observer = new MutationObserver((mutations) => {
const elements = this.#extractMatchedElements(mutations, selector, options.recursive);
elements.forEach(callback);
if (elements && options.once) this.disconnect();
});
observer.observe(options.parentEl, { subtree: options.subtree, childList: options.childList, });
this.#observerList.push(observer);
window.x=observer;
return observer;
}
#injectStyle(styleString) {
const style = document.createElement('style');
style.textContent = styleString;
return document.head.append(style);
}
add(name, url, selector, callback, options) {
if(url === '') url = ['.*'];
const urls = Array.isArray(url) ? url : [url];
if (!urls.some(this.#checkIfUrlMatch, this)) return;
const opts = this.#mergeOptions(options);
const observer = this.#on(selector, callback, opts);
this.#waitList.push({'name': name, 'url': url, 'selector': selector, 'callback': callback, 'options': opts, 'observer': observer});
}
addCss(name, url, cssString) {
if(url === '') url = ['.*'];
const urls = Array.isArray(url) ? url : [url];
if (!urls.some(this.#checkIfUrlMatch, this)) return;
this.#injectStyle(cssString);
}
stopAll() {
while (this.#observerList.length) this.#observerList.pop().disconnect();
}
}
const WFN = new WaitForNode();
WFN.add('CSDN', '', '#passportbox', (el) => {
console.info(123);
//el.querySelector('#passportbox > span[style]').click()
}, {immediate: false});
console.log(WFN);