2008-10-22 10 views
25

Estoy escribiendo un script de GreaseMonkey en el que estoy iterando a través de un grupo de elementos. Para cada elemento, necesito una ID de cadena que pueda usar para hacer referencia a ese elemento más adelante. El elemento en sí no tiene un atributo id, y no puedo modificar el documento original para darle uno (aunque puedo hacer cambios DOM en mi script). No puedo almacenar las referencias en mi script porque cuando las necesito, el script de GreaseMonkey se habrá salido del alcance. ¿Hay alguna forma de obtener una ID "interna" que usa el navegador, por ejemplo? Una solución solo para Firefox está bien; una solución de navegador cruzado que podría aplicarse en otros escenarios sería increíble.Id. De elemento único, incluso si el elemento no tiene uno

Editar:

  • Si el script de Greasemonkey está fuera de alcance, ¿cómo estás referenciando los elementos más adelante? El script GreaseMonkey agrega eventos a los objetos DOM. No puedo almacenar las referencias en una matriz u otro mecanismo similar porque cuando el evento se dispara, la matriz se habrá ido porque la secuencia de comandos de GreaseMonkey se habrá salido del alcance. Por lo tanto, el evento necesita alguna forma de conocer la referencia de elemento que tenía el script cuando se adjuntó el evento. Y el elemento en cuestión no es al que está unido.

  • ¿No puede simplemente usar una propiedad personalizada en el elemento? Sí, pero el problema está en la búsqueda. Tendría que recurrir a iterar a través de todos los elementos buscando el que tiene esa propiedad personalizada establecida en el ID deseado. Eso funcionaría, claro, pero en documentos grandes podría llevar mucho tiempo. Estoy buscando algo donde el navegador pueda hacer el trabajo de búsqueda.

  • Espera, ¿puedes o no puedes modificar el documento? No puedo modificar el documento fuente, pero puedo hacer cambios DOM en el script. Voy a aclarar en la pregunta.

  • ¿No puedes usar cierres? Los cierres sí funcionaron, aunque al principio pensé que no. Ver mi publicación posterior.

Parece la respuesta a la pregunta: "¿Hay alguna identificación interna del navegador que pueda usar?" no es."

+2

Pregunta muy interesante. De hecho, me gustaría saber cómo varios navegadores implementan esto. Quizás algunos navegadores asignan un índice oculto a todos los elementos para que puedan acceder a ellos internamente sin la necesidad de recorrer todo el árbol DOM. Tendría sentido. La pregunta es si podemos acceder al índice desde JavaScript. –

+0

@AsGoodAsItGets En realidad, es al revés, ya que esta pregunta se creó primero. :) –

+0

@AsGoodAsItGets: * shrug * Nadie te obligará a hacerlo; hazlo si sientes que deberías hacerlo –

Respuesta

5

ACTUALIZACIÓN: Los cierres son de hecho la respuesta. Entonces, después de jugar un poco más, descubrí por qué los cierres eran inicialmente problemáticos y cómo solucionarlos. Lo complicado con un cierre es que debes tener cuidado al recorrer los elementos para no terminar con todos tus cierres haciendo referencia al mismo elemento. Por ejemplo, esto no funciona:

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

Pero esto hace:

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

todos modos, no decidido seleccionar una respuesta porque la pregunta tenía dos respuestas: 1) No, no hay ID internas que puedes usar; 2) deberías usar cierres para esto. Así que simplemente voté por encima de las primeras personas para decir si había identificaciones internas o quién recomendaba la generación de identificaciones, más cualquiera que mencionara los cierres. ¡Gracias por la ayuda!

+1

El problema de la función dentro de un lazo se describe bien aquí: http://jslinterrors.com/dont-make-functions-within-a-loop. Gracias por el aviso; eso hubiera consumido algunas de mis horas fácilmente. – Seth

0

en JavaScript, podría adjuntar un campo ID personalizado para el nodo

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

// store myId 

Es un poco de corte, sino que va a dar a cada nodo un identificador que puede utilizar. Por supuesto, document.getElementById() no le prestará atención.

+0

Sí, pero el problema está en la búsqueda final más tarde. La única forma en que tendría que encontrar ese elemento de nuevo es iterar todos los elementos en el documento y comparar esa propiedad personalizada. Dependiendo del tamaño del documento, eso podría ser realmente lento. –

2

Puede establecer el atributo de ID en un valor calculado. Hay una función en la biblioteca de prototipos que puede hacer esto por usted.

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

Mi favorito es librería javascript jQuery. Desafortunadamente jQuery no tiene una función como identificar. Sin embargo, aún puede establecer el atributo id en un valor que genere usted mismo.

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

Aquí hay un fragmento parcial de la documentación de jQuery que establece Identificación de divs en base a la posición en la página:

