疑似コードでは、これが私が欲しいものです。
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ノードオブジェクトで作成された場合は機能しません。
プラグインが存在することを確認しました(同じ名前のプラグインも考えました)が、ここに私が作成した簡単な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;
};
TL; DR-これは見かけよりも複雑な問題であり、 library を使用する必要があります。
この問題は一見すると簡単に見えますが、 プレーンURLをリンクで置き換えるのは簡単ではありません と同じように、見た目よりもトリッキーです。いくつかの考慮事項:
:eq()
を使用すると、ソリューションの有用性が制限されますrequirejQuery問題が思ったほど簡単ではないことのさらなる証拠:CSSセレクターを生成する10個以上のライブラリーがあり、そのうちの1人の作成者が この比較 を公開しています。
jQuery-GetPath が出発点として最適です。次のように、アイテムの祖先を提供します。
var path = $('#foo').getPath();
// e.g., "html > body > div#bar > ul#abc.def.ghi > li#foo"
IEで機能するBlixtの回答のバージョンを次に示します。
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;
};
理解するのが非常にはっきりしているので、自分のバージョンも共有したかっただけです。このスクリプトをすべての一般的なブラウザーでテストしましたが、上司のように機能しています。
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;
}
@Blixtのものと同じソリューションですが、複数のjQuery要素と互換性があります。
jQuery('.some-selector')
は、1つまたは複数の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(',');
}
});
包括的な非jQueryソリューションを探している場合は、 axe.utils.getSelector を試してください。
アレックスが書いたことのフォローアップ。 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> body> ul#hello> li.5:eq(1)」のようなパスを返します
また、ページの更新時に現在選択されているノードを保存するためにFirefox開発者ツールで使用される findCssSelector も確認できます。 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;
};