web-dev-qa-db-fra.com

Obtenir un sélecteur jQuery pour un élément

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.

42
Alex Wayne

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;
};
50
Blixt

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:

Preuve 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 .

17
Dan Dascalescu

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"
8
Agos

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;
};
7
crizCraig

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;
}
5
Ahmet Can Güven

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(',');
    }
});
3
algorhythm

Si vous cherchez une solution complète et non jQuery, vous devriez essayer axe.utils.getSelector .

2
Konrad Dzwinel

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)"

1
Develop Ideas

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;

};
0
Ashraf Sabry