此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.greasyfork.cloud/scripts/448895/1077494/ElementGetter%E5%BA%93.js
您需要先安装一款用户样式管理器扩展(如 Stylus)后才能安装此样式。
您需要先安装一款用户样式管理器扩展(如 Stylus)后才能安装此样式。
您需要先安装一款用户样式管理器扩展(如 Stylus)后才能安装此样式。
您需要先安装一款用户样式管理器扩展后才能安装此样式。
您需要先安装一款用户样式管理器扩展后才能安装此样式。
您需要先安装一款用户样式管理器扩展后才能安装此样式。
(我已经安装了用户样式管理器,让我安装!)
// ==UserScript==
// @name ElementGetter库
// @author cxxjackie
// @version 1.1.1
// @namespace http://tampermonkey.net/
// @description 库
// @icon
// @grant none
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// Your code here...
})();
class ElementGetter {
#jQuery;
#window;
#matchesSelector;
#mutationObserver;
#addListener;
#listeners;
#addObserver(target, callback) {
const observer = new this.#mutationObserver(mutations => {
for (const mutation of mutations) {
for (const addedNode of mutation.addedNodes) {
if (observer.canceled) return;
callback(addedNode);
}
}
});
observer.canceled = false;
observer.observe(target, { childList: true, subtree: true });
return function() {
observer.canceled = true;
observer.disconnect();
};
}
#addEvent(target, callback) {
const listener = e => callback(e.target);
target.addEventListener('DOMNodeInserted', listener);
return function() {
target.removeEventListener('DOMNodeInserted', listener);
};
}
#addFilter(target, filter) {
if (this.#listeners.has(target)) {
const listener = this.#listeners.get(target);
listener.filters.push(filter);
} else {
const removeFunc = this.#addListener(target, node => {
if (node instanceof Element) {
listener.filters.forEach(f => f(node));
}
});
const listener = {
filters: [filter],
remove: removeFunc
};
this.#listeners.set(target, listener);
}
}
#removeFilter(target, filter) {
if (!this.#listeners.has(target)) return;
const listener = this.#listeners.get(target);
const index = listener.filters.indexOf(filter);
listener.filters.splice(index, 1);
if (listener.filters.length === 0) {
listener.remove();
this.#listeners.delete(target);
}
}
#query(selector, parent, includeParent) {
const $ = this.#jQuery;
if ($) {
let jNodes = $(parent.querySelectorAll('*'));
if (includeParent) jNodes = jNodes.add(parent);
jNodes = jNodes.filter(selector);
return jNodes.length > 0 ? $(jNodes.get(0)) : null;
} else {
if (includeParent && this.#matchesSelector.call(parent, selector)) {
return parent;
}
return parent.querySelector(selector);
}
}
#queryAll(selector, parent, includeParent) {
const $ = this.#jQuery;
let result = [];
if ($) {
let jNodes = $(parent.querySelectorAll('*'));
if (includeParent) jNodes = jNodes.add(parent);
jNodes = jNodes.filter(selector);
jNodes.each((i, elm) => result.push($(elm)));
} else {
if (includeParent && this.#matchesSelector.call(parent, selector)) {
result.push(parent);
}
result.push(...parent.querySelectorAll(selector));
}
return result;
}
#getOne(selector, parent, timeout) {
return new Promise(resolve => {
const result = this.#query(selector, parent, false);
if (result) return resolve(result);
let timer;
const filter = node => {
const result = this.#query(selector, node, true);
if (result) {
this.#removeFilter(parent, filter);
timer && clearTimeout(timer);
resolve(result);
}
};
this.#addFilter(parent, filter);
if (timeout > 0) {
timer = setTimeout(() => {
this.#removeFilter(parent, filter);
resolve(null);
}, timeout);
}
});
}
#getList(selectorList, parent, timeout) {
const promiseList = [];
for (const selector of selectorList) {
promiseList.push(this.#getOne(selector, parent, timeout));
}
return Promise.all(promiseList);
}
constructor(jQuery) {
this.#jQuery = jQuery && jQuery.fn && jQuery.fn.jquery ? jQuery : null;
this.#window = window.unsafeWindow || document.defaultView || window;
const elmProto = this.#window.Element.prototype;
this.#matchesSelector = elmProto.matches
|| elmProto.matchesSelector
|| elmProto.webkitMatchesSelector
|| elmProto.msMatchesSelector
|| elmProto.mozMatchesSelector;
this.#mutationObserver = this.#window.MutationObserver
|| this.#window.WebkitMutationObserver
|| this.#window.MozMutationObserver;
this.#addListener = this.#mutationObserver ? this.#addObserver : this.#addEvent;
this.#listeners = new WeakMap();
}
get(selector, ...args) {
const parent = typeof args[0] !== 'number' && args.shift() || this.#window.document;
const timeout = args[0] || 0;
if (typeof selector === 'string' || selector instanceof String) {
return this.#getOne(selector, parent, timeout);
} else if (selector instanceof Array) {
return this.#getList(selector, parent, timeout);
} else {
return Promise.resolve(null);
}
}
each(selector, ...args) {
const parent = typeof args[0] !== 'function' && args.shift() || this.#window.document;
const callback = args[0];
let removed = false;
const handle = {
remove: () => {
removed = true;
}
};
setTimeout(() => {
const elms = this.#queryAll(selector, parent, false);
for (const elm of elms) {
if (callback(elm, false) === false || removed) return;
}
const refs = new WeakSet();
const removeFunc = this.#addListener(parent, node => {
if (node instanceof Element) {
const elms = this.#queryAll(selector, node, true);
for (const elm of elms) {
const _node = this.#jQuery ? elm.get(0) : elm;
if (!refs.has(_node)) {
refs.add(_node);
if (callback(elm, false) === false) handle.remove();
if (removed) return;
}
}
}
});
handle.remove = () => {
removed = true;
removeFunc();
};
}, 0);
return handle;
}
remove(handle) {
handle && handle.remove();
}
create(domString) {
const template = this.#window.document.createElement('template');
template.innerHTML = domString;
const node = template.content.firstElementChild || template.content.firstChild;
node.remove();
return node;
}
}