我正在编写Chrome扩展程序,将脚本注入Google的搜索结果页面并修改所有结果的主要元素.
我的问题是结果是异步呈现的,并且未在文档加载/就绪的页面中显示.
我有2个初始解决方案不起作用:
>设置超时:不好的做法,但它的工作原理.然而,可能会显示不一致的结果,所以我宁愿避免这种解决方案.
>绑定到’DOMNodeInserted’.通常可行,但在我的情况下更复杂,因为我在锚点之前插入新的节点,这会触发递归.如果锚已被“标记”,我可以插入代码以避免它,但同样,这个解决方案很糟糕,因为每次插入一个节点时我都需要遍历所有锚点 – 从我检查过这种情况发生的次数超过140次搜索结果页面.
解决方法
你是对的,使用“DOMNodeInserted”不是一个好方法.如果不出意外,它就是过时的
Mutation Events API的一部分,由于臭名昭着的低效率而被弃用(以及其他原因).
它已被MutationObserver API取代,所以这是你应该使用的.您可以使用MutationObserver在根节点及其后代上观察“childList”DOM突变.
(如果选择这种方法,mutation-summary library也可能派上用场.)
在(非常浅)搜索之后,我发现(至少对我而言)Google将其结果放在带有id搜索的div中.以下是执行以下操作的示例扩展的代码:
>注册MutationObserver以检测div#search插入DOM.
>注册MutationObserver以检测div#search及其后代中的“childList”更改.
>每当a< a>添加节点,函数遍历相关节点并修改链接. (出于显而易见的原因,脚本会忽略< script>元素.)
此示例扩展只是将链接的文本包含在~~中,但您可以轻松地将其更改为执行您需要的任何操作.
manifest.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);