Получить индекс элемента как дочерний относительно родителя

160

Допустим, у меня есть эта разметка:

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

И у меня есть этот JQuery:

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

Как я могу получить индекс ребенка по liотношению к его родителю при нажатии на это li?

Например, когда вы нажимаете «Шаг 1», alertдолжно появиться «0».

AgileMeansDoAsLittleAsPossible
источник

Ответы:

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

Однако вместо того, чтобы прикреплять обработчик одного клика для каждого элемента списка, лучше (с точки зрения производительности) использовать, delegateкоторый будет выглядеть следующим образом:

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

В jQuery 1.7+ вы должны использовать on. Приведенный ниже пример привязывает событие к #wizardэлементу, работая как событие делегата:

$("#wizard").on("click", "li", function() {
    console.log( $(this).index() );
});
Красный квадрат
источник
3
Привет redsquare .. Я практикуюсь с jQuery, я вроде n00b о делегате: D .. почему делегат лучше, чем прикрепление событий непосредственно к элементам?
Stecb
1
@steweb - Привет - потому что один обработчик событий гораздо предпочтительнее, чем многие. Присоединение событий к dom может быть дорогостоящим, если у вас большие списки, поэтому, как правило, я стараюсь использовать вышесказанное, где это возможно, а не отдельные обработчики для каждого элемента. Одна статья , которая идет глубже briancrescimanno.com/2008/05/19/... - также Google «делегирование событий JavaScript»
RedSquare
Итак, управление событиями таким образом, это что-то легче, чем классическая «привязанность к дому» :) .. спасибо!
Stecb
@steweb - Это также легче, потому что jquery не нужно выполнять начальный поиск всех элементов li. Он просто просматривает контейнер и прослушивает на этом уровне, чтобы увидеть, был ли выбранный объект li.
красный квадрат
Этот метод .index очень полезен для меня. Я использую jQuery Sortable для сортировки, но она хранит информацию о семантическом ранжировании в DOM. С помощью .index () я могу очень аккуратно вывести рейтинг из списка. Спасибо!
SimplGy
41

что-то вроде:

$("ul#wizard li").click(function () {
  var index = $("ul#wizard li").index(this);
  alert("index is: " + index)
});
затор
источник
9
Отличное дополнение - мы можем найти индекс относительно родительского селектора. +1
BasTaller
1
предпочитаю этот ответ, так как он на самом деле отвечает на вопрос, а не на возможное решение для примера
DarkMukke
8

Посмотрите на этот пример .

$("#wizard li").click(function () {
    alert($(this).index()); // alert index of li relative to ul parent
});
Kimtho6
источник
4

Нет необходимости требовать большую библиотеку, такую ​​как jQuery, если вы этого не хотите. Чтобы достичь этого с помощью встроенных манипуляций с DOM, получите коллекцию liбратьев и сестер в массиве, и при щелчке отметьте indexOfэлемент clicked в этом массиве.

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>

Или с делегированием события:

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>

Или, если дочерние элементы могут изменяться динамически (как со списком задач), вам придется создавать массив lis при каждом щелчке, а не заранее:

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
источник
$ (...). indexOf не является функцией
Карим
2
Ни один из фрагментов кода в моем ответе не приводит к этой ошибке - вы можете нажать «Выполнить фрагмент кода», чтобы увидеть, как они работают. Если вы получаете эту ошибку, вы используете другой код - похоже, вы используете jQuery, но мой ответ показывает, как этого добиться без jQuery
CertainPerformance
Справедливо. Да, я использую JQuery. Спасибо
Карим
3

Delegate и Live просты в использовании, но если у вас больше не будет динамически добавляться li: s, вы также можете использовать функцию delagation с обычным bind / click. При использовании этого метода должно быть некоторое повышение производительности, так как DOM не нужно отслеживать для новых соответствующих элементов. У меня нет реальных цифр, но это имеет смысл :)

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

Вы можете проверить это на jsFiddle: http://jsfiddle.net/jimmysv/4Sfdh/1/

jimmystormig
источник
1
На что вы ссылаетесь, когда говорите, что «DOM не нужно контролировать»? JQ делегат не контролирует дом.
красный квадрат
@redsquare У меня сложилось впечатление, что Delegate - это просто более эффективный вариант Live. Это от api.jquery.com/delegate : «Присоединить обработчик к одному или нескольким событиям для всех элементов, которые соответствуют селектору, сейчас или в будущем, на основе определенного набора корневых элементов». Это должен быть мониторинг, чтобы связать в будущем?
jimmystormig
Нет, он просто использует делегирование для контейнера, как ваш код выше. Если вы вставите новый элемент li в контейнер, событие щелчка контейнера все равно будет запущено, и все будет работать. Мониторинг не требуется.
красный квадрат
@redsquare Да, вы правы. Мое решение работает, даже если после загрузки DOM добавлены новые элементы. Поскольку Delegate использует Live для внутреннего использования (см. Stackoverflow.com/questions/2954932/… ), он все еще чувствует себя более оптимизированным, но менее удобным. Но, как я уже сказал, у меня нет номеров.
jimmystormig