web-dev-qa-db-fra.com

ID d'élément unique, même si l'élément n'en a pas

J'écris un script GreaseMonkey dans lequel je parcours plusieurs éléments. Pour chaque élément, j'ai besoin d'un ID de chaîne que je peux utiliser pour référencer cet élément ultérieurement. L'élément lui-même n'a pas d'attribut id et je ne peux pas modifier le document d'origine pour lui en donner un (bien que je puisse apporter des modifications DOM dans mon script). Je ne peux pas stocker les références dans mon script car, lorsque j'en aurai besoin, le script GreaseMonkey lui-même sera hors de portée. Existe-t-il un moyen d’obtenir un identifiant "interne" utilisé par le navigateur, par exemple? Une solution réservée à Firefox suffit. une solution multi-navigateur qui pourrait être appliquée dans d'autres scénarios serait génial.

Modifier:

  • Si le script GreaseMonkey est hors de portée, comment allez-vous référencer les éléments plus tard? Ils Le script GreaseMonkey ajoute des événements aux objets DOM. Je ne peux pas stocker les références dans un tableau ni dans un autre mécanisme similaire car, lorsque l'événement se déclenche, le tableau disparaîtra car le script GreaseMonkey sera hors de portée. L'événement a donc besoin d'un moyen de connaître la référence à l'élément que le script avait quand l'attachement à l'événement. Et l'élément en question n'est pas celui auquel il est attaché.

  • Ne pouvez-vous pas simplement utiliser une propriété personnalisée sur l'élément? Oui, mais le problème est à la recherche. Je devrais recourir à tous les éléments à la recherche de celui qui a cette propriété personnalisée définie à l'identifiant souhaité. Cela fonctionnerait, bien sûr, mais dans les gros documents, cela prendrait beaucoup de temps. Je cherche quelque chose où le navigateur peut faire le travail de recherche grunt.

  • Attendez, pouvez-vous ou ne pouvez-vous pas modifier le document? Je ne peux pas modifier le document source, mais je peux apporter des modifications DOM dans le script. Je vais clarifier la question.

  • Ne pouvez-vous pas utiliser des fermetures? Les Closuses se sont avérés efficaces, même si au départ je pensais que non. Voir mon post plus tard.

Cela ressemble à la réponse à la question: "Existe-t-il un identifiant de navigateur interne que je pourrais utiliser?" est "non"

29
Robert J. Walker

MISE À JOUR: Les fermetures sont la solution. Ainsi, après avoir joué un peu plus avec cela, j'ai compris pourquoi les fermetures étaient initialement problématiques et comment y remédier. La difficulté avec une fermeture est que vous devez faire attention lorsque vous parcourez les éléments pour ne pas vous retrouver avec toutes vos fermetures référençant le même élément. Par exemple, cela ne fonctionne pas:

for (var i = 0; i < elements.length; i++) {
    var element = elements[i];
    var button = document.createElement("button");
    button.addEventListener("click", function(ev) {
        // do something with element here
    }, false)
}

Mais cela fait:

var buildListener = function(element) {
    return function(ev) {
        // do something with event here
    };
};

for (var i = 0; i < elements.length; i++) {
    var element = elements[i];
    var button = document.createElement("button");
    button.addEventListener("click", buildListener(element), false)
}

Quoi qu'il en soit, j'ai décidé de ne pas sélectionner une réponse car la question comportait deux réponses: 1) Non, vous ne pouvez utiliser aucun identifiant interne. 2) vous devriez utiliser des fermetures pour cela. J'ai donc simplement invité les premières personnes à dire s'il existait des identifiants internes ou ceux qui recommandaient de générer des identifiants, ainsi que toute personne ayant mentionné des fermetures. Merci pour l'aide!

5
Robert J. Walker

La réponse est non, il n'y a pas d'identifiant interne auquel vous pouvez accéder. Opera et IE (peut-être Safari?) Supportent-ils .sourceIndex (ce qui change si DOM le fait) mais Firefox n'a rien de ce genre. 

Vous pouvez simuler source-index en générant Xpath sur un nœud donné ou en recherchant l'index du nœud à partir de document.getElementsByTagName('*'), qui renverra toujours les éléments dans l'ordre source.

