40252492 发表于 2022-9-4 17:34:18

大佬大佬,诶嘿嘿~我也因为要把脚本传到greasyfork上,所以把您的库的1.2.0传到上面了
没加license MIT
https://greasyfork.org/zh-CN/scripts/450726-elementgetter1-2-0

↓这是我写的,多谢大佬这一个月来的帮助!要不您看看哪里有可以优化的地方?Q3Q
https://greasyfork.org/zh-CN/scripts/450728-dulst-com%E7%9A%84cardfighit-vanguard-overdulst%E6%96%87%E6%9C%AC%E6%9B%BF%E6%8D%A2%E6%B1%89%E5%8C%96

cxxjackie 发表于 2022-9-4 20:29:09

40252492 发表于 2022-9-4 17:34
大佬大佬,诶嘿嘿~我也因为要把脚本传到greasyfork上,所以把您的库的1.2.0传到上面了
没加license MIT
htt ...

应该没什么问题吧,主要我不玩这游戏,还得看你的用户有什么反馈。

王一之 发表于 2022-9-13 01:49:55

帮大佬迁移到油猴脚本的主题了

https://scriptcat.org/lib/513/latest/ElementGetter.js 的话是最新的版本

笑尘天雨 发表于 2022-9-21 00:10:15

本帖最后由 笑尘天雨 于 2022-9-21 00:17 编辑

> 本帖最后由 笑尘天雨 于 2022-9-21 00:16 编辑

看了大佬的代码,深有所感,于是有了以下代码。
1. 之所以不用jQuery,是因为jQuery可以直接传入dom节点将它转成jQuery dom对象。
2. 之所以只用document.querySelectorAll, 是因为如果有多个相同的id时,document.querySelectorAll可以都获取到,而jQuery不能。
3. 对了,应该加上一个功能,让它在document.readyState为complete时再运行,避免run-at在document-start时导致找不到document.body的问题,不过问题不大,我要睡觉了
4. 最后,欢迎大家来挑刺


```
// ==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() !== "") {
      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__?.) {
            return targetNode.__use_observer__;
      } else {
            targetNode.__observer_list__.push(selectorString);
            return (targetNode.__use_observer__ = 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__ = 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__.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__.resolve( target );
                              }
                              delete targetNode.__use_observer__;
                              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 = await querySelector('[class="bui-danmaku-switch-input"');
// 对错误进行处理
// const = await querySelector(');
// 对错误进行处理并设置默认值
const = await querySelector(');

console.log("弹幕", target);
```

cxxjackie 发表于 2022-9-21 21:21:30

笑尘天雨 发表于 2022-9-21 00:10
> 本帖最后由 笑尘天雨 于 2022-9-21 00:16 编辑

看了大佬的代码,深有所感,于是有了以下代码。


支持jquery主要是为了jquery选择器,有些人可能更习惯用这个。
我不太喜欢往元素上绑属性的做法,如果有多个脚本同时用了这个库,容易引发冲突,用一个WeakMap来做映射可能更好一点。
监听属性变化的理由是:假设某元素刚创建时有一个loading的类名,加载完成后去除,这种情况如果选择器写':not(.loading)',结果是获取不到的,因为不算节点插入,元素还是那个元素,只是属性变了而已。还有很多类似的情况,比如写属性选择器,元素的属性不一定第一时间符合,之后属性变化了也应当触发一次检查。
另外async/await的错误处理是个经典问题了,稍有不慎就会写的很难看,我倾向于能用resolve(null)解决的问题,就尽量不reject,当然这只是个人风格。

笑尘天雨 发表于 2022-9-21 22:26:50

cxxjackie 发表于 2022-9-21 21:21
支持jquery主要是为了jquery选择器,有些人可能更习惯用这个。
我不太喜欢往元素上绑属性的做法,如果有 ...

1. jquery选择器的问题,querySelector能用的jQuery也能用,而且原生支持比jquery选择器更合适,当然了,这属于个人倾向问题,问题不大
2. 元素上绑定属性, 我是在这个元素上绑定了一个所需要获取的字段列表,所以真的有不同脚本引用相同的库,这个库上可以共享这个列表一起使用,当然了,想法很不错,至于有没有起效,我还没测试,有空来可以测下看看。
3. 监听属性变化,我是用了setTimeout延迟执行而且直接使用的原生方法获取没有使用mutations列表,所以不存在你这个问题,当然了坏处就是会有延迟不能第一时间做处理,而且后台执行的时候setTimeout可能还会出问题导致它更慢,不过考虑到大部分场景都是在前台打开页面的,而且设置的15ms延迟,对我个人而言,我还是能接受的
4. async/await的错误处理,这个说的确实对,所以我将它改成和原生的保持一致,获取不到就返回一个空数组,获取到了转成数组之后再返回

cxxjackie 发表于 2022-9-21 23:24:44

笑尘天雨 发表于 2022-9-21 22:26
1. jquery选择器的问题,querySelector能用的jQuery也能用,而且原生支持比jquery选择器更合适,当然 ...

1. jquery有一部分选择器是原生不支持的,可以参考这里:
https://www.w3school.com.cn/jquery/jquery_ref_selectors.asp
我这个库的意思是,传入jquery引用就用jquery选择器,不传就用原生的。主要还是之前那个waitForKeyElement的库必须用jquery,这里提供一个改动较小的替换方案。
2. 我没细究代码,你这个不同线程里resolve不会有问题吗?
3. 这个不是延迟的问题,而是不监听属性变化,MutationObserver就不会触发,除非之后“恰好”又发生了新的节点插入,使得你有机会进行检查,否则回调函数可能永远都无法触发。

笑尘天雨 发表于 2022-9-22 23:53:42

cxxjackie 发表于 2022-9-21 23:24
1. jquery有一部分选择器是原生不支持的,可以参考这里:
https://www.w3school.com.cn/jquery/jquery_re ...

1. jQuery属于个人倾向问题,这个倒是没有关系,毕竟使用方式完全不一样,也就没必要考虑兼容以前的一些事。
2. 这个是最关键的,能不能成全看它了,但是真没时间去试,现在才下班到家。
3.说实话,我对这几个参数是不了解的,只是感觉它像是监听目标元素的attr才干掉它的。因为考虑到既然只是监听目标元素的attr,而我们正常用的时候肯定是获取目标内部元素的,所以你说的这种情况本身就不存在。当然也有可能是我感觉出问题了 这也很正常。如果attr是监听目标元素和目标内部所有元素的attr,那么就确实有必要了

cxxjackie 发表于 2022-9-23 21:10:51

笑尘天雨 发表于 2022-9-22 23:53
1. jQuery属于个人倾向问题,这个倒是没有关系,毕竟使用方式完全不一样,也就没必要考虑兼容以前的一些 ...

attributes 是否观察属性变化
childList 是否观察节点变动
subtree 是否应用到所有后代节点
attributes和subtree组合就可以观察到后代的属性变化。

笑尘天雨 发表于 2022-9-23 23:41:45

cxxjackie 发表于 2022-9-23 21:10
attributes 是否观察属性变化
childList 是否观察节点变动
subtree 是否应用到所有后代节点


原来如此,感谢解答。
那你写的监听document.body的话,岂不是会很耗性能?
页: 1 2 3 4 [5] 6 7 8 9 10 11 12 13 14
查看完整版本: 异步获取元素的脚本库 ElementGetter