js事件委托

    <ul>
        <li data-index='1'>1</li>
        <li data-index='2'>2</li>
        <li data-index='3'>3</li>
    </ul>

上面这样可以把事件绑定在ul上,通过dataset来判断点击的是哪个li,来触发不同的动作

下面这种

<ul>
    <li data-index='1'>
        <p>
            <span>1</span>
        </p>
        <img/>
    </li>
    <li data-index='2'>
        <p>
            <span>2</span>
        </p>
        <img/>
    </li>
    <li data-index='3'>
        <p>
            <span>3</span>
        </p>
        <img/>
    </li>
</ul>

li 内有多个子元素,点击 li 必然可能会点到子元素,如果子元素上不写dataset等标记,如何判断点了哪个li? 如果给li的子元素都写上标记那肯定不对吧,太多了。这种情况怎么优化事件绑定的过程?

阅读 3.8k
5 个回答

事件被触发的时候,事件对象event有两个很关键属性Event.targetEvent.currentTarget
它们俩的主要区别就在于,currentTarget该属性总是指向被绑定事件的元素,而target则是指向触发该事件的元素。

举个例子,你把这个事件绑定在了最上级的<ul>标签中,然后你现在点击了下属某个<span>标签。
之后就是事件冒泡,冒泡至<ul>标签时,回调被触发,此时Event.currentTarget指向的是当前<ul>的DOM对象,而Event.target则是指向你点击的那个<span>的DOM对象。

所以在事件委托的处理中,只要在回调中对Event.target进行分析,就能知道触发事件的元素是不是你需要的了。

一个思路是,遍历父元素找到<li>

var target = e.target
while( target ){
    if( target.nodeName == 'li' ){
        return
    }
    target = target.parentNode
} 

// 读取标记
target.dataIndex

你要是想获取序号,根本不用标注,直接从ul的子节点一个一个往下找就可以了。如果只有几个或十几个li,不会很慢。

<ul>
<li><span>1st</span></li>
<li><span>2nd</span></li>
<li><span>3rd</span></li>
</ul>

<script>

    document.addEventListener('DOMContentLoaded', function() {

        var ul = document.querySelector('ul');
        ul.addEventListener('click', function(e) {
            // <span>, <ul>
            console.log(e.target, e.currentTarget);
           
            var li = e.target;
            while(li.nodeName !== 'LI')
                li = li.parentNode;
            
            var idx = [].indexOf.call(ul.children, li) + 1;
            console.log(idx);
        });
    })

</script>

建议看下DOM事件流程,即:

捕获阶段→目标阶段→冒泡阶段

其中捕获阶段就是事件从父元素向子元素传递的过程,说白了就是父元素应该比子元素更早的拿到事件,所以我觉得题主可以直接在每个li元素上直接挂事件监听、然后设置捕获模式(addEventListener第三个参数设置为true),这样处理时再判断下index就行了。
另外,我觉得不用特别纠结在事件委托上,个人以为它更加适用于父元素里有大量重复的子元素或者父元素内的子元素不确定的情况。

CSS

li > * { pointer-events: none; }

这是另一种解决方案

推荐问题
宣传栏