Obtener selector único de elemento en Jquery


Quiero crear algo como una grabadora que rastrea todas las acciones de un usuario. Para eso, necesito identificar los elementos con los que interactúa el usuario, de modo que pueda hacer referencia a estos elementos en una sesión posterior.

Hablado en pseudo-código, quiero ser capaz de hacer algo como lo siguiente

HTML de muestra (podría ser de cualquier complejidad):

<html>
<body>
  <div class="example">
    <p>foo</p>
    <span><a href="bar">bar</a></span>
  </div>
</body>
</html>

El usuario hace clic en algo, como el enlace. Ahora necesito identificar el elemento pulsado y guardar su ubicación en el árbol DOM para uso posterior:

(any element).onclick(function() {
  uniqueSelector = $(this).getUniqueSelector();
})

Ahora, uniqueSelector debería ser algo como (no me importa si es estilo selector xpath o css):

html > body > div.example > span > a

Esto proporcionaría la posibilidad de guardar esa cadena de selector y usarla en un momento posterior, para reproducir las acciones que realizó el usuario.

¿Cómo es eso posible?

Actualización

Tengo mi respuesta: Obtener un selector de jQuery para un elemento

Author: Community, 2011-04-18

11 answers

Responderé a esto yo mismo, porque encontré una solución que tuve que modificar. El siguiente script está funcionando y está basado en un script de Blixt:

jQuery.fn.extend({
    getPath: function () {
        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 sameTagSiblings = parent.children(name);
            if (sameTagSiblings.length > 1) { 
                var allSiblings = parent.children();
                var index = allSiblings.index(realNode) + 1;
                if (index > 1) {
                    name += ':nth-child(' + index + ')';
                }
            }

            path = name + (path ? '>' + path : '');
            node = parent;
        }

        return path;
    }
});
 32
Author: Alp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-06 01:26:05

La misma solución como la de @Alp pero compatible con varios elementos de jQuery.

jQuery('.some-selector') puede resultar en uno o varios elementos DOM. La solución de @Alp funciona desafortunadamente solo con la primera. Mi solución concatena todos ellos con ,.

Si quieres manejar el primer elemento hazlo así:

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)
                {
                    var 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(',');
    }
});
 15
Author: algorhythm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-06 01:25:37
(any element).onclick(function() {
  uniqueSelector = $(this).getUniqueSelector();
})

this ES el selector único y la ruta a ese elemento en el que se ha hecho clic. ¿Por qué no usar eso? Puede utilizar el método $.data() de jquery para establecer el selector de jquery. Alternativamente, simplemente presione los elementos que necesita usar en el futuro:

var elements = [];
(any element).onclick(function() {
  elements.push(this);
})

Si realmente necesita el xpath, puede calcularlo usando el siguiente código:

  function getXPath(node, path) {
    path = path || [];
    if(node.parentNode) {
      path = getXPath(node.parentNode, path);
    }

    if(node.previousSibling) {
      var count = 1;
      var sibling = node.previousSibling
      do {
        if(sibling.nodeType == 1 && sibling.nodeName == node.nodeName) {count++;}
        sibling = sibling.previousSibling;
      } while(sibling);
      if(count == 1) {count = null;}
    } else if(node.nextSibling) {
      var sibling = node.nextSibling;
      do {
        if(sibling.nodeType == 1 && sibling.nodeName == node.nodeName) {
          var count = 1;
          sibling = null;
        } else {
          var count = null;
          sibling = sibling.previousSibling;
        }
      } while(sibling);
    }

    if(node.nodeType == 1) {
      path.push(node.nodeName.toLowerCase() + (node.id ? "[@id='"+node.id+"']" : count > 0 ? "["+count+"]" : ''));
    }
    return path;
  };

Referencia: http://snippets.dzone.com/posts/show/4349

 4
Author: Gary Green,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-18 18:02:18

Creo que una mejor solución sería generar un id aleatorio y luego acceder a un elemento basado en ese id:

Asignando id único:

// or some other id-generating algorithm
$(this).attr('id', new Date().getTime()); 

Seleccionando basado en el id único:

// getting unique id
var uniqueId = $(this).getUniqueId();

// or you could just get the id:
var uniqueId = $(this).attr('id');

// selecting by id:
var element = $('#' + uniqueId);

// if you decide to use another attribute other than id:
var element = $('[data-unique-id="' + uniqueId + '"]');
 3
Author: Eli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-18 18:10:35

Esta respuesta no satisface la descripción original de la pregunta, sin embargo responde a la pregunta del título. Llegué a esta pregunta buscando una manera de obtener un selector único para un elemento, pero no tenía la necesidad de que el selector sea válido entre cargas de página. Por lo tanto, mi respuesta no funcionará entre cargas de página.

Siento que modificar el DOM no es idel, pero es una buena manera de construir un selector que sea único sin un tun de código. Tengo esta idea después de leer @Eli respuesta:

Asigne un atributo personalizado con un valor único.

$(element).attr('secondary_id', new Date().getTime())
var secondary_id = $(element).attr('secondary_id');

