本帖最后由 笑尘天雨 于 2022-9-21 00:17 编辑 
本帖最后由 笑尘天雨 于 2022-9-21 00:16 编辑 
看了大佬的代码,深有所感,于是有了以下代码。
- 之所以不用jQuery,是因为jQuery可以直接传入dom节点将它转成jQuery dom对象。
 
- 之所以只用document.querySelectorAll, 是因为如果有多个相同的id时,document.querySelectorAll可以都获取到,而jQuery不能。
 
- 对了,应该加上一个功能,让它在document.readyState为complete时再运行,避免run-at在document-start时导致找不到document.body的问题,不过问题不大,我要睡觉了
 
- 最后,欢迎大家来挑刺
 
// ==UserScript==
// @name              async querySelector
// @namespace         heiheihei
// @version           1.0.0
// @author            lll
// @description       测试
// @run-at            document-end
// @match             https://www.bilibili.com/video/*
// @noframes
// ==/UserScript==
async function querySelector(selectorString, targetNode = document.body, timerout = 10 * 1000) {
    if ( typeof selectorString !== "string" || targetNode.toString() !== "[object HTMLBodyElement]") {
        return Promise.reject("请输入正确的字符串或正确的dom对象");
    }
    // 观察器的配置(需要观察什么变动)
    const config = { /** attributes: true, */ childList: true, subtree: true };
    /** 在dom上添加自定义字段 */
    // 初始化字段 __use_observer__ 记录当前selectorString是否已经被监听
    if (!targetNode.__use_observer__) {
        targetNode.__use_observer__ = {};
    }
    // 初始化字段 __observer_list__ 记录当前targetNode监听时所需要获取到的dom节点
    if (!targetNode.__observer_list__) {
        targetNode.__observer_list__ = [];
    }
    /** __is_observer__ 为true时,表示已经在监听targetNode了 */
    if (targetNode.__is_observer__ === true) {
        if (targetNode.__use_observer__?.[selectorString]) {
            return targetNode.__use_observer__[selectorString];
        } else {
            targetNode.__observer_list__.push(selectorString);
            return (targetNode.__use_observer__[selectorString] = Promise);
        }
    } else {
        targetNode.__is_observer__ = true;
    }
    targetNode.__observer_list__.push(selectorString);
    // callback 防抖,并作为超时基准值
    targetNode.__observer_timer__ = Date.now() + 15; // 15ms
    let timer;
    // 创建一个观察器实例并传入回调函数
    return (targetNode.__use_observer__[selectorString] = new Promise((resolve, reject) => {
            const observer = new MutationObserver(function fn(mutationsList) {
                if (Date.now() > targetNode.__observer_timer__) {
                    if (Date.now() > timerout + targetNode.__observer_timer__) {
                        // 超时,所有promise都reject且清除数据并关闭监听
                        targetNode.__observer_list__.forEach((str) => {
                            const rejectMsg = new Error("获取超时");
                            if (str === selectorString) {
                                return reject(rejectMsg);;
                            }else{
                                targetNode.__use_observer__[str].reject(rejectMsg);
                            }
                        });
                        clearObserve();
                    }
                    // 过滤数据并查询列表内的每一项
                    targetNode.__observer_list__ = targetNode.__observer_list__.filter((str) => {
                            const target = document.querySelectorAll(str);
                            if (target && target.length) {
                                if (str === selectorString) {
                                    resolve(target);
                                } else {
                                    targetNode.__use_observer__[str].resolve( target );
                                }
                                delete targetNode.__use_observer__[str];
                                return false;
                            } else {
                                return true;
                            }
                        });
                    if (!targetNode.__observer_list__.length) {
                        // 全部完成时,清除数据并关闭监听
                        clearObserve();
                    }
                } else {
                    clearTimeout(timer);
                    timer = setTimeout(() => fn(mutationsList), 15);
                }
            });
            // 以上述配置开始观察目标节点
            observer.observe(targetNode, config);
            function clearObserve() {
                delete targetNode.__use_observer__;
                delete targetNode.__observer_list__;
                delete targetNode.__is_observer__;
                delete targetNode.__observer_timer__;
                observer.disconnect();
            }
        }
    ));
}
// 无错误处理
// const [target] = await querySelector('[class="bui-danmaku-switch-input"');
// 对错误进行处理
// const [target] = await querySelector('[class="bui-danmaku-switch-input"').catch( err => []);
// 对错误进行处理并设置默认值
const [target = document.body] = await querySelector('[class="bui-danmaku-switch-input"').catch((err) => [console.log(err)]);
console.log("弹幕", target);