聊聊 CSS 选择器
firefox 开发者工具有一个很酷的功能:在查看器中右键单击某元素,选择“复制唯一选择器”,刻意复制定位至该元素的唯一选择器。这跟 CSS 选择器的功能恰好相反,可以视作 document.querySelector
的逆运算。
这个功能 firefox 怎么实现的呢? grep
源码,追踪到 browser/devtools/inspector/inspector-panel.js
文件中下列代码:
/**
* Copy a unique selector of the selected Node to the clipboard.
*/
function InspectorPanel_copyUniqueSelector() {
if (!this.selection.isNode()) {
return;
}
let toCopy = CssLogic.findCssSelector(this.selection.node);
if (toCopy) {
clipboardHelper.copyString(toCopy);
}
}
根据 CssLogic
又跟踪到 toolkit/devtools/styleinspector/css-logic.js
文件:
/**
* Find a unique CSS selector for a given element
* @returns a string such that ele.ownerDocument.querySelector(reply) === ele
* and ele.ownerDocument.querySelectorAll(reply).length === 1
*/
CssLogic.findCssSelector = function CssLogic_findCssSelector(ele) {
ele = LayoutHelpers.getRootBindingParent(ele);
var document = ele.ownerDocument;
if (!document || !document.contains(ele)) {
throw new Error('findCssSelector received element not inside document');
}
// document.querySelectorAll("#id") returns multiple if elements share an ID
if (ele.id && document.querySelectorAll('#' + CSS.escape(ele.id)).length === 1) {
return '#' + CSS.escape(ele.id);
}
// Inherently unique by tag name
var tagName = ele.localName;
if (tagName === 'html') {
return 'html';
}
if (tagName === 'head') {
return 'head';
}
if (tagName === 'body') {
return 'body';
}
// We might be able to find a unique class name
var selector, index, matches;
if (ele.classList.length > 0) {
for (var i = 0; i < ele.classList.length; i++) {
// Is this className unique by itself?
selector = '.' + CSS.escape(ele.classList.item(i));
matches = document.querySelectorAll(selector);
if (matches.length === 1) {
return selector;
}
// Maybe it's unique with a tag name?
selector = tagName + selector;
matches = document.querySelectorAll(selector);
if (matches.length === 1) {
return selector;
}
// Maybe it's unique using a tag name and nth-child
index = positionInNodeList(ele, ele.parentNode.children) + 1;
selector = selector + ':nth-child(' + index + ')';
matches = document.querySelectorAll(selector);
if (matches.length === 1) {
return selector;
}
}
}
// Not unique enough yet. As long as it's not a child of the document,
// continue recursing up until it is unique enough.
if (ele.parentNode !== document) {
index = positionInNodeList(ele, ele.parentNode.children) + 1;
selector = CssLogic_findCssSelector(ele.parentNode) + ' > ' +
tagName + ':nth-child(' + index + ')';
}
return selector;
};