ElementGetter_gf

这是一个异步获取元素的脚本库。不少人写脚本都碰到过元素延迟加载的问题,使用定时器获取不仅实时性不足,还有性能问题,DOMNodeInserted的性能也不好,一般都推荐MutationObserver的方案。但是MutationObserver的语法较复杂,回调函数的写法也不易于使用,因此本库将相关代码加以封装,让元素获取一步到位,方便脚本的快速开发。

بۇ قوليازمىنى بىۋاسىتە قاچىلاشقا بولمايدۇ. بۇ باشقا قوليازمىلارنىڭ ئىشلىتىشى ئۈچۈن تەمىنلەنگەن ئامبار بولۇپ، ئىشلىتىش ئۈچۈن مېتا كۆرسەتمىسىگە قىستۇرىدىغان كود: // @require https://update.greasyfork.org/scripts/477884/1267853/ElementGetter_gf.js

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

ئاپتورى
ZHider Joo
نەشرى
2.0.0
قۇرۇلغان ۋاقتى
2023-10-20
يېڭىلانغان ۋاقتى
2023-10-20
Size
7.4 KB
ئىجازەتنامىسى
يوق

转载 @cxxjackie 大佬的脚本:https://bbs.tampermonkey.net.cn/thread-2726-1-1.html

elmGetter具有以下属性和方法:

属性

currentSelector

只读属性,一个表示当前选择器类型的字符串,可通过selector方法改变。

方法

get(selector [, parent] [, timeout])

selector 必须, 选择器或选择器数组,默认使用css选择器
parent 可选,父节点,默认值document。
timeout 可选,超时时间(毫秒),默认值0。
返回值 Promise,selector为选择器时返回元素,为数组时返回元素数组。

根据选择器或选择器数组获取元素,若元素已存在则返回,否则监听父节点的元素插入,直至获取到元素或达到超时时间为止。返回类型为Promise,使用.then或async/await取得元素:

// 回调写法
(function() {
    elmGetter.get('div').then(div => {
        console.log(div);
    });
})();
// 同步写法
(async function() {
    const div = await elmGetter.get('div');
    console.log(div);
})();

使用async/await时,若需要在同一父节点上同时获取多个元素,多次await可能造成性能问题,可以用Promise.all优化性能,库内部会尝试将多个监听器合并为一个。为简化这一流程,get方法的第一个参数允许是选择器数组,效果等同于Promise.all,参考以下示例:

// 以下两种写法等价
const [elm1, elm2, elm3] = await Promise.all([
  elmGetter.get('.elm1'),
  elmGetter.get('.elm2'),
  elmGetter.get('.elm3')
]);
const [elm1, elm2, elm3] = await elmGetter.get(['.elm1', '.elm2', '.elm3']);

each(selector[, parent], callback)

selector 必须,选择器,默认使用css选择器。\ parent 可选,父节点,默认值document。\ callback 必须,回调函数。\ 返回值 无。

为父节点设置监听,所有符合选择器的元素(包括页面已有的和新插入的)都将被传给回调函数处理,回调函数只在每个元素上触发一次。 回调函数接收2个参数,第一个是符合选择器的元素,第二个表明该元素是否为新插入的(已有为false,插入为true)。each方法适用于各种滚动加载的列表(如评论区),或者发生非刷新跳转的页面等,参考以下示例:

// b站评论区自动展开回复
elmGetter.each('.reply-item', document, reply => {
    const btn = reply.querySelector('.view-more-btn');
    if (btn) btn.click();
});

令回调函数返回false即可移除监听,参考以下示例:

const listener = elmGetter.each('div', document, (elm, isInserted) => {
    if (isInserted) {
        return false;
    }
});

selector(desc)

desc 可选,指定选择器类型,参考下述说明。\ 返回值 生效的选择器类型。\ 根据传入的参数更改get方法和each方法的选择器类型,可以被多次调用。参数desc存在以下几种情况:\ 传入jQuery引用:使用jQuery选择器。\ 传入"jquery"字符串(不区分大小写):在window和unsafeWindow中检索jQuery引用,若存在则使用jQuery选择器,否则使用css选择器。\ 传入"xpath"字符串(不区分大小写):使用XPath选择器。\ 其他情况或未调用selector方法:使用css选择器。

