![](/img/trans.png)
[英]Chrome Dev Tools script injection that can survive page reload
[英]Chrome Dev Tools - Remembering the selected element through page reload
我正在构建自己的类型的Chrome开发工具(检查元素),该工具也将以使用JavaScript的Chrome扩展程序的形式出现。 我希望它具有与Chrome开发者工具类似的功能,但我发现其中之一很难揭露他们是如何做到的。
基本上,当您右键单击任何元素并单击“检查元素”时,它将打开开发工具(如果尚未打开),并将鼠标悬停在您的右键单击定位的HTML元素上。
我要复制的一点是,当刷新页面的同时仍在开发工具中选择了元素,它将重新加载所有HTML并直接转到在重新加载之前在开发工具中选择的元素。
如果不清楚,这就是我的意思:
HTML:
<div class="1">
<div class="2">
<div class="3"></div>
</div>
</div>
如果我将鼠标悬停在类别为“ 3”的div上并刷新了页面,则Chrome开发人员工具知道可以使用突出显示该确切div的开发人员工具进行重新加载。 即使存在多个具有该类或类似结构的div,它也始终会悬停在正确的位置上。
谁知道这里最好的方法是否是拥有一个大的if语句,该语句查找元素的某些特征,例如理想的id,但是如果元素没有id(例如周围元素或唯一元素),还会查找大量回退与该元素关联的类/属性?
我曾尝试搜索“ Chrome扩展程序节点选择器”或类似的变体,但无法找到任何信息。
已经存在一个非常好的“检查元素”扩展,您可以在这里找到。 但是,它不支持保存检查的元素。 但是,由于这是一个赏金问题,我可以给您一些提示,以将其改进为可行的解决方案。
我在这里看到的主要问题是“序列化”元素。 您可以在此处找到有关此主题的好答案。 (另请参见小提琴 )它基本上可以概括为“使用ID查找最近的祖先,并向下跟踪到被检查元素的路径”。 在您的情况下,可能是:
tracePath: function (element, result) {
if (element.id !== '') {
result.push({
id: element.id
});
return;
}
if (element === document.body) {
result.push({
tag: element.tagName
});
return;
}
var siblings = element.parentNode.childNodes;
for (var i = 0; i < siblings.length; ++i) {
var sibling = siblings[i];
if (sibling === element) {
result.push({
index: i,
tag: element.tagName
});
return this.tracePath(element.parentNode, result);
}
}
},
上面只是存储了反序列化期间要遵循的节点数组:
find: function (path) {
var element;
while (path.length) {
var current = path.pop();
if (!current) return element;
if (current.id) element = document.getElementById(current.id);
else if (element) {
if (current.index < element.childNodes.length && current.tag === element.childNodes[current.index].tagName)
element = element.childNodes[current.index];
else
return;
} else {
var matches = document.getElementsByTagName(current.tag);
if (matches.length)
element = matches[0];
}
}
return element;
},
一旦这两个,我们只需要存储/加载选择的方法:
store: function (path) {
var selection = Object.create({});
selection[window.location.href] = path;
chrome.storage.local.set(selection, function () {
if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError)
}
})
},
并加载:
load: function () {
var self = this, key = window.location.href;
chrome.storage.local.get(key, function (found) {
if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError)
} else if (found && found[key]) {
var path = found[key],
element = Util.find(path);
if (element) {
// zoom into inspected element
element.scrollIntoView();
// add selection to Inspector instance
self.$selection = element;
// function similar to layout() - highlights inspected element
self.select();
}
}
})
}
然后,您可以编写一个突出显示功能,例如select
以突出显示被检查的元素,类似于已经存在的突出标尺的layout
。
单击该元素即可完成保存:
registerEvents: function() {
...
document.addEventListener('click', function () {
var path = [];
Util.tracePath(this.$target, path);
this.$selection = this.$target;
this.select(true);
this.storeSelection(path);
}.bind(this));
...
},
有一个叉子,其中包含上述修改,您可以在此处找到。 您可以通过下载它并在chrome://extensions/
下的新标签中将app
文件夹作为未压缩的扩展名加载来试用。 请享用!
您始终可以将元素的信息(例如id
, class
, font-family
等)存储到chrome.storage.local
/ sync
。
要处理重新加载,您可以执行一些操作,例如在自定义开发工具标签中获取chrome.storage
对象和window.location(myElement)
。
这是我为您解决的问题的解决方法。
想法 :要求内容脚本为被检查的元素选择唯一的选择器,然后将其保存到chrome.storage.local
,这样您就可以加载选择器,查找元素并为其运行inspect()
。
首先是manifest.json文件:
{
"manifest_version": 2,
"name": "remember element",
"version": "1.0.0",
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content-script.js"],
"run_at": "document_start"
}
],
"permissions": [
"storage"
],
"devtools_page": "loader.html"
}
然后,我在Devtools中创建一个新面板。
loader.html
<!doctype html>
<html lang="en">
<body>
<script src="loader.js"></script>
</body>
</html>
loader.js
chrome.devtools.panels.create('Remember element', null, 'panel.html');
panel.html
<!doctype html>
<html>
<body>
<button id="save">Save</button>
<button id="get">Get</button>
<script src="panel.js"></script>
</body>
</html>
因此,该面板包含两个按钮:一个用于保存当前检查的元素,第二个用于检查先前保存的元素。
这是我们在panel.js中需要的:
document.addEventListener('DOMContentLoaded', () => {
// Ask content-script to resolve unique selector and save it in the storage
document.getElementById('save').addEventListener('click', () => {
chrome
.devtools
.inspectedWindow
.eval("saveUniqueSelector($0)", {
useContentScriptContext: true // run the code in the content-script
});
});
document.getElementById('get').addEventListener('click', () => {
// Getting saved selector
chrome.storage.local.get('prev_selected', items => {
// Find the element by selector and run inspection for it
chrome
.devtools
.inspectedWindow
.eval(`inspect(document.querySelector("${items.prev_selected}"))`);
});
});
});
和content-script.js
/**
* Saves unique selector for the given element into chrome.storage
*
* The function invokes by panel.js ("chrome.devtools.inspectedWindow.eval")
* @param el {HTMLElement}
*/
function saveUniqueSelector(el) {
if (el) {
const selectorGenerator = new CssSelectorGenerator();
const selector = selectorGenerator.getSelector(el);
if (selector) {
console.log('Saving selector', selector);
chrome.storage.local.set({prev_selected: selector});
}
}
}
有很多库可用于获取元素的唯一选择器。 我在这里使用css-selector-generator
这个例子有一个很大的缺陷:在devtools中选择元素时,我没有找到为元素自动保存的方法。 (setInterval除外,但这不是一个好主意)。
扩展DomFlags正是这样做的。 您可以在元素面板中设置多个标志,并且第一个会在页面重新加载时自动选择。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.