2010-01-14 27 views
41

En código psuedo, esto es lo que quiero.Obteniendo un selector jQuery para un elemento

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

La razón es que tengo que pasar esto a un entorno externo, donde una cadena es mi única manera de intercambiar datos. Este entorno externo necesita enviar un resultado, junto con qué elemento actualizar. Así que necesito poder serializar un selector de CSS único para cada elemento en la página.

Me di cuenta de que jquery tiene un método selector, pero no parece funcionar en este contexto. Solo funciona si el objeto se creó con un selector. No funciona si el objeto se creó con un objeto de nodo HTML.

+0

¿El selector * tiene * utilizar la sintaxis de jQuery (por ejemplo 'eq()'), o puede ser un selector CSS general como estipula el [muchas bibliotecas] (http://stackoverflow.com/ preguntas/2068272/getting-a-jquery-selector-for-an-element # 32218234)? –

+0

Un poco fuera de tema, pero encontré esta pregunta tratando de encontrar un selector para muchos elementos en una página, no solo uno en particular. Esto es lo que se me ocurrió que solo usaba etiquetas y clases: 'element.parents(). ToArray(). Reverse(). Splice (2) .map (e => e.localName + '.' + E.classList .value.replace (//g, '.')). join ('') ' –

Respuesta

49

Ahora veo que existía un plugin (con el mismo nombre pensé demasiado), pero aquí está solo un poco de JavaScript rápido que escribí. No toma en consideración los ID o las clases de elementos – solo la estructura (y agrega :eq(x) donde el nombre de un nodo es ambiguo).

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 tiene una función 'index' integrada que puede ocuparse de la parte del bucle. Simplemente diga 'var i = siblings.index (node)' y eso debería funcionar. – Dan

+0

@Dan: Ah, tenía la sensación de que habría algo así, gracias =) – Blixt

+0

+1 Buena solución. He creado una solución que funciona con múltiples elementos jQuery. Pero en realidad sin tus últimas mejoras. Quizás lo actualice pronto. Ver mi respuesta ... – algorhythm

8

jQuery-GetPath es un buen punto de partida: te dará los antepasados ​​del artículo, así:

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

jQuery-GetPath no está en Github, y aparentemente no se ha mantenido desde 2011. Hay más de 10 bibliotecas legítimas que generan CSS selectores, y el autor de uno de ellos ha publicado [esta comparación] (https://github.com/fczbkk/css-selector-generator-benchmark). –

7

Aquí hay una versión de la respuesta de Blixt que funciona en 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

Este problema puede parecer simple, pero en realidad es un poco más complejo, ya que genera selectores de CSS únicos que, en el mejor de los casos, son algo robustos a los cambios en la estructura de la página. Hay más de 10 bibliotecas que generan selectores de CSS, y el autor de una de ellas ha publicado [esta comparación] (https: // github.com/fczbkk/css-selector-generator-benchmark). –

5

Sólo quería compartir mi versión demasiado porque es muy claro de entender. Probé este script en todos los navegadores comunes y funciona como un jefe.

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

Un selector que no usa identificadores es [frágil] (https://github.com/fczbkk/css-selector-generator-benchmark/issues/1). –

3

La misma solución que la de @Blixt pero compatible con múltiples elementos jQuery.

jQuery('.some-selector') puede dar como resultado uno o varios elementos DOM. La solución @ Blixt funciona lamentablemente solo con la primera. Mi solución los concatena todos con ,.

Si sólo desea manejar el primer elemento de hacerlo de esta manera:

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

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

Versión mejorada

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

En seguimiento de lo que escribió Alex. jQuery-GetPath es un gran punto de partida, pero lo he modificado un poco para incorporar: eq(), lo que me permite distinguir entre múltiples elementos sin id.

Añadir esto antes de la línea de retorno getPath:

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

Esto devolverá un camino como "html> body> ul # hola> li.5: eq (1)"

+0

Hay más de 10 bibliotecas que generan selectores de CSS, y el autor de una de ellas ha publicado [esta comparación] (https://github.com/fczbkk/css-selector-generator-benchmark). –

+0

@DanDascalescu La comparación no se ha actualizado en mucho tiempo, desafortunadamente. –

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

¿Cómo va a ayudar esto a resolver el problema? –

+0

Vaya, lo siento, debería haber mencionado que esto solo funciona para selectores como '#foo .bar' que son muy comunes en el mundo en el que vivo. – pepper69

15

TL; DR: este es un problema más complejo de lo que parece y debes usar un library.


Este problema parece fácil a primera vista, pero es más complicado de lo que parece, al igual que replacing plain URLs with links is non-trivial. Algunas consideraciones:

Otra prueba de que el problema no es tan fácil como parece: hay 10 + bibliotecas que generan selectores CSS, y el autor de uno de ellos ha publicado this comparison.

-1

También puede consultar findCssSelector, que se utiliza en las herramientas de desarrollador de Firefox para guardar el nodo seleccionado actualmente al actualizar la página. No usa jQuery ni ninguna biblioteca.

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; 

}; 
Cuestiones relacionadas