it-swarm.com.de

JQuery-Selektor für ein Element abrufen

Im Pseudo-Code möchte ich dies.

var selector = $(this).cssSelectorAsString(); // Made up method...
// selector is now something like: "html>body>ul>li>img[3]"
var element = $(selector);

Der Grund ist, dass ich dies an eine externe Umgebung übergeben muss, in der ein String die einzige Möglichkeit ist, Daten auszutauschen. Diese externe Umgebung muss dann ein Ergebnis zusammen mit dem zu aktualisierenden Element zurücksenden. Daher muss ich in der Lage sein, einen eindeutigen CSS-Selektor für jedes Element auf der Seite zu serialisieren.

Ich habe bemerkt, dass jquery eine selector-Methode hat, aber es scheint nicht in diesem Zusammenhang zu funktionieren. Es funktioniert nur, wenn das Objekt mit einem Selektor erstellt wurde. Es funktioniert nicht, wenn das Objekt mit einem HTML-Knotenobjekt erstellt wurde.

42
Alex Wayne

Ich sehe jetzt, dass es ein Plugin gab (mit demselben Namen, an das ich auch gedacht hatte), aber hier ist nur ein paar schnelle JavaScript-Dateien, die ich geschrieben habe. Es berücksichtigt nicht die IDs oder Klassen von Elementen - nur die Struktur (und fügt :eq(x) hinzu, wenn ein Knotenname mehrdeutig ist).

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 - Dies ist ein komplexeres Problem als es scheint und Sie sollten eine library verwenden.


Dieses Problem scheint auf den ersten Blick einfach zu sein, aber es ist schwieriger als es scheint, genauso wie einfache URLs durch Links zu ersetzen, ist nicht trivial . Einige Überlegungen:

Ein weiterer Beweis dafür, dass das Problem nicht so einfach ist, wie es scheint: Es gibt mehr als 10 Bibliotheken, die CSS-Selektoren generieren, und der Autor einer von ihnen hat diesen Vergleich veröffentlicht.

17
Dan Dascalescu

jQuery-GetPath ist ein guter Ausgangspunkt: Sie erhalten die Vorfahren des Elements wie folgt:

var path = $('#foo').getPath();
// e.g., "html > body > div#bar > ul#abc.def.ghi > li#foo"
8
Agos

Hier ist eine Version von Blixts Antwort, die im IE funktioniert:

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

Ich wollte nur meine Version teilen, weil sie sehr klar zu verstehen ist. Ich habe dieses Skript in allen gängigen Browsern getestet und funktioniert wie ein Chef.

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

Dieselbe Lösung wie die von @ Blixt, jedoch kompatibel mit mehreren jQuery-Elementen.

jQuery('.some-selector') kann zu einem oder mehreren DOM-Elementen führen. Die Lösung von @ Blixt funktioniert leider nur mit der ersten. Meine Lösung verkettet sie alle mit ,.

Wenn Sie nur mit dem ersten Element umgehen möchten, machen Sie es so:

jQuery('.some-selector').first().getPath();

// or
jQuery('.some-selector:first').getPath();

Verbesserte Version

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

Wenn Sie nach einer umfassenden, nicht jQuery-Lösung suchen, sollten Sie axe.utils.getSelector versuchen.

2
Konrad Dzwinel

Nachzufassen, was alex geschrieben hat.

Fügen Sie dies vor der getPath-Returnzeile ein

if (typeof id == 'undefined' && cur != 'body') {
    allSiblings = $(this).parent().children(cur);
    var index = allSiblings.index(this);// + 1;
    //if (index > 0) {
        cur += ':eq(' + index + ')';
    //}
}

Dies gibt einen Pfad wie "html> body> ul # hello> li.5: eq (1)" zurück.

1
Develop Ideas

Sie können auch einen Blick auf findCssSelector werfen, der in Firefox-Entwicklertools zum Speichern des aktuell ausgewählten Knotens bei Seitenaktualisierungen verwendet wird. Es verwendet weder jQuery noch irgendeine Bibliothek.

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