Tout cela nécessite bien sûr un fichier complètement statique. Les modifications apportées à DOM interrompront la recherche.

Ce que je ne comprends pas, c'est comment vous pouvez perdre des références aux nœuds mais pas aux identifiants internes (théoriques)? Les fermetures et les affectations fonctionnent ou ne fonctionnent pas. Ou est-ce que je manque quelque chose?

7
Borgar

Un peu gêné par le libellé de votre question - vous dites que vous "avez besoin d’un identifiant de chaîne que [vous] pourrez utiliser pour référencer cet élément plus tard", mais que vous "ne pouvez pas stocker les références dans [votre] script car parce que [ vous avez besoin d’eux, le script GreaseMonkey lui-même sera hors de portée. "

Si le script est hors de portée, comment allez-vous le référencer plus tard?!

Je vais ignorer le fait que je suis confus par ce que vous voulez en venir et vous dire que j’écris assez souvent des scripts Greasemonkey et can modifie les éléments DOM auxquels j’ai accès pour leur attribuer une propriété ID. C'est le code que vous pouvez utiliser pour obtenir une valeur pseudo-unique à utiliser temporairement:

var PseudoGuid = new (function() {
    this.empty = "00000000-0000-0000-0000-000000000000";
    this.GetNew = function() {
        var fourChars = function() {
            return (((1 + Math.random()) * 0x10000)|0).toString(16).substring(1).toUpperCase();
        }
        return (fourChars() + fourChars() + "-" + fourChars() + "-" + fourChars() + "-" + fourChars() + "-" + fourChars() + fourChars() + fourChars());
    };
})();

// usage example:
var tempId = PseudoGuid.GetNew();
someDomElement.id = tempId;

Cela fonctionne pour moi, je viens de le tester moi-même dans un script Greasemonkey.


MISE À JOUR: Les fermetures sont la voie à suivre - personnellement, en tant que développeur JavaScript fondamental, je ne sais pas comment vous n'avez pas pensez immédiatement à celles-ci. :) 

myDomElement; // some DOM element we want later reference to

someOtherDomElement.addEventListener("click", function(e) {
   // because of the closure, here we have a reference to myDomElement
   doSomething(myDomElement);
}, false);

Maintenant, myDomElement est l’un des éléments que vous avez apparemment, d’après votre description, déjà autour (puisque vous envisagiez d’y ajouter un identifiant, ou peu importe).

Peut-être que si vous publiez un exemple de ce que vous essayez de faire, il serait plus facile de vous aider, en supposant que ce ne soit pas le cas.

6
Jason Bunting

La fermeture est la voie à suivre. De cette façon, vous aurez une référence exacte à l'élément qui survivra même à un remaniement de DOM.

Exemple pour ceux qui ne connaissent pas les fermetures:

var saved_element = findThatDOMNode();

document.body.onclick = function() 
{
   alert(saved_element); // it's still there!
}

Si vous deviez le stocker dans un cookie, je vous recommande de l'installer (par exemple, remontez le DOM en comptant les frères et sœurs précédents jusqu'à ce que vous trouviez un élément avec un identifiant et que vous obtiendrez quelque chose comme [@id=foo]/div[4]/p[2]/a) . 

XPointer est la solution du W3C à ce problème.

6
Kornel

Si vous pouvez écrivez dans le DOM (je suis sûr que vous le pouvez). Je voudrais résoudre ceci comme ceci:

Demandez à une fonction de retourner ou de générer un identifiant:

//(function () {

  var idCounter = new Date().getTime();
  function getId( node ) {
    return (node.id) ? node.id : (node.id = 'tempIdPrefix_' + idCounter++ );
  }

//})();

Utilisez ceci pour obtenir les identifiants nécessaires:

var n = document.getElementById('someid');
getId(n);  // returns "someid"

var n = document.getElementsByTagName('div')[1];
getId(n);  // returns "tempIdPrefix_1224697942198"

De cette façon, vous n'avez pas à vous soucier de l'apparence du code HTML lorsque le serveur vous le transmet.

4
Borgar

Si vous ne modifiez pas le DOM, vous pouvez tous les obtenir par ordre indexé:

( Prototype exemple)

myNodes = document.body.descendants()
alert(document.body.descendants()[1].innerHTML)

