关注web技术发展
用心做好每一件事

js事件委托,jq事件委托,以及解决vue事件委托

在前端开发中,事件委托用还是比较多的,为什么要用事件委托?事件委托有什么好处?如果这些概念不是很清楚,那么请点击这里:传送门:为什么要用事件委托?事件委托有什么好处

原生javascript处理事件委托是这么处理的:

假定我们有一个UL元素,它有几个子元素:

<ul id="parent-list">
    <li id="post-1">Item 1</li>
    <li id="post-2">Item 2</li>
    <li id="post-3">Item 3</li>
    <li id="post-4">Item 4</li>
    <li id="post-5">Item 5</li>
    <li id="post-6">Item 6</li>
</ul>

我们还假设,当每个子元素被点击时,将会有各自不同的事件发生。你可以给每个独立的li元素添加事件监听器,但有时这些li元素可能会被删除,可能会有新增,监听它们的新增或删除事件将会是一场噩梦,尤其是当你的监听事件的代码放在应用的另一个地方时。但是,如果你将监听器安放到它们的父元素上呢?你如何能知道是那个子元素被点击了?

简单:当子元素的事件冒泡到父ul元素时,你可以检查事件对象的target属性,捕获真正被点击的节点元素的引用。下面是一段很简单的JavaScript代码,演示了事件委托的过程:

// 找到父元素,添加监听器...

document.getElementById("parent-list").addEventListener("click",function(e) {
    // e.target是被点击的元素!
    // 如果被点击的是li元素
    if(e.target && e.target.nodeName == "LI") {
        // 找到目标,输出ID!
        console.log("List item",e.target.id.replace("post-")," was clicked!");
    }
});

第一步是给父元素添加事件监听器。当有事件触发监听器时,检查事件的来源,排除非li子元素事件。如果是一个li元素,我们就找到了目标!如果不是一个li元素,事件将被忽略。这个例子非常简单,UL和li是标准的父子搭配。让我们试验一些差异比较大的元素搭配。假设我们有一个父元素div,里面有很多子元素,但我们关心的是里面的一个带有”classA” CSS类的A标记:

// 获得父元素DIV, 添加监听器...

document.getElementById("myDiv").addEventListener("click",function(e) {
    // e.target是被点击的元素
    if(e.target && e.target.nodeName == "A") {
        // 获得CSS类名
        var classes = e.target.className.split(" ");
        // 搜索匹配!
        if(classes) {
            // For every CSS class the element has...
            for(var x = 0; x < classes.length; x++) {
                // If it has the CSS class we want...
                if(classes[x] == "classA") {
                    // Bingo!
                    console.log("Anchor element clicked!");

                    // Now do something here....

                }
            }
        }

    }
});

上面这个例子中不仅比较了标签名,而且比较了CSS类名。虽然稍微复杂了一点,但还是很具代表性的。比如,如果某个A标记里有一个span标记,则这个span将会成为target元素。这个时候,我们需要上溯DOM树结构,找到里面是否有一个 A.classA 的元素。

jq事件委托是这么处理的:

jQuery中的事件委托方式比较丰富,就以同样的例子来说:

  1. 用on方法,代码如下:
$(function(){
            $("#lists").on("click","li",function(event){
                var target = $(event.target);
                target.css("background-color","red");
            })
        })
  1. 用delegate()方法,代码如下:
 $(function(){
 $("#lists").delegate("li","click",function(event){
                var target = $(event.target);
               target.css("background-color","red");
             })
         })

复制代码
on()方法和delegate()方法对于事件委托的写法很像。并且执行事件委托的时候只有子元素(本文中的li)会触发事件,而代为执行的父元素(本文中为ul)不会触发事件,所以我们不需要盘判断触发事件的元素节点名,这一点明显优于原生的JavaScript。

  1. 用bind()方法,代码如下:
        $(function(){
            $("#lists").bind("click","li",function(event){
                var target = $(event.target);
                 if(target.prop("nodeName")=="LI"){
                 target.css("background-color","red");}
            })
        })

bind()方法同原生的JavaScript实现方法一样,当父元素代子元素执行事件时,父元素也会触发事件,所以我们需要判断一下触发事件的元素名。此外,用bind()方法给元素绑定事件的时候要注意,它只能给已经存在DOM元素添加事件,不能给未来存在DOM

元素添加添加事件。如果要频繁地添加DOM元素,并且给新添加的DOM元素绑定事件的话,用live(),delegate(),on()等方法。鉴于jQuery从1.7之后就不推荐live()delegate()方法了,所以大家还是使用on()方法吧。

vue事件委托

刚接触到vue的时候列表事件是这样的

<ul>
    <li v-for="(item, index) in data" @click="handleClick(index)">
        Click Me
    </li>
</ul>

然后这样的话,结果就是所有的li元素都绑定了事件。

我们都知道,过多的事件对于性能来说是很糟糕的,尤其在移动端,可以说是无法容忍。

于是我想到了事件委托,结合自己的项目,因为事件冒泡的关系,我的点击元素其实是span,但是我想要触发的是li,于是有了下面的代码。

<div class="read_list">
    <ol class="chapter-ol-catalog" @click="checkLogItem">
        <li class="chapter-bar" data-index="0">
            第一卷:我想说
        </li>
        <li class="chapter-li" data-index="0">
            <div>第一章:上架感言,新年快乐</div>
        </li>
        <li class="chapter-li" data-index="0">
            <div>第一章:上架感言,新年快乐</div>
        </li>
        <li class="chapter-li" data-index="0">
            <div>第一章:上架感言,新年快乐</div>
        </li>
        <li class="chapter-li" data-index="0">
            <div>第一章:上架感言,新年快乐</div>
        </li>
        <li class="chapter-li" data-index="0">
            <div>第一章:上架感言,新年快乐</div>
        </li>
    </ol>
  </div>

js代码

methods:{
      checkLogItem(e){
        console.log(e.target.parentNode.nodeName)
        if (e.target.parentNode.nodeName.toLowerCase() === 'li') {
            const index = parseInt(e.target.parentNode.dataset.index)
            console.log(index)
            //this.$router.push("/readingIndex")
          }
      },
  }
分享到: 更多 (0)

我要点评 抢沙发

评论前必须登录!