jne*_*ney 19 javascript css css-selectors
我有这个函数来获取一个cssPath:
var cssPath = function (el) {
var path = [];
while (
(el.nodeName.toLowerCase() != 'html') &&
(el = el.parentNode) &&
path.unshift(el.nodeName.toLowerCase() +
(el.id ? '#' + el.id : '') +
(el.className ? '.' + el.className.replace(/\s+/g, ".") : ''))
);
return path.join(" > ");
}
console.log(cssPath(document.getElementsByTagName('a')[123]));
Run Code Online (Sandbox Code Playgroud)
但我有这样的事情:
html > body > div#div-id > div.site > div.clearfix > ul.choices > li
但要完全正确,它应该是这样的:
html > body > div#div-id > div.site:nth-child(1) > div.clearfix > ul.choices > li:nth-child(5)
有人有任何想法只是在javascript中实现它?
ass*_*lin 24
上面的答案实际上有一个错误 - 当遇到非元素节点(例如文本节点)时,while循环过早地中断,导致不正确的CSS选择器.
这是一个改进版本,修复了这个问题加:
nth-of-type()使选择器更具可读性
var cssPath = function(el) {
if (!(el instanceof Element))
return;
var path = [];
while (el.nodeType === Node.ELEMENT_NODE) {
var selector = el.nodeName.toLowerCase();
if (el.id) {
selector += '#' + el.id;
path.unshift(selector);
break;
} else {
var sib = el, nth = 1;
while (sib = sib.previousElementSibling) {
if (sib.nodeName.toLowerCase() == selector)
nth++;
}
if (nth != 1)
selector += ":nth-of-type("+nth+")";
}
path.unshift(selector);
el = el.parentNode;
}
return path.join(" > ");
}
Gum*_*mbo 14
要始终获得正确的元素,您将需要使用:nth-child()或:nth-of-type()用于不唯一标识元素的选择器.试试这个:
var cssPath = function(el) {
if (!(el instanceof Element)) return;
var path = [];
while (el.nodeType === Node.ELEMENT_NODE) {
var selector = el.nodeName.toLowerCase();
if (el.id) {
selector += '#' + el.id;
} else {
var sib = el, nth = 1;
while (sib.nodeType === Node.ELEMENT_NODE && (sib = sib.previousSibling) && nth++);
selector += ":nth-child("+nth+")";
}
path.unshift(selector);
el = el.parentNode;
}
return path.join(" > ");
}
Run Code Online (Sandbox Code Playgroud)
你可以添加一个例行检查在其对应的背景下独特的元素(如TITLE,BASE,CAPTION,等).
执行反向 CSS 选择器查找本质上是一件棘手的事情。我通常遇到两种类型的解决方案:
沿着 DOM 树向上走,从元素名称、类和idorname属性的组合中组合出选择器字符串。这种方法的问题在于它可能导致选择器返回多个元素,如果我们要求它们只选择一个唯一的元素,它不会削减它。
使用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-*'], // Common attributes
aSel = [],
// Derive selector from element
getSelector = function(el) {
// 1. Check ID first
// NOTE: ID must be unique amongst all IDs in an HTML5 document.
// https://www.w3.org/TR/html5/dom.html#the-id-attribute
if (el.id) {
aSel.unshift('#' + el.id);
return true;
}
aSel.unshift(sSel = el.nodeName.toLowerCase());
// 2. Try to select by classes
if (el.className) {
aSel[0] = sSel += '.' + el.className.trim().replace(/ +/g, '.');
if (uniqueQuery()) return true;
}
// 3. Try to select by classes + attributes
for (var i=0; i<aAttr.length; ++i) {
if (aAttr[i]==='data-*') {
// Build array of data attributes
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;
}
}
// 4. Try to select by nth-of-type() as a fallback for generic elements
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;
// 5. Try to select by nth-child() as a last resort
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;
},
// Test query to see if it returns one element
uniqueQuery = function() {
return document.querySelectorAll(aSel.join('>')||null).length===1;
};
// Walk up the DOM tree to compile a unique selector
while (elSrc.parentNode) {
if (getSelector(elSrc)) return aSel.join(' > ');
elSrc = elSrc.parentNode;
}
}
Run Code Online (Sandbox Code Playgroud)
另外两个提供的答案有一些与我遇到的浏览器兼容性的假设.下面的代码不会使用nth-child,也会检查previousElementSibling.
function previousElementSibling (element) {
if (element.previousElementSibling !== 'undefined') {
return element.previousElementSibling;
} else {
// Loop through ignoring anything not an element
while (element = element.previousSibling) {
if (element.nodeType === 1) {
return element;
}
}
}
}
function getPath (element) {
// False on non-elements
if (!(element instanceof HTMLElement)) { return false; }
var path = [];
while (element.nodeType === Node.ELEMENT_NODE) {
var selector = element.nodeName;
if (element.id) { selector += ('#' + element.id); }
else {
// Walk backwards until there is no previous sibling
var sibling = element;
// Will hold nodeName to join for adjacent selection
var siblingSelectors = [];
while (sibling !== null && sibling.nodeType === Node.ELEMENT_NODE) {
siblingSelectors.unshift(sibling.nodeName);
sibling = previousElementSibling(sibling);
}
// :first-child does not apply to HTML
if (siblingSelectors[0] !== 'HTML') {
siblingSelectors[0] = siblingSelectors[0] + ':first-child';
}
selector = siblingSelectors.join(' + ');
}
path.unshift(selector);
element = element.parentNode;
}
return path.join(' > ');
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
15016 次 |
| 最近记录: |