En code pseudo, c'est ce que je veux.
var selector = $(this).cssSelectorAsString(); // Made up method...
// selector is now something like: "html>body>ul>li>img[3]"
var element = $(selector);
La raison en est que je dois le transmettre à un environnement externe, où une chaîne est mon seul moyen d'échanger des données. Cet environnement externe doit ensuite renvoyer un résultat, ainsi que l’élément à mettre à jour. Je dois donc pouvoir sérialiser un sélecteur CSS unique pour chaque élément de la page.
J'ai remarqué que jquery a une méthode selector
, mais cela ne semble pas fonctionner dans ce contexte. Cela ne fonctionne que si l'objet a été créé avec un sélecteur. Cela ne fonctionne pas si l'objet a été créé avec un objet de noeud HTML.
Je vois maintenant qu’un plugin existait (avec le même nom que celui auquel je pensais aussi), mais voici juste un peu de JavaScript j’ai écrit. Il ne prend pas en compte les identifiants ou les classes d'éléments - seulement la structure (et ajoute :eq(x)
lorsqu'un nom de nœud est ambigu).
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 - il s’agit d’un problème plus complexe qu’il ne le semble et vous devez utiliser un bibliothèque .
Ce problème semble facile au premier abord, mais il est plus compliqué qu'il n'y paraît, tout comme le remplacement d'URL ordinaires par des liens n'est pas trivial . Quelques considérations:
:eq()
limite l'utilité de la solution, car elle require jQueryPreuve supplémentaire que le problème n’est pas aussi simple qu’il ne le semble: plus de 10 bibliothèques génèrent des sélecteurs CSS, et l’auteur de l’une d’elles a publié cette comparaison .
jQuery-GetPath est un bon point de départ: il vous donnera les ancêtres de l'élément, comme ceci:
var path = $('#foo').getPath();
// e.g., "html > body > div#bar > ul#abc.def.ghi > li#foo"
Voici une version de la réponse de Blixt qui fonctionne dans 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;
};
Je voulais juste partager ma version aussi parce que c'est très clair à comprendre. J'ai testé ce script dans tous les navigateurs courants et il fonctionne comme un patron.
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;
}
Même solution que celle de @Blixt mais compatible avec plusieurs éléments jQuery.
jQuery('.some-selector')
peut générer un ou plusieurs éléments DOM. La solution de @ Blixt ne fonctionne malheureusement qu'avec le premier. Ma solution les concatène tous avec ,
.
Si vous voulez simplement gérer le premier élément, procédez comme suit:
jQuery('.some-selector').first().getPath();
// or
jQuery('.some-selector:first').getPath();
Version améliorée
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(',');
}
});
Si vous cherchez une solution complète et non jQuery, vous devriez essayer axe.utils.getSelector .
Suivre ce que alex a écrit… .. jQuery-GetPath est un bon point de départ, mais je l’ai légèrement modifié pour intégrer eq (), ce qui me permet de distinguer plusieurs éléments sans identifiant.
Ajoutez ceci avant la ligne de retour getPath:
if (typeof id == 'undefined' && cur != 'body') {
allSiblings = $(this).parent().children(cur);
var index = allSiblings.index(this);// + 1;
//if (index > 0) {
cur += ':eq(' + index + ')';
//}
}
Cela retournera un chemin comme "html> body> ul # hello> li.5: eq (1)"
Vous pouvez également consulter findCssSelector , utilisé dans les outils de développement de Firefox pour enregistrer le nœud actuellement sélectionné lors de l'actualisation de la page. Il n'utilise ni jQuery ni aucune bibliothèque.
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;
};