Vous pouvez parcourir tous les nœuds et leur attribuer un nom de classe unique que vous pourrez ensuite sélectionner facilement.

Vous pouvez définir l'attribut id sur une valeur calculée. Il existe une fonction dans la bibliothèque de prototypes qui peut le faire pour vous.

http://www.prototypejs.org/api/element/identify

Ma bibliothèque javascript préférée est jQuery. Malheureusement, jQuery n’a pas une fonction comme identifier. Cependant, vous pouvez toujours définir l'attribut id sur une valeur que vous générez vous-même.

http://docs.jquery.com/Attributes/attr#keyfn

Voici un extrait partiel de la documentation jQuery qui définit l'identifiant des div en fonction de la position dans la page:

  $(document).ready(function(){

    $("div").attr("id", function (arr) {
          return "div-id" + arr;
        });
  });
2
Michael

Vous pouvez générer un identifiant unique et stable pour tout nœud donné dans un DOM avec la fonction suivante:

function getUniqueKeyForNode (targetNode) {
    const pieces = ['doc'];
    let node = targetNode;

    while (node && node.parentNode) {
        pieces.Push(Array.prototype.indexOf.call(node.parentNode.childNodes, node));
        node = node.parentNode
    }

    return pieces.reverse().join('/');
}

Cela créera des identifiants tels que doc/0, doc/0/0, doc/0/1, doc/0/1/0, doc/0/1/1 pour une structure comme celle-ci:

<div>
    <div />
    <div>
        <div />
        <div />
    </div>
</div>

Vous pouvez également apporter quelques optimisations et modifications, par exemple:

  • Dans la boucle while, break quand cette node a un attribut que vous savez être unique, par exemple @id

  • Non reverse() la pieces, actuellement, il est simplement là pour ressembler davantage à la structure DOM à partir de laquelle les ID sont générés.

  • N'incluez pas la première piecedoc si vous n'avez pas besoin d'identifiant pour le noeud du document.

  • Enregistrez l'identifiant sur le nœud d'une manière ou d'une autre et réutilisez cette valeur pour les nœuds enfants afin d'éviter de devoir parcourir à nouveau toute la hauteur de l'arborescence.

  • Si vous écrivez ces identifiants en XML, utilisez un autre caractère de concaténation si l'attribut que vous écrivez est restreint.

1
wybe

Vous pouvez également utiliser pguid (identifiant unique de page) pour la génération d'identifiant unique:

 pguid = b9j.pguid.next() // A unique id (suitable for a DOM element)
                          // is generated
                          // Something like "b9j-pguid-20a9ff-0"
 ...
 pguid = b9j.pguid.next() // Another unique one... "b9j-pguid-20a9ff-1"

 // Build a custom generator
 var sequence = new b9j.pguid.Sequence({ namespace: "frobozz" })
 pguid = sequence.next() "frobozz-c861e1-0"

http://appengine.bravo9.com/b9j/documentation/pguid.html

1
Robert Krimen

Utilisez les propriétés de souris et/ou de position de l'élément pour générer un identifiant unique.

1
Paul Tierney

Je pense que je viens de résoudre un problème similaire à celui-ci. Cependant, j'utilise jQuery dans un environnement DOM de navigateur.

var objA = $ ("sélecteur à un élément de dom"); var objB = $ ("sélecteur à un autre élément de dom");

if (objA [0] === objB [0]) { // GREAT! les deux objets pointent exactement sur le même nœud dom }

0
Tom Carnell

En javascript, vous pouvez associer un champ d'identifiant personnalisé au nœud.

if(node.id) {
  node.myId = node.id;
} else {
  node.myId = createId();
}

// store myId

C'est un peu de bidouillage, mais cela donnera à chaque nœud un identifiant que vous pouvez utiliser. Bien entendu, document.getElementById() n'y fera pas attention.

0
sblundy

OK, il n'y a pas d'identifiant associé automatiquement à l'élément DOM. DOM a une structure hiérarchique d'éléments qui constitue l'information principale. Dans cette perspective, vous pouvez associer des données à des éléments DOM avec jQuery ou jQLite. Cela peut résoudre certains problèmes lorsque vous devez lier des données personnalisées à des éléments.

0
kisp