2010-01-14 20 views
41

W kodzie psuedo, tego właśnie chcę.Uzyskiwanie selektora jQuery dla elementu

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

Powodem jest to, że muszę przekazać to do zewnętrznego środowiska, gdzie ciąg jest moim jedynym sposobem wymiany danych. To zewnętrzne środowisko musi następnie odesłać wynik wraz z elementem do aktualizacji. Dlatego muszę mieć możliwość serializowania unikalnego selektora CSS dla każdego elementu na stronie.

Zauważyłem, że jquery ma metodę selector, ale nie wydaje się działać w tym kontekście. Działa tylko wtedy, gdy obiekt został utworzony za pomocą selektora. Nie działa, jeśli obiekt został utworzony za pomocą obiektu węzła HTML.

+0

Czy selektor * nie * aby użyć składni jQuery (np 'eq()'), lub może być ogólną selektor CSS przewidziane przez [wielu bibliotekach] (http://stackoverflow.com/ pytania/2068272/pobieranie-a-jquery-selector-for-an-element # 32218234)? –

+0

Mały temat, ale znalazłem to pytanie, próbując znaleźć selektor dla wielu elementów na stronie, a nie tylko konkretnego. To właśnie wymyśliłem ten tylko użyty tag i klasy: 'element.parents(). ToArray(). Reverse(). Splice (2) .map (e => e.localName + '.' + E.classList .value.replace (//g, '.')). join ('') ' –

Odpowiedz

49

Teraz widzę, że plugin istniał (o tej samej nazwie myślałem zbyt), ale tutaj jest tylko kilka szybkich skryptów JavaScript, które napisałem. Nie bierze pod uwagę identyfikatorów i klas elementów, tylko struktura (i dodaje :eq(x), gdzie nazwa węzła jest niejednoznaczna).

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

jQuery ma wbudowaną funkcję 'index', która może zająć się częścią pętli. Po prostu powiedz 'var i = siblings.index (node)' i to powinno działać. – Dan

+0

@Dan: Ah, miałam przeczucie, że będzie coś takiego, dzięki =) – Blixt

+0

+ 1 Ładne rozwiązanie. Zrobiłem rozwiązanie działające z wieloma elementami jQuery. Ale bez twoich najnowszych ulepszeń. Może wkrótce to zaktualizuję. Zobacz moją odpowiedź ... – algorhythm

8

jQuery-GetPath jest dobrym punktem wyjścia: będzie ona daje przodków pozycja, podobnie jak to:

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

jQuery-GetPath nie jest na Github, i najwyraźniej nie był utrzymywany od 2011 roku. Istnieje 10 + legit bibliotek, które generują CSS selektorzy, a autor jednego z nich opublikował [to porównanie] (https://github.com/fczbkk/css-selector-generator-benchmark). –

7

Oto wersja odpowiedzi Blixt, że działa w 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; 
}; 
+1

Ten problem może wydawać się prosty, ale w rzeczywistości jest trochę bardziej skomplikowany - generując unikalne selektory CSS, które są w miarę pewne w stosunku do zmian w strukturze strony. Istnieje ponad 10 bibliotek, które generują selektory CSS, a autor jednego z nich opublikował [to porównanie] (https: // github.com/fczbkk/css-selector-generator-benchmark). –

5

Chciałem podzielić się moją wersję zbyt ponieważ jest to bardzo jasne do zrozumienia. Testowałem ten skrypt we wszystkich popularnych przeglądarkach i działa jak boss.

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; 
} 
+0

Selektor, który nie używa identyfikatorów ids, jest [kruchy] (https://github.com/fczbkk/css-selector-generator-benchmark/issues/1). –

3

samo rozwiązanie jak ten jeden z @Blixt ale zgodny z wieloma elementami jQuery.

jQuery('.some-selector') może powodować jeden lub wiele elementów DOM. Rozwiązanie @ Blixt działa niestety tylko z pierwszym. Moje rozwiązanie łączy je wszystkie z ,.

Jeśli chcesz obsługiwać tylko pierwszy element to zrobić tak:

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

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

Ulepszona wersja

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

W nawiązaniu do tego, co napisał Alex. jQuery-GetPath jest świetnym punktem wyjścia, ale trochę go zmodyfikowałem, aby włączyć: eq(), pozwalając mi na rozróżnienie wielu elementów bez id.

Dodaj to przed linią powrotną getPath:

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

powoduje przywrócenie ścieżkę jak "HTML> ciała> ul> # powitania li.5: eq (1)"

+0

Istnieje ponad 10 bibliotek, które generują selektory CSS, a autor jednego z nich opublikował [to porównanie] (https://github.com/fczbkk/css-selector-generator-benchmark). –

+0

@DanDascalescu Porównanie nie było od dawna aktualizowane, niestety. –

-1
$.fn.getSelector = function(){ 
    var $ele = $(this); 
    return '#' + $ele.parents('[id!=""]').first().attr('id') 
       + ' .' + $ele.attr('class'); 
}; 
+2

Jak to pomoże w rozwiązaniu problemu? –

+0

Ups, przepraszam, powinienem wspomnieć, że to działa tylko dla selektorów takich jak "#foo .bar", które są bardzo popularne w świecie, w którym żyję. – pepper69

15

TL; DR - jest to bardziej złożony problem niż się wydaje i powinieneś użyć library.


Ten problem pojawia się proste na pierwszy rzut oka, ale to trudniejsze niż się wydaje, tak jak replacing plain URLs with links is non-trivial. Niektóre uwagi:

Kolejny dowód, że problem nie jest tak proste jak się wydaje: istnieje ponad 10 bibliotek, które generują selektorów CSS i autor jednego z nich opublikował this comparison.

-1

Możesz również rzucić okiem na findCssSelector, który jest używany w narzędziach programistycznych Firefoksa do zapisywania aktualnie wybranego węzła po odświeżeniu strony. Nie używa jQuery ani żadnej biblioteki.

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; 

}; 
Powiązane problemy