web-dev-qa-db-fra.com

Obtenez un sélecteur d'élément unique dans Jquery

Je veux créer quelque chose comme un enregistreur qui suit toutes les actions d'un utilisateur. Pour cela, j'ai besoin d'identifier les éléments avec lesquels l'utilisateur interagit, afin de pouvoir faire référence à ces éléments dans une session ultérieure.

Parlé en pseudo-code, je veux pouvoir faire quelque chose comme ce qui suit

Exemple de code HTML (peut être de toute complexité):

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

L'utilisateur clique sur quelque chose, comme le lien. Maintenant, je dois identifier l'élément cliqué et enregistrer son emplacement dans l'arborescence DOM pour une utilisation ultérieure:

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

Maintenant, uniqueSelector devrait être quelque chose comme (cela ne me dérange pas s'il s'agit d'un style de sélecteur xpath ou css):

html > body > div.example > span > a

Cela offrirait la possibilité d'enregistrer cette chaîne de sélection et de l'utiliser à une date ultérieure, pour rejouer les actions effectuées par l'utilisateur.

Comment est-ce possible?

Mise à jour

J'ai ma réponse: Obtenir un sélecteur jQuery pour un élément

50
Alp

J'y répondrai moi-même, car j'ai trouvé une solution que j'ai dû modifier. Le script suivant fonctionne et est basé sur 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;
    }
});
36
Alp

Même solution que celle de @Alp mais compatible avec plusieurs éléments jQuery.

jQuery('.some-selector') peut entraîner un ou plusieurs éléments DOM. La solution de @ Alp ne fonctionne malheureusement qu'avec la première. Ma solution les concatène tous avec ,.

Si vous voulez simplement gérer le premier élément, faites-le comme ceci:

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

Je pense qu'une meilleure solution serait de générer un identifiant aléatoire puis d'accéder à un élément basé sur cet identifiant:

Attribution d'un identifiant unique:

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

Sélection basée sur l'identifiant unique:

// 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 + '"]');
5
Eli
(any element).onclick(function() {
  uniqueSelector = $(this).getUniqueSelector();
})

this [~ # ~] est [~ # ~] le sélecteur unique et le chemin vers cet élément cliqué. Pourquoi ne pas utiliser ça? Vous pouvez utiliser la méthode $.data() de jquery pour définir le sélecteur jquery. Alternativement, il suffit de pousser les éléments que vous devez utiliser à l'avenir:

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

Si vous avez vraiment besoin du xpath, vous pouvez le calculer en utilisant le code suivant:

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

Référence: http://snippets.dzone.com/posts/show/4349

5
Gary Green

Alors que la question était pour jQuery, dans ES6, il est assez facile d'obtenir quelque chose de similaire à @ Alp's pour Vanilla JavaScript (j'ai également ajouté quelques lignes, en suivant un nameCount, pour minimiser l'utilisation 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;
}
3
Brett Zamir

Solution JavaScript pure

Remarque: Cela utilise Array.from et Array.prototype.filter , qui nécessitent tous deux à polyfiller dans IE11.

function getUniqueSelector(node) {
  let selector = "";
  while (node.parentElement) {
    const siblings = Array.from(node.parentElement.children).filter(
      e => e.tagName === node.tagName
    );
    selector =
      (siblings.indexOf(node)
        ? `${node.tagName}:nth-of-type(${siblings.indexOf(node) + 1})`
        : `${node.tagName}`) + `${selector ? " > " : ""}${selector}`;
    node = node.parentElement;
  }
  return `html > ${selector.toLowerCase()}`;
}

Utilisation

getUniqueSelector(document.getElementsByClassName('SectionFour')[0]);

getUniqueSelector(document.getElementById('content'));
3
Raphael Rafatpanah

Cette réponse ne satisfait pas la description de la question d'origine, mais elle répond à la question du titre. Je suis venu à cette question à la recherche d'un moyen d'obtenir un sélecteur unique pour un élément, mais je n'avais pas besoin que le sélecteur soit valide entre les chargements de page. Donc, ma réponse ne fonctionnera pas entre les chargements de page.

J'ai l'impression que modifier le DOM n'est pas idel, mais c'est un bon moyen de construire un sélecteur unique sans tun de code. J'ai eu cette idée après avoir lu la réponse de @ Eli:

Attribuez un attribut personnalisé avec une valeur unique.

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

Utilisez ensuite cet identifiant unique pour créer un sélecteur CSS.

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

Ensuite, vous avez un sélecteur qui sélectionnera votre élément.

var found_again = $(selector);

Et vous voulez souvent vérifier qu'il n'y a pas déjà de secondary_id attribut sur l'élément.

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

Mettre tous ensemble

$.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
Nate

Si vous avez un attribut d'identité (par exemple id = "quelque chose"), vous devriez en obtenir la valeur comme,

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

Si vous n'avez pas d'attribut d'identité et que vous souhaitez en obtenir le sélecteur, vous pouvez créer un attribut d'identité. Quelque chose comme ci-dessus,

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

Vous pouvez utiliser votre propre méthode guid, ou utiliser le code source trouvé dans this alors répondez,

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

Vous pouvez également consulter findCssSelector . Le code est dans mon autre réponse .

1
Ashraf Sabry

Vous pouvez faire quelque chose comme ça:

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

Il attache un gestionnaire d'événements onclick à chaque objet avec la classe track. Chaque fois qu'un objet est cliqué, son identifiant est introduit dans la fonction recordEvent(). Vous pouvez faire en sorte que cette fonction enregistre l'heure et l'id de chaque objet ou ce que vous voulez.

0
Nick Brunt
$(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;
    });
});

Exemple de travail: http://jsfiddle.net/peeter/YRmr5/

Vous rencontrerez probablement des problèmes lors de l'utilisation du sélecteur * (très lent) et de l'arrêt de la propagation de l'événement, mais ne pouvez pas vraiment y aider sans plus de code HTML.

0
Peeter