$(document).ready(function(){ 

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

Si usted no está modificando el DOM se puede llegar a todos ellos por orden indizado:

(Prototype ejemplo)

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

Podría recorrer todos los nodos y darles un nombre de clase único que luego podría seleccionar fácilmente.

4

La respuesta es no, no hay una identificación interna a la que pueda acceder. Opera e IE (¿quizás Safari?) Admiten .sourceIndex (que cambia si DOM lo hace) pero Firefox no tiene nada de esto.

Puede simular el índice de origen generando Xpath en un nodo determinado o encontrando el índice del nodo en document.getElementsByTagName('*'), que siempre devolverá los elementos en orden de origen.

Todo esto requiere un archivo completamente estático por supuesto. Los cambios en DOM romperán la búsqueda.

Lo que no entiendo es cómo puede perder referencias a los nodos pero no a las identificaciones internas (teóricas)? O cierres y asignaciones funcionan o no. ¿O me estoy perdiendo algo?

7

Un poco confundido por la redacción de su pregunta: dice que "necesita una ID de cadena que [puede usar] para hacer referencia a ese elemento más adelante", pero que "no puede almacenar las referencias en [su] script porque cuando [los necesita], el script GreaseMonkey se habrá salido del alcance ".

Si la secuencia de comandos se habrá salido del alcance, ¿cómo los hace referencia más adelante?

voy a pasar por alto el hecho de que estoy confundido por lo que está recibiendo en y le dirá que escribo scripts de Greasemonkey con bastante frecuencia y puedo modificar los elementos DOM que el acceso a darles una propiedad ID. Este es el código que puede utilizar para obtener un valor de pseudo-único para uso temporal:

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; 

que funciona para mí, sólo se probó en un script de Greasemonkey mí mismo.


ACTUALIZACIÓN: Los cierres son el camino a seguir - personalmente, como un núcleo duro de JavaScript desarrollador, no sé cómo se hizo no pensar en los inmediatamente. :)

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

Ahora, myDomElement es uno de los elementos que, aparentemente, a partir de su descripción, ya alrededor (ya estabas pensando en añadir un ID a la misma, o lo que sea).

Tal vez si publica un ejemplo de lo que está tratando de hacer, sería más fácil ayudarle, suponiendo que esto no ocurra.

+1

Las grandes mentes piensan igual. Nitpick: los identificadores DOM deben comenzar con un carácter A-Z, por lo que esto rompe el HTML (aunque funciona). :-) – Borgar

+0

Es cierto: tiendo a olvidar algunos de los detalles de las especificaciones, ya que la mayoría de los navegadores son tan convenientemente indulgentes. :) He utilizado este patrón durante bastante tiempo en mi propio código JavaScript sin problemas, pero ahora que lo mencionas, creo que haré cambios para garantizar la validez ... tal vez. : P –

+0

Sí, normalmente usaría cierres en un escenario como este. No estoy seguro de por qué no pensé en ellos en este caso. Lo atribuyo a demasiadas noches trabajando hasta tarde. :) –

4

Si puede escriba en el DOM (estoy seguro de que puede hacerlo).Me gustaría resolver este como esto:

tener un retorno de función o generar un ID:

//(function() { 

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

//})(); 

Uso esto para conseguir identificaciones según sea necesario:

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

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

De esta manera no es necesario Preocúpese por el aspecto del HTML cuando el servidor se lo entregue.

6

El cierre es el camino a seguir. De esta forma tendrá una referencia exacta del elemento que incluso sobrevivirá a algunos cambios de DOM.

ejemplo para aquellos que no conocen cierres:

var saved_element = findThatDOMNode(); 

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

Si usted tuvo que almacenarla en una cookie, entonces te recomiendo que XPath para computar (por ejemplo, caminar hasta el DOM contando los hermanos anteriores hasta encuentras un elemento con una ID y terminarás con algo como [@id=foo]/div[4]/p[2]/a).

XPointer es la solución de W3C a ese problema.

1

También puede utilizar pguid (identificador de página-único) para la generación de identificador único:

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

Uso del ratón y/o las propiedades de posición del elemento para generar un identificador único.

+1

Esto no es una prueba completa si los elementos se superponen por completo –

0

Creo que acabo de resolver un problema similar. Sin embargo, estoy usando jQuery en un entorno DOM del navegador.

var objA = $ ("selector a algún elemento dom"); var objB = $ ("selector a algún otro elemento dom");

if (objA [0] === objB [0]) { // ¡GRANDE! los dos objetos apuntan exactamente al mismo nodo dom }

0

OK, no hay un ID asociado al elemento DOM automáticamente. DOM tiene una estructura jerárquica de elementos que es la información principal. Desde esta perspectiva, puede asociar datos a los elementos DOM con jQuery o jQLite. Puede resolver algunos problemas cuando tiene que vincular datos personalizados a elementos.

0

Se puede generar un identificador estable, único para cualquier nodo dado en un DOM con la siguiente función:

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

Esto creará identificadores tales como doc/0, doc/0/0, doc/0/1, doc/0/1/0, doc/0/1/1 para una estructura como éste:

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

Hay también algunas optimizaciones y cambios que puede hacer, por ejemplo:

  • En el bucle while, break cuando ese node tiene un atributo que sabes que es único, por ejemplo @id

  • No reverse() la pieces, en la actualidad es sólo allí para parecerse más a la estructura del DOM el de ID se generan a partir de

  • no incluye la primera piecedoc si no necesita un identificador para el nodo de documento

  • Guarde el identificador en el nodo de alguna manera y reutilice ese valor para los nodos secundarios para evitar tener que recorrer todo el camino nuevamente en el árbol.

  • Si está escribiendo estos identificadores en XML, use otro carácter de concatenación si el atributo que está escribiendo está restringido.

Cuestiones relacionadas