获取元素作为子元素相对于父元素的索引位置

177

假设我有以下标记:

<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 元素时,如何获取它相对于其父元素的索引?

例如,当您单击 "Step 1" 时,应该弹出一个带有 "0" 的 alert

6个回答

272
$("#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,但我对代理(delegate)不太熟悉 :D .. 为什么代理比直接在元素上附加事件更好呢? - stecb
1
@steweb - 嘿 - 因为一个事件处理程序比可能有很多个更可取。如果您有大型列表,则将事件附加到dom可能很昂贵,因此通常情况下,我尽量使用上述方法而不是在每个元素上使用单独的处理程序。一篇更深入的文章是http://briancrescimanno.com/2008/05/19/an-introduction-to-javascript-event-delegation/ - 还可以搜索“javascript事件委托”。 - redsquare
我已经使用相同的委托原则更新了答案,并添加了on()事件处理程序。 - Dan Atkinson
@danatkinson 嘿,世界真小,我在 Twitter 上关注了你...SO 在版本控制方面有点困难! - redsquare
新手问题:那个性能提示今天还管用吗,或者已经被“编译器”抽象化了?:P - cregox
显示剩余4条评论

45

类似这样:

$("ul#wizard li").click(function () {
  var index = $("ul#wizard li").index(this);
  alert("index is: " + index)
});

10
很棒的新增功能 - 我们可以相对于父级选择器找到索引。 +1 - BasTaller
1
我更喜欢这个回答,因为它实际上回答了问题,而不仅仅是一个可能的解决方案例子。 - DarkMukke

11

如果您不想使用像jQuery这样的大型库来完成此操作,那么并不需要。要使用内置的DOM操作实现此操作,请获取数组中的

  • 兄弟节点集合,并在单击时检查在该数组中点击元素的indexOf

    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>

    或者,如果子元素可能会动态更改(比如待办事项列表),那么您将不得不在每次点击时构建

  • 数组,而不是预先构建:

    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>


  • 2
    $(...).indexOf 不是一个函数。 - Kareem
    3
    我的答案中的代码片段没有产生那个错误 - 您可以按“运行代码片段”以查看它们工作。如果您遇到了该错误,则说明您正在使用不同的代码 - 这听起来像是您在使用jQuery,但我的答案展示了如何不使用jQuery完成此类操作。 - CertainPerformance
    好的,没问题。是的,我正在使用JQuery。谢谢。 - Kareem

    9

    看一下这个例子

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

    3

    Delegate和Live很容易使用,但如果您不会动态添加更多的li元素,则也可以使用事件委托与普通的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/

    1
    当你说“DOM 不需要被监控”时,你指的是什么?jq 代理不会监控 DOM。 - redsquare
    @redsquare 我的理解是Delegate只是Live的一种更高效的变体。这来自于http://api.jquery.com/delegate/:“基于特定的根元素,为与选择器匹配的所有元素附加一个或多个事件处理程序,现在或将来。”它必须监视将来绑定吗? - jimmystormig
    不,它只是像你上面的代码一样在容器上使用委托。如果你将一个新的li元素插入到容器中,容器的点击事件仍然会触发,并且一切仍然正常工作。不需要监控。 - redsquare
    @redsquare 是的,你说得对。我的解决方案即使在DOM加载后添加新元素也可以工作。由于Delegate在内部使用Live(请参见https://dev59.com/HnA85IYBdhLWcg3wEPVL#2954951),因此它仍然感觉更加优化但不太方便。但正如我所说,我没有数据支持。 - jimmystormig

    2

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接