在伪代码中,这就是我想要的。
var selector = $(this).cssSelectorAsString(); // Made up method...
// selector is now something like: "html>body>ul>li>img[3]"
var element = $(selector);
原因是我需要将其传递给外部环境,其中字符串是我交换数据的唯一方式。然后,此外部环境需要发回结果以及要更新的元素。所以我需要能够为页面上的每个元素序列化一个唯一的CSS选择器。
我注意到jquery有一个selector
方法,但它似乎不适用于此上下文。它仅在使用选择器创建对象时才有效。如果使用HTML节点对象创建对象,则它不起作用。
答案 0 :(得分:51)
我现在看到一个插件存在(我也想到了同名),但这里只是我编写的一些快速JavaScript。它不需要考虑元素的id或类 - 只有结构(并且在节点名称不明确时添加:eq(x)
)。
jQuery.fn.getPath = function () {
if (this.length != 1) throw 'Requires one element.';
var path, node = this;
while (node.length) {
var realNode = node[0], name = realNode.localName;
if (!name) break;
name = name.toLowerCase();
var parent = node.parent();
var siblings = parent.children(name);
if (siblings.length > 1) {
name += ':eq(' + siblings.index(realNode) + ')';
}
path = name + (path ? '>' + path : '');
node = parent;
}
return path;
};
答案 1 :(得分:8)
jQuery-GetPath是一个很好的起点:它会给你这个项目的祖先,就像这样:
var path = $('#foo').getPath();
// e.g., "html > body > div#bar > ul#abc.def.ghi > li#foo"
答案 2 :(得分:7)
这是Blixt的一个版本,可以在IE中使用:
jQuery.fn.getPath = function () {
if (this.length != 1) throw 'Requires one element.';
var path, node = this;
while (node.length) {
var realNode = node[0];
var name = (
// IE9 and non-IE
realNode.localName ||
// IE <= 8
realNode.tagName ||
realNode.nodeName
);
// on IE8, nodeName is '#document' at the top level, but we don't need that
if (!name || name == '#document') break;
name = name.toLowerCase();
if (realNode.id) {
// As soon as an id is found, there's no need to specify more.
return name + '#' + realNode.id + (path ? '>' + path : '');
} else if (realNode.className) {
name += '.' + realNode.className.split(/\s+/).join('.');
}
var parent = node.parent(), siblings = parent.children(name);
if (siblings.length > 1) name += ':eq(' + siblings.index(node) + ')';
path = name + (path ? '>' + path : '');
node = parent;
}
return path;
};
答案 3 :(得分:5)
我只是想分享我的版本,因为它很清楚。我在所有常见的浏览器中测试了这个脚本,它就像老板一样工作。
jQuery.fn.getPath = function () {
var current = $(this);
var path = new Array();
var realpath = "BODY";
while ($(current).prop("tagName") != "BODY") {
var index = $(current).parent().find($(current).prop("tagName")).index($(current));
var name = $(current).prop("tagName");
var selector = " " + name + ":eq(" + index + ") ";
path.push(selector);
current = $(current).parent();
}
while (path.length != 0) {
realpath += path.pop();
}
return realpath;
}
答案 4 :(得分:3)
与@Blixt的解决方案相同,但与多个jQuery元素兼容。
jQuery('.some-selector')
可以生成一个或多个DOM元素。不幸的是,@ Blixt的解决方案仅适用于第一个解决方案。我的解决方案将它们与,
连接起来。
如果您只想处理第一个元素,请执行以下操作:
jQuery('.some-selector').first().getPath();
// or
jQuery('.some-selector:first').getPath();
改进版
jQuery.fn.extend({
getPath: function() {
var pathes = [];
this.each(function(index, element) {
var path, $node = jQuery(element);
while ($node.length) {
var realNode = $node.get(0), name = realNode.localName;
if (!name) { break; }
name = name.toLowerCase();
var parent = $node.parent();
var sameTagSiblings = parent.children(name);
if (sameTagSiblings.length > 1)
{
allSiblings = parent.children();
var index = allSiblings.index(realNode) +1;
if (index > 0) {
name += ':nth-child(' + index + ')';
}
}
path = name + (path ? ' > ' + path : '');
$node = parent;
}
pathes.push(path);
});
return pathes.join(',');
}
});
答案 5 :(得分:2)
如果您正在寻找全面的非jQuery解决方案,那么您应该尝试axe.utils.getSelector。
答案 6 :(得分:1)
跟进亚历克斯所写的内容。 jQuery-GetPath是一个很好的起点,但我对其进行了一些修改,以便合并:eq(),允许我区分多个无id元素。
在getPath返回行之前添加:
if (typeof id == 'undefined' && cur != 'body') {
allSiblings = $(this).parent().children(cur);
var index = allSiblings.index(this);// + 1;
//if (index > 0) {
cur += ':eq(' + index + ')';
//}
}
这将返回类似“html&gt; body&gt; ul#hello&gt; li.5:eq(1)”
的路径答案 7 :(得分:0)
您还可以查看findCssSelector,它在Firefox开发人员工具中用于在页面刷新时保存当前选定的节点。它没有使用jQuery或任何库。
const findCssSelector = function(ele) {
ele = getRootBindingParent(ele);
let document = ele.ownerDocument;
if (!document || !document.contains(ele)) {
throw new Error("findCssSelector received element not inside document");
}
let cssEscape = ele.ownerGlobal.CSS.escape;
// document.querySelectorAll("#id") returns multiple if elements share an ID
if (ele.id &&
document.querySelectorAll("#" + cssEscape(ele.id)).length === 1) {
return "#" + cssEscape(ele.id);
}
// Inherently unique by tag name
let 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
let selector, index, matches;
if (ele.classList.length > 0) {
for (let i = 0; i < ele.classList.length; i++) {
// Is this className unique by itself?
selector = "." + cssEscape(ele.classList.item(i));
matches = document.querySelectorAll(selector);
if (matches.length === 1) {
return selector;
}
// Maybe it's unique with a tag name?
selector = cssEscape(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 = findCssSelector(ele.parentNode) + " > " +
cssEscape(tagName) + ":nth-child(" + index + ")";
}
return selector;
};
答案 8 :(得分:-1)
$.fn.getSelector = function(){
var $ele = $(this);
return '#' + $ele.parents('[id!=""]').first().attr('id')
+ ' .' + $ele.attr('class');
};