我正在编写Chrome扩展程序,将脚本注入Google的搜索结果页面并修改所有结果的锚点元素。
我的问题是结果是异步呈现的,并且未在文档加载/就绪的页面中显示。
我有两个不起作用的初始解决方案:
设置超时:不好的做法但是有效。然而,可能会显示不一致的结果,所以我更愿意避免这种解决方案。
绑定到'DOMNodeInserted'。通常是有效的,但在我的情况下更复杂,因为我在锚之前插入新的节点,这会触发递归。如果锚已被“标记”,我可以插入代码来避免它,但同样,这个解决方案很糟糕,因为每次插入一个节点时我都需要遍历所有锚点 - 从我检查过这种情况发生了140多次搜索结果页面。
搜索结果页面上是否有任何类型的自定义事件Google触发器?在这种情况下是否还有其他可以发挥作用的DOM事件?
答案 0 :(得分:2)
通常,您可以使用Mutation Observers来监听文档更改。为了避免递归,只需在更改文档之前断开变异观察者,然后再次启用它
从概念上讲,它与DOMNodeInserted
事件没有太大区别,因此您还可以删除事件侦听器,插入节点,然后重新绑定事件侦听器。但是,Mutation观察者更有效率,因此您应该使用这些而不是DOM突变事件。
在这种特定情况下(Google的搜索结果),您还可以使用hashchange
event来检测Google何时呈现新的搜索结果。此方法仅有用,因为位置片段,搜索项和搜索结果之间存在相关性:
https://www.google.com/search?q=old#q=<new term>
)。示例:
// 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);
}
答案 1 :(得分:2)
你是对的,使用“DOMNodeInserted”不是一个好方法。如果不出意外,它就是过时的 Mutation Events API 的一部分,因其出了名的低效率而被弃用(以及其他原因)。
它已被 MutationObserver API 取代,因此您应该使用它。您可以使用 MutationObserver 来观察根节点及其后代上的“childList”DOM突变。
(如果您选择此方法, mutation-summary library 也可能派上用场。)
在(非常浅)搜索之后,我发现(至少对我来说)Google将其结果放在div
id search
。以下是执行以下操作的示例扩展的代码:
注册MutationObserver以检测div#search
到DOM的插入。
注册MutationObserver以检测div#search
及其后代中的“childList”更改。
每当添加<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);