关于jQuery选择器\ jQuery必须为页面原有或脚本引入,库本身不包含jQuery。\ 指定jQuery选择器时,get方法和each方法将返回jQuery节点。\ 尽管ElementGetter兼容低版本jQuery,但不同版本的jQuery对选择器的支持不尽相同,请自行测试或查询相关文档,以免得不到预期结果。\ 关于XPath选择器\ 库只获取元素,请勿匹配非元素节点。\ 以/或//开头时,根节点将基于document,这会导致父节点被忽略,要使其正确生效,选择器应以./或.//开头。\ XPath选择器不能直接作用于shadowRoot,参考“在特定环境中应用get和each方法”。\ XPath在性能上会不可避免地比css和jQuery慢,如非必要,不建议作为首选。

以下是一个使用XPath匹配特定文本内容元素的示例:

elmGetter.selector('xpath');
elmGetter.get('.//*[contains(text(),"待匹配文本")]').then(el => {
    console.log(el);
});

create(domString[, returnList][, parent])

domString 必须,待解析的字符串。\ returnList 可选,布尔值,是否返回以id作为索引的元素列表。\ parent 可选,父节点,将创建的元素添加到父节点末尾处。\ 返回值 元素或对象,取决于returnList参数。

将html字符串解析为元素。注意,该方法只会返回一个元素,多个元素并列时返回第一个。示例:

const div = elmGetter.create('<div class="mydiv">Hello world</div>', document.body);

若returnList为true,则在创建后的元素中查找其所有具有id的后代元素,并返回一个以id作为索引的对象,该元素本身以0为索引。示例:

const list = elmGetter.create(`
    <div>
        <div id="div1"></div>
        <div>
            <div id="div2"></div>
        </div>
    </div>
`, true);
console.log(list[0], list.div1, list.div2);

在特定环境中应用get和each方法

同源iframe:指定父节点为iframe.contentDocument即可。\ shadow DOM:指定父节点为shadowRoot即可。若使用XPath选择器,则父节点不能为shadowRoot,但可以shadowRoot内的元素为目标,参考以下示例:

const shadowRoot = document.querySelector('#shadow_dom').shadowRoot;
const div = shadowRoot.querySelector('div');
elmGetter.selector('xpath');
elmGetter.each('.//*', div, el => {
    console.log(el);
});
// 错误用法:
elmGetter.get('.//*', shadowRoot); // '#document-fragment' is not a valid context node type.

综合示例

以下综合示例展示了该库是如何工作的:

// ==UserScript==
// @name         油猴中文网一键登录 - 示例脚本
// @namespace    ...
// @author       ...
// @version      2.0.0
// @match        https://bbs.tampermonkey.net.cn/*
// @require      https://scriptcat.org/lib/513/2.0.0/ElementGetter.js
// ==/UserScript==

(function() {
    'use strict';
    /* global elmGetter */
    elmGetter.each('[id^="loginform_"]', document, form => {
        const submit = form.querySelector('[name="loginsubmit"]');
        const button = elmGetter.create(`
<button class="pn pnc" type="submit" tabindex="1" style="margin-left: 20px;">
    <strong>一键登录</strong>
</button>
        `, submit.parentNode);
        button.addEventListener('click', async e => {
            e.stopImmediatePropagation();
            // 这里也可以直接用querySelector,代码仅作为示例
            const [username, password, cookietime] = await elmGetter.get([
                '[id^="username_"]',
                '[id^="password3_"]',
                '[id^="cookietime_"]'
            ], form);
            username.value = '12345'; // 用户名
            password.value = '54321'; // 密码
            cookietime.checked = true;
            submit.click();
        }, true);
    });
})();

更新日志

1.0.0\ 初始版本\

1.1.0\ 1.修复each方法的回调函数可能在相同元素上反复触发的问题,现在每个元素只会触发一次。\ 2.新增jQuery支持。\ 3.get方法和each方法允许单独省略parent参数。\

1.1.1\ 1.修复上个版本的一点遗留问题(有句代码忘了删- -)。\ 2.each的回调函数现在可以通过return false移除监听(考虑改动remove机制,可能不向下兼容)。\

1.2.0\ 1.废弃remove方法,改用return false的方式移除监听,each方法不再具有返回值。\ 2.废弃MutationEvent兼容。get方法和each方法现在额外监听属性变化,以使属性选择器的结果更准确。\ 3.each方法优化。\ 4.create方法新增parent参数。\ 5.精简了下代码,代码量减少约20%。\

1.2.1\ 对私有属性的语法降级,以兼容低版本浏览器。\

2.0.0\ 1.库不再需要实例化,不向下兼容。\ 2.新增selector方法,支持XPath选择器。\ 3.create方法新增returnList参数。