此腳本不應該直接安裝,它是一個供其他腳本使用的函式庫。欲使用本函式庫,請在腳本 metadata 寫上: // @require https://update.cn-greasyfork.org/scripts/448197/1072378/ElementGetter.js
你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式
你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式
你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式
你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式
你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式
你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式
(我已經安裝了使用者樣式管理器,讓我安裝!)
// ==UserScript==
// @name ElementGetter
// @author cxxjackie
// @version 1.0.0
// ==/UserScript==
class ElementGetter {
#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);
}
}
#getOne(selector, parent, timeout) {
return new Promise(resolve => {
const result = parent.querySelector(selector);
if (result) return resolve(result);
let timer;
const filter = node => {
const isMatches = this.#matchesSelector.call(node, selector);
const result = isMatches ? node : node.querySelector(selector);
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);
}
});
}
#getAll(selectorList, parent, timeout) {
const promiseList = [];
for (const selector of selectorList) {
promiseList.push(this.#getOne(selector, parent, timeout));
}
return Promise.all(promiseList);
}
constructor() {
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, parent = this.#window.document, timeout = 0) {
if (typeof selector === 'string' || selector instanceof String) {
return this.#getOne(selector, parent, timeout);
} else if (selector instanceof Array) {
return this.#getAll(selector, parent, timeout);
} else {
return Promise.resolve(null);
}
}
each(selector, parent, callback) {
let removed = false;
const handle = {
remove: () => {
removed = true;
}
};
setTimeout(() => {
const elms = parent.querySelectorAll(selector);
for (const elm of elms) {
callback(elm, false);
if (removed) return;
}
const removeFunc = this.#addListener(parent, node => {
if (node instanceof Element) {
let elms = node.querySelectorAll(selector);
if (this.#matchesSelector.call(node, selector)) {
elms = [node, ...elms];
}
for (const elm of elms) {
callback(elm, true);
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;
}
}