Obter índice do elemento como filho em relação ao pai

160

Digamos que eu tenho essa marcação:

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

E eu tenho esse jQuery:

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

Como posso obter o índice do filho em lirelação ao pai, ao clicar nele li?

Por exemplo, quando você clica em "Etapa 1", um alertcom "0" deve aparecer.

AgileMeansDoAsLittleAsPossible
fonte

Respostas:

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

No entanto, em vez de anexar um manipulador de clique para cada item da lista, é melhor (em termos de desempenho) usar o delegateseguinte:

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

No jQuery 1.7+, você deve usar on. O exemplo abaixo vincula o evento ao #wizardelemento, trabalhando como um evento delegado:

$("#wizard").on("click", "li", function() {
    console.log( $(this).index() );
});
quadrado vermelho
fonte
3
oi redsquare .. Estou praticando com jQuery, sou meio n00b sobre delegate: D .. por que delegar é melhor do que anexar eventos diretamente nos elementos?
stecb
1
@steweb - Hey - porque um manipulador de eventos é muito preferível a potencialmente muitos. Anexar eventos ao dom pode ser caro se você tiver listas grandes, de modo que, em regra, tento usar o acima, sempre que possível, em vez de manipuladores individuais em cada elemento. Um artigo que é mais profundo é briancrescimanno.com/2008/05/19/… - também no google 'javascript event delegation'
redsquare
ok, então, gerenciar eventos dessa maneira é algo mais leve que o clássico 'acessório doméstico' :) .. obrigado!
stecb
@steweb - Também é mais leve porque o jquery não precisa fazer uma pesquisa inicial de todos os elementos li. Ele apenas pesquisa o contêiner e escuta nesse nível para ver se o objeto clicado era um li.
redsquare
Esse método .index é super útil para mim. Estou usando o jQuery Sortable para fazer a classificação, mas ele armazena as informações de classificação semântica no DOM. Com .index (), posso puxar a classificação de volta para fora do domínio realmente muito limpa. Obrigado!
precisa saber é o seguinte
41

algo como:

$("ul#wizard li").click(function () {
  var index = $("ul#wizard li").index(this);
  alert("index is: " + index)
});
Mash
fonte
9
Ótima adição - podemos encontrar um índice relativamente ao seletor principal. +1
BasTaller 13/02/2012
1
preferem esta resposta como ela realmente responde à pergunta em vez de uma solução possível para o exemplo
DarkMukke
8

Dê uma olhada neste exemplo .

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

Não é necessário exigir uma grande biblioteca como o jQuery para fazer isso, se você não quiser. Para conseguir isso com a manipulação interna do DOM, obtenha uma coleção dos liirmãos em uma matriz e, ao clicar, verifique o indexOfelemento clicado nessa 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>

Ou, com delegação de eventos:

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>

Ou, se os elementos filhos puderem mudar dinamicamente (como em uma lista de tarefas), você precisará construir a matriz de lis a cada clique, e não 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>

CertainPerformance
fonte
$ (...). indexOf não é uma função
Kareem
2
Nenhum dos trechos de código na minha resposta produz esse erro - você pode pressionar "Executar trecho de código" para vê-los funcionando. Se você está recebendo esse erro, está usando código diferente - parece que você está usando jQuery, mas minha resposta está mostrando como realizar esse tipo de coisa sem o jQuery
CertainPerformance
Justo. Sim, estou usando o JQuery. Obrigado
Kareem
3

Delegate e Live são fáceis de usar, mas se você não tiver mais li: s adicionados dinamicamente, também poderá usar a delagação de eventos com ligação / clique normal. Deve haver algum ganho de desempenho usando esse método, pois o DOM não precisará ser monitorado para novos elementos correspondentes. Não tenho números reais, mas faz sentido :)

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

Você pode testá-lo em jsFiddle: http://jsfiddle.net/jimmysv/4Sfdh/1/

jimmystormig
fonte
1
A que você está se referindo quando diz que esse 'DOM não precisa ser monitorado'? O delegado jq não monitora o dom.
redsquare
@redsquare Fiquei com a impressão de que o Delegado é apenas uma variante mais eficiente do Live. Isto é de api.jquery.com/delegate : "Anexe um manipulador a um ou mais eventos para todos os elementos que correspondem ao seletor, agora ou no futuro, com base em um conjunto específico de elementos raiz". Ele deve estar monitorando para vincular no futuro?
21411 jimmystormig
Não, ele apenas usa delegação no contêiner como seu código acima. Se você inserir um novo elemento li no contêiner, o evento click do contêiner ainda será acionado e tudo funcionará. Não é necessário monitoramento.
redsquare 15/02
@redsquare Sim, você está correto. Minha solução funciona mesmo se houver novos elementos adicionados após o carregamento do DOM. Como o Delegate usa o Live internamente (consulte stackoverflow.com/questions/2954932/… ), ainda parece que isso é mais otimizado, mas menos conveniente. Mas como eu disse, não tenho números.
22411 jimmystormig
2

Ainda outra maneira

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

Demo https://jsfiddle.net/m9xge3f5/

h0mayun
fonte