反向CSS选择器查找是一件本质上棘手的事情。我通常遇到两种解决方案:
沿着DOM树向上组装选择器字符串,由元素名称、类和id
或name
属性的组合构成。这种方法的问题在于它可能会导致选择器返回多个元素,如果我们要求它们只选择一个唯一的元素,则无法达到要求。
使用nth-child()
或nth-of-type()
组装选择器字符串,这可能会导致非常长的选择器。在大多数情况下,选择器越长,具体性就越高,具体性越高,当DOM结构发生变化时,它就越容易被破坏。
以下解决方案是尝试解决这两个问题的混合方法。它是一种混合方法,输出一个唯一的CSS选择器(即,document.querySelectorAll(getUniqueSelector(el))
应该始终返回一个单项数组)。虽然返回的选择器字符串不一定是最短的,但它是以CSS选择器效率为重点,同时平衡特异性的选择,通过将nth-of-type()
和nth-child()
优先级放在最后。
您可以通过更新aAttr
数组来指定要合并到选择器中的属性。最低浏览器要求为IE 9。
function getUniqueSelector(elSrc) {
if (!(elSrc instanceof Element)) return;
var sSel,
aAttr = ['name', 'value', 'title', 'placeholder', 'data-*'],
aSel = [],
getSelector = function(el) {
if (el.id) {
aSel.unshift('#' + el.id);
return true;
}
aSel.unshift(sSel = el.nodeName.toLowerCase());
if (el.className) {
aSel[0] = sSel += '.' + el.className.trim().replace(/ +/g, '.');
if (uniqueQuery()) return true;
}
for (var i=0; i<aAttr.length; ++i) {
if (aAttr[i]==='data-*') {
var aDataAttr = [].filter.call(el.attributes, function(attr) {
return attr.name.indexOf('data-')===0;
});
for (var j=0; j<aDataAttr.length; ++j) {
aSel[0] = sSel += '[' + aDataAttr[j].name + '="' + aDataAttr[j].value + '"]';
if (uniqueQuery()) return true;
}
} else if (el[aAttr[i]]) {
aSel[0] = sSel += '[' + aAttr[i] + '="' + el[aAttr[i]] + '"]';
if (uniqueQuery()) return true;
}
}
var elChild = el,
sChild,
n = 1;
while (elChild = elChild.previousElementSibling) {
if (elChild.nodeName===el.nodeName) ++n;
}
aSel[0] = sSel += ':nth-of-type(' + n + ')';
if (uniqueQuery()) return true;
elChild = el;
n = 1;
while (elChild = elChild.previousElementSibling) ++n;
aSel[0] = sSel = sSel.replace(/:nth-of-type\(\d+\)/, n>1 ? ':nth-child(' + n + ')' : ':first-child');
if (uniqueQuery()) return true;
return false;
},
uniqueQuery = function() {
return document.querySelectorAll(aSel.join('>')||null).length===1;
};
while (elSrc.parentNode) {
if (getSelector(elSrc)) return aSel.join(' > ');
elSrc = elSrc.parentNode;
}
}
:eq(1)
或者:nth-child(2)
而不是[1]
。 - Andy E