Obtener el índice del elemento como hijo en relación con el padre

160

Digamos que tengo este marcado:

<ul id="wizard">
    <li>Step 1</li>
    <li>Step 2</li>
</ul>

Y tengo este jQuery:

$("#wizard li").click(function () {
    // alert index of li relative to ul parent
});

¿Cómo puedo obtener el índice del niño en lirelación con su padre, al hacer clic en eso li?

Por ejemplo, cuando hace clic en "Paso 1", alertdebería aparecer un con "0".

AgileMeansDoAsLittleAsPossible
fuente

Respuestas:

247
$("#wizard li").click(function () {
    console.log( $(this).index() );
});

Sin embargo, en lugar de adjuntar un controlador de clic para cada elemento de la lista, es mejor (en cuanto al rendimiento) usar el delegateque se vería así:

$("#wizard").delegate('li', 'click', function () {
    console.log( $(this).index() );
});

En jQuery 1.7+, debe usar on. El siguiente ejemplo vincula el evento al #wizardelemento, funcionando como un evento delegado:

$("#wizard").on("click", "li", function() {
    console.log( $(this).index() );
});
cuadrado rojo
fuente
3
hola redsquare .. Estoy practicando con jQuery, estoy un poco n00b acerca de delegado: D .. ¿por qué delegar es mejor que adjuntar eventos directamente en los elementos?
stecb
1
@steweb - Hola, porque un controlador de eventos es mucho mejor que potencialmente muchos. Adjuntar eventos al dom puede ser costoso si tiene listas grandes, así que, por regla general, trato de usar lo anterior siempre que sea posible en lugar de controladores individuales en cada elemento. Un artículo que profundiza es briancrescimanno.com/2008/05/19/… - también 'delegación de eventos javascript' de google
redsquare
ok, entonces, gestionar eventos de esta manera es algo más ligero que el clásico 'archivo adjunto dom' :) ¡Gracias!
stecb
@steweb: también es más ligero porque jquery no necesita hacer una búsqueda inicial de todos los elementos li. Simplemente busca el contenedor y escucha a ese nivel para ver si el objeto cliqueado era un li.
Redsquare
Este método .index es muy útil para mí. Estoy usando jQuery Sortable para ordenar, pero almacena la información de clasificación semántica en el DOM. Con .index (), puedo sacar la clasificación del dom de manera muy limpia. ¡Gracias!
SimplGy
41

algo como:

$("ul#wizard li").click(function () {
  var index = $("ul#wizard li").index(this);
  alert("index is: " + index)
});
Mezcla
fuente
9
Gran adición: podemos encontrar el índice relativamente al selector principal. +1
BasTaller
1
prefiero esta respuesta, ya que en realidad responde a la pregunta en lugar de una posible solución para el ejemplo
DarkMukke
8

Echa un vistazo a este ejemplo .

$("#wizard li").click(function () {
    alert($(this).index()); // alert index of li relative to ul parent
});
Kimtho6
fuente
4

No es necesario requerir una gran biblioteca como jQuery para lograr esto, si no lo desea. Para lograr esto con la manipulación DOM incorporada, obtenga una colección de los lihermanos en una matriz y, al hacer clic, verifique el indexOfelemento cliqueado en esa matriz.

const lis = [...document.querySelectorAll('#wizard > li')];
lis.forEach((li) => {
  li.addEventListener('click', () => {
    const index = lis.indexOf(li);
    console.log(index);
  });
});
<ul id="wizard">
    <li>Step 1</li>
    <li>Step 2</li>
</ul>

O, con la delegación del evento:

const lis = [...document.querySelectorAll('#wizard li')];
document.querySelector('#wizard').addEventListener('click', ({ target }) => {
  // Make sure the clicked element is a <li> which is a child of wizard:
  if (!target.matches('#wizard > li')) return;
  
  const index = lis.indexOf(target);
  console.log(index);
});
<ul id="wizard">
    <li>Step 1</li>
    <li>Step 2</li>
</ul>

O, si los elementos secundarios pueden cambiar dinámicamente (como con una lista de tareas pendientes), entonces tendrá que construir la matriz de lis con cada clic, en lugar de antes:

const wizard = document.querySelector('#wizard');
wizard.addEventListener('click', ({ target }) => {
  // Make sure the clicked element is a <li>
  if (!target.matches('li')) return;
  
  const lis = [...wizard.children];
  const index = lis.indexOf(target);
  console.log(index);
});
<ul id="wizard">
    <li>Step 1</li>
    <li>Step 2</li>
</ul>

Cierto rendimiento
fuente
$ (...). indexOf no es una función
Kareem
2
Ninguno de los fragmentos de código en mi respuesta produce ese error: puede presionar "Ejecutar fragmento de código" para verlos funcionar. Si obtiene ese error, está usando un código diferente; parece que está usando jQuery, pero mi respuesta muestra cómo lograr este tipo de cosas sin jQuery
CertainPerformance
Lo suficientemente justo. Sí, estoy usando JQuery. Gracias
Kareem
3

Delegate y Live son fáciles de usar, pero si no tiene más li: s agregados dinámicamente, también puede usar la demora de eventos con enlace / clic normal. Debería haber alguna ganancia de rendimiento con este método ya que el DOM no tendrá que ser monitoreado para detectar nuevos elementos coincidentes. No tengo ningún número real pero tiene sentido :)

$("#wizard").click(function (e) {
    var source = $(e.target);
    if(source.is("li")){
        // alert index of li relative to ul parent
        alert(source.index());
    }
});

Puede probarlo en jsFiddle: http://jsfiddle.net/jimmysv/4Sfdh/1/

jimmystormig
fuente
1
¿A qué se refiere cuando dice que 'DOM no tendrá que ser monitoreado'? El delegado jq no monitorea el dom.
Redsquare
@redsquare Tenía la impresión de que Delegate es solo una variante más eficiente de Live. Esto es de api.jquery.com/delegate : "Adjunte un controlador a uno o más eventos para todos los elementos que coincidan con el selector, ahora o en el futuro, en función de un conjunto específico de elementos raíz". ¿Debe estar monitoreando para unir en el futuro?
jimmystormig
No, solo usa la delegación en el contenedor como el código anterior. Si inserta un nuevo elemento li en el contenedor, el evento click del contenedor todavía se activa y todo sigue funcionando. No se requiere monitoreo.
Redsquare
@redsquare Sí, tienes razón. Mi solución funciona incluso si hay nuevos elementos agregados después de que se haya cargado el DOM. Dado que Delegate usa Live internamente (consulte stackoverflow.com/questions/2954932/… ), todavía parece que esto está más optimizado pero es menos conveniente. Pero como dije, no tengo números.
jimmystormig