【问题标题】:How to know when Google search results page renders its results?如何知道 Google 搜索结果页面何时呈现结果?
【发布时间】:2014-01-02 04:41:02
【问题描述】:

我正在编写一个 Chrome 扩展程序,将脚本注入到 Google 的搜索结果页面并修改所有结果的锚元素。

我的问题是结果是异步呈现的,并且在文档加载/就绪时未显示在页面中。

我有 2 个无效的初始解决方案:

  1. 设置超时:不好的做法,但它有效。不过,可能会显示不一致的结果,因此我宁愿避免使用此解决方案。

  2. 绑定到“DOMNodeInserted”。通常有效,但在我的情况下更复杂,因为我在锚之前插入了自己的新节点,这会触发递归。如果锚已经被“标记”,我可以插入代码来避免它,但同样,这个解决方案很糟糕,因为每次插入节点时我都需要遍历所有锚 - 从我检查的结果来看,这种情况发生了超过 140 次搜索结果页面。

搜索结果页面上是否有任何类型的自定义事件 Google 触发器?在这种情况下还有其他 DOM 事件可以工作吗?

【问题讨论】:

    标签: dom google-chrome-extension google-search dom-events


    【解决方案1】:

    你是对的,使用“DOMNodeInserted”不是一个好方法。如果不出意外,它是过时的 Mutation Events API 的一部分,由于效率低下而被弃用(除其他原因外)。

    它已被 MutationObserver API 取代,因此您应该使用它。您可以利用 MutationObserver 来观察根节点及其后代上的“childList”DOM 突变。
    (如果您选择这种方法,mutation-summary library 也可能会派上用场。)

    经过(非常浅薄的)搜索后,我发现(至少对我而言)Google 将其结果放在div 中,id search。以下是执行以下操作的示例扩展的代码:

    1. 注册一个 MutationObserver 以检测 div#search 插入 DOM。

    2. 注册一个 MutationObserver 以检测 div#search 及其后代中的“childList”变化。

    3. 每当添加<a> 节点时,函数都会遍历相关节点并修改链接。 (出于显而易见的原因,该脚本忽略了 <script> 元素。)

    这个示例扩展只是将链接文本包含在 ~~ 中,但您可以轻松更改它以执行您需要的任何操作。

    ma​​nifest.json:

    {
        "manifest_version": 2,
        "name":    "Test Extension",
        "version": "0.0",
    
        "content_scripts": [{
            "matches": [
                ...
                "*://www.google.gr/*",
                "*://www.google.com/*"
            ],
            "js":         ["content.js"],
            "run_at":     "document_end",
            "all_frames": false
        }],
    
    }
    

    content.js:

    console.log("Injected...");
    
    /* MutationObserver configuration data: Listen for "childList"
     * mutations in the specified element and its descendants */
    var config = {
        childList: true,
        subtree: true
    };
    var regex = /<a.*?>[^<]*<\/a>/;
    
    /* Traverse 'rootNode' and its descendants and modify '<a>' tags */
    function modifyLinks(rootNode) {
        var nodes = [rootNode];
        while (nodes.length > 0) {
            var node = nodes.shift();
            if (node.tagName == "A") {
                /* Modify the '<a>' element */
                node.innerHTML = "~~" + node.innerHTML + "~~";
            } else {
                /* If the current node has children, queue them for further
                 * processing, ignoring any '<script>' tags. */
                [].slice.call(node.children).forEach(function(childNode) {
                    if (childNode.tagName != "SCRIPT") {
                        nodes.push(childNode);
                    }
                });
            }
        }
    }
    
    /* Observer1: Looks for 'div.search' */
    var observer1 = new MutationObserver(function(mutations) {
        /* For each MutationRecord in 'mutations'... */
        mutations.some(function(mutation) {
            /* ...if nodes have beed added... */
            if (mutation.addedNodes && (mutation.addedNodes.length > 0)) {
                /* ...look for 'div#search' */
                var node = mutation.target.querySelector("div#search");
                if (node) {
                    /* 'div#search' found; stop observer 1 and start observer 2 */
                    observer1.disconnect();
                    observer2.observe(node, config);
    
                    if (regex.test(node.innerHTML)) {
                        /* Modify any '<a>' elements already in the current node */
                        modifyLinks(node);
                    }
                    return true;
                }
            }
        });
    });
    
    /* Observer2: Listens for '<a>' elements insertion */
    var observer2 = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            if (mutation.addedNodes) {
                [].slice.call(mutation.addedNodes).forEach(function(node) {
                    /* If 'node' or any of its desctants are '<a>'... */
                    if (regex.test(node.outerHTML)) {
                        /* ...do something with them */
                        modifyLinks(node);
                    }
                });
            }
        });
    });
    
    /* Start observing 'body' for 'div#search' */
    observer1.observe(document.body, config);
    

    【讨论】:

    • 感谢 MutationObservers 的示例 - 我从未使用过它们。这应该有效。
    • 谢谢,你的答案正是我正在寻找的 :)))
    • @ExpertSystem 嗨,我如何只能在根锚标签上插入~~~~,比如搜索结果标题底部的锚标签?
    【解决方案2】:

    一般情况下,您可以使用Mutation Observers 来监听文档更改。为避免递归,只需在更改文档之前断开突变观察器,然后再次启用它。
    从概念上讲,它与DOMNodeInserted 事件没有太大区别,因此您也可以移除事件侦听器,插入节点,然后重新绑定事件侦听器。但是,突变观察者更有效,因此您应该使用它们而不是 DOM 突变事件。

    在这种特定情况下(Google 的搜索结果),您还可以使用hashchange event 来检测 Google 何时呈现新的搜索结果。这种方法之所以有用,是因为位置片段、搜索词和搜索结果之间存在关联:

    1. 用户输入搜索词Enter
    2. 搜索结果已更新。
    3. 位置片段已更改 (https://www.google.com/search?q=old#q=&lt;new term&gt;)。

    例子:

    // On document load
    printResult();
    // Whenever the search term is changed
    window.addEventListener('hashchange', function(event) {
        printResult();
    });
    function printResult() {
        // Example: Print first search result
        console.log(document.querySelector('h3 a').href);
    }
    

    【讨论】:

    • hashchange 事件会很棒,但它不会在第一个搜索结果上触发,它只会在第一个搜索结果之后触发。
    • @rob-w,我收到此错误:contentscript.js:22 Uncaught TypeError: Cannot read property 'href' of null
    猜你喜欢
    • 2016-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-27
    • 2016-07-13
    • 1970-01-01
    • 2011-12-13
    相关资源
    最近更新 更多