Luego use ese id único para construir un Selector CSS.

var selector = '[secondary_id='+secondary_id+']';

Entonces tienes un selector que seleccionará tu elemento.

var found_again = $(selector);

Y muchos quieren comprobar para asegurarse de que no hay ya un atributo secondary_id en el elemento.

if ($(element).attr('secondary_id')) {
  $(element).attr('secondary_id', (new Date()).getTime());
}
var secondary_id = $(element).attr('secondary_id');

Poniendo todo junto

$.fn.getSelector = function(){
  var e = $(this);

  // the `id` attribute *should* be unique.
  if (e.attr('id')) { return '#'+e.attr('id') }

  if (e.attr('secondary_id')) {
    return '[secondary_id='+e.attr('secondary_id')+']'
  }

  $(element).attr('secondary_id', (new Date()).getTime());

  return '[secondary_id='+e.attr('secondary_id')+']'
};

var selector = $('*').first().getSelector();
 2
Author: Nate,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-20 16:45:15

En caso de que tenga un atributo de identidad (por ejemplo id="algo"), debe obtener el valor de esto como,

var selector = "[id='" + $(yourObject).attr("id") + "']";
console.log(selector); //=> [id='something']
console.log($(selector).length); //=> 1

En caso de que no tenga un atributo de identidad y desee obtener el selector de él, puede crear un atributo de identidad. Algo como lo anterior,

var uuid = guid();
$(yourObject).attr("id", uuid); // Set the uuid as id of your object.

Puedes usar tu propio método guid, o usar el código fuente que se encuentra en this so answer,

function guid() {
    function s4() {
        return Math.floor((1 + Math.random()) * 0x10000)
                .toString(16)
                .substring(1);
    }
    return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
            s4() + '-' + s4() + s4() + s4();
}
 2
Author: George Siggouroglou,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:33:26

También puede echar un vistazo a findCssSelector. El código está en mi otra respuesta.

 1
Author: Ashraf Sabry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-07 08:47:55

Mientras que la pregunta era para jQuery, en ES6, es bastante fácil obtener algo similar a @Alp's para Vanilla JavaScript (también he agregado un par de líneas, rastreando un nameCount, para minimizar el uso de nth-child):

function getSelectorForElement (elem) {
    let path;
    while (elem) {
        let subSelector = elem.localName;
        if (!subSelector) {
            break;
        }
        subSelector = subSelector.toLowerCase();

        const parent = elem.parentElement;

        if (parent) {
            const sameTagSiblings = parent.children;
            if (sameTagSiblings.length > 1) {
                let nameCount = 0;
                const index = [...sameTagSiblings].findIndex((child) => {
                    if (elem.localName === child.localName) {
                        nameCount++;
                    }
                    return child === elem;
                }) + 1;
                if (index > 1 && nameCount > 1) {
                    subSelector += ':nth-child(' + index + ')';
                }
            }
        }

        path = subSelector + (path ? '>' + path : '');
        elem = parent;
    }
    return path;
}
 1
Author: Brett Zamir,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-06 02:06:02
$(document).ready(function() {
    $("*").click(function(e) {
        var path = [];
        $.each($(this).parents(), function(index, value) {
            var id = $(value).attr("id");
            var class = $(value).attr("class");
            var element = $(value).get(0).tagName
                path.push(element + (id.length > 0 ? " #" + id : (class.length > 0 ? " .": "") + class));
        });
        console.log(path.reverse().join(">"));
        return false;
    });
});

Ejemplo de trabajo: http://jsfiddle.net/peeter/YRmr5 /

Probablemente se encontrará con problemas al usar el selector * (muy lento) y detener el evento de burbujeo, pero realmente no puede ayudar sin más código HTML.

 0
Author: Peeter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-18 18:56:38

Podrías hacer algo como esto:

$(".track").click(function() {
  recordEvent($(this).attr("id"));
});

Adjunta un controlador de eventos onclick a cada objeto con la clase track. Cada vez que se hace clic en un objeto, su id se introduce en la función recordEvent(). Puede hacer que esta función registre la hora y el id de cada objeto o lo que desee.

 -1
Author: Nick Brunt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-18 18:02:11

Podrías hacer algo así (no probado)

function GetPathToElement(jElem)
{
   var tmpParent = jElem;
   var result = '';
   while(tmpParent != null)
   {
       var tagName = tmpParent.get().tagName;
       var className = tmpParent.get().className;
       var id = tmpParent.get().id;
       if( id != '') result = '#' + id + result;
       if( className !='') result = '.' + className + result;
       result = '>' + tagName + result;
       tmpParent = tmpParent.parent();
    }
    return result;
}

Esta función guardará la "ruta" al elemento, ahora para encontrar el elemento de nuevo en el futuro va a ser casi imposible de la manera html es porque en esta función no guardar el índice de sibbling de cada elemento,solo guardar el id(s) y las clases.

Así que a menos que todos y cada uno de los elementos de su documento html tengan un ID, este enfoque no funcionará.

 -1
Author: Jean-Philippe Gire,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-18 18:13:21