60 lines
1.4 KiB
TypeScript
60 lines
1.4 KiB
TypeScript
/**
|
|
* DOM 工具 - 等待元素、Shadow DOM 穿透
|
|
* 从 docs/extension/plan.md §6.5 移植
|
|
*/
|
|
|
|
/**
|
|
* 等待任一选择器出现(MutationObserver + 超时)
|
|
*/
|
|
export function waitForAny(
|
|
selectors: string[],
|
|
timeoutMs = 10_000
|
|
): Promise<Element | null> {
|
|
const hit = () => selectors.map(s => document.querySelector(s)).find(Boolean) ?? null;
|
|
|
|
const found = hit();
|
|
if (found) return Promise.resolve(found);
|
|
|
|
return new Promise((resolve) => {
|
|
const timer = setTimeout(() => {
|
|
observer.disconnect();
|
|
resolve(null); // 超时返回 null
|
|
}, timeoutMs);
|
|
|
|
const observer = new MutationObserver(() => {
|
|
const el = hit();
|
|
if (el) {
|
|
clearTimeout(timer);
|
|
observer.disconnect();
|
|
resolve(el);
|
|
}
|
|
});
|
|
|
|
observer.observe(document.documentElement, { childList: true, subtree: true });
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 穿透 Shadow DOM 查询元素
|
|
* 1688 部分组件用了 Web Components
|
|
*/
|
|
export function queryAllDeep(selectors: string[]): Element[] {
|
|
const out: Element[] = [];
|
|
for (const sel of selectors) {
|
|
let nodes: NodeListOf<Element>;
|
|
try {
|
|
nodes = document.querySelectorAll(sel);
|
|
} catch {
|
|
continue; // 选择器写错不能拖垮整个扫描
|
|
}
|
|
nodes.forEach(el => {
|
|
if (el.shadowRoot) {
|
|
out.push(...Array.from(el.shadowRoot.querySelectorAll('img, video')));
|
|
} else {
|
|
out.push(el);
|
|
}
|
|
});
|
|
}
|
|
return out;
|
|
}
|