Twlig / issuesBlog

MIT License
3 stars 0 forks source link

事件处理与事件委托 #20

Open Twlig opened 2 years ago

Twlig commented 2 years ago

事件意味着用户或浏览器执行的某种动作。比如,单击(click)、加载(load)、鼠标悬停(mouseover)。为响应事件而调用的函数被称为事件处理程序(或事件监听器)事件处理程序的名字以"on"开头,因此 click 事件的处理程序叫作 onclick,而 load 事件的处理程序叫作 onload。

事件处理程序

事件处理程序有很多指定方式

  1. HTML事件处理程序

    特定元素支持的每个事件都可以使用事件处理程序的名字以 HTML 属性的形式来指定。

    <script> 
    function showMessage() { 
    console.log("Hello world!"); 
    } 
    </script> 
    <input type="button" value="Click Me" onclick="showMessage()"/>
  2. DOM0事件处理程序

    把一个函数赋值给(DOM 元素的)一个事件处理程序属性。

    let btn = document.getElementById("myBtn"); 
    btn.onclick = function() { 
    console.log(this.id); // "myBtn"  this指向该dom元素
    };

    在事件处理程序里通过 this 可以访问元素的任何属性和方法。以这种方式添加事件处理程序是注册在事件流的冒泡阶段的。

    通过将事件处理程序属性的值设置为 null,可以移除通过 DOM0 方式添加的事件处理程序,如下面的例子所示:

    btn.onclick = null;
  3. DOM2事件处理程序

    DOM2 Events 为事件处理程序的赋值和移除定义了两个方法:addEventListener()和 removeEventListener()

    接收 3 个参数:事件名、事件处理函数和一个布尔值,true 表示在捕获阶段调用事件处理程序,false(默认值)表示在冒泡阶段调用事件处理程序

    let btn = document.getElementById("myBtn"); 
    btn.addEventListener("click", () => {  //注意,这里是click不是onclick
    console.log(this.id); 
    }, false);  //在冒泡的时候调用事件处理程序

    注意:事件名前面没有on,有on的是事件处理程序

    通过 addEventListener()添加的事件处理程序只能使用 removeEventListener()并传入与添加时同样的参数来移除。这意味着使用 addEventListener()添加的匿名函数无法移除

    let btn = document.getElementById("myBtn"); 
    btn.addEventListener("click", () => { 
    console.log(this.id); 
    }, false); 
    // 其他代码
    btn.removeEventListener("click", function() { // 没有效果!
    console.log(this.id); 
    }, false);
    let btn = document.getElementById("myBtn"); 
    let handler = function() { 
    console.log(this.id); 
    }; 
    btn.addEventListener("click", handler, false); 
    // 其他代码
    btn.removeEventListener("click", handler, false); // 有效果!
  4. IE事件处理程序

    attachEvent()和 detachEvent()。这两个方法接收两个参数事件处理程序的名字和事件处理函数。因为 IE8 及更早版本只支持事件冒泡,所以使用attachEvent()添加的事件处理程序会添加到冒泡阶段,不需要三个参数。

    var btn = document.getElementById("myBtn"); 
    btn.attachEvent("onclick", function() { 
    console.log("Clicked"); 
    });

    IE与 DOM0 方式区别:

    主要区别是事件处理程序的作用域

    • 使用 DOM0方式时,事件处理程序中的 this 值等于目标元素

    • 使用 attachEvent()时,事件处理程序是在全局作用域中运行的,因此 this 等于 window

事件对象

在 DOM 中发生事件时,所有相关信息都会被收集并存储在 event 对象中。

let btn = document.getElementById("myBtn"); 
btn.onclick = function(event) { 
 console.log(event.type); // "click" 
}; 
btn.addEventListener("click", (event) => { 
 console.log(event.type); // "click" 
}, false);

HTML属性指定的事件也有event对象

<input type="button" value="Click Me" onclick="console.log(event.type)">

event对象中有很多属性和方法,下面介绍最主要的几个:

  1. type

    被触发的事件类型,如click,mouseover等事件名

  2. currentTarget和target

    currentTarget表示当前事件处理程序所在的元素,this始终等于currentTarget。target表示事件目标。

    如果我们在button和body上都添加了事件处理程序:

    • 那么当我们点击button的时
      • button的事件处理程序中的event.currentTarget是button,target也是button。
      • body的事件处理程序中的event.currentTarget是body,target是button
    • 点击body时(没点到body内其他元素)
      • body的事件处理程序中的event.currentTarget是body,target是body

    总结:点击的元素决定着target,当前元素的事件处理程序决定这currentTarget。也就是说,在一个事件流中,target是不变的,但是由于target的父元素也存在事件处理程序,那么父元素的事件处理程序中的currentTarget就是父元素本身。

  3. preventDefault()

    preventDefault()方法用于阻止特定事件的默认动作。比如,链接的默认行为就是在被单击时导航到 href 属性指定的 URL。如果想阻止这个导航行为,可以在 onclick 事件处理程序中取消,如下面的例子所示:

    let link = document.getElementById("myLink"); 
    link.onclick = function(event) { 
    event.preventDefault(); 
    };
  4. stopPropagation()

    stopPropagation()方法用于立即阻止事件流在 DOM 结构中传播,取消后续的事件捕获或冒泡。例如,直接添加到按钮的事件处理程序中调用 stopPropagation(),可以阻止 document.body 上注册的事件处理程序执行。比如:

    let btn = document.getElementById("myBtn"); 
    btn.onclick = function(event) { 
    console.log("Clicked"); 
    event.stopPropagation(); 
    }; 
    document.body.onclick = function(event) { 
    console.log("Body clicked"); 
    };
    //Clicked
    //body click事件处理程序被取消

事件委托

“过多事件处理程序”的解决方案是使用事件委托。事件委托利用事件冒泡,可以只使用一个事件处理程序来管理一种类型的事件。

如下,有三个li元素,点击每个元素都有不同的事件触发

<ul id="myLinks"> 
 <li id="goSomewhere">Go somewhere</li> 
 <li id="doSomething">Do something</li> 
 <li id="sayHi">Say hi</li> 
</ul>

使用事件委托,只要给所有元素共同的祖先节点添加一个事件处理程序,就可以解决问题。

let list = document.getElementById("myLinks"); 
list.addEventListener("click", (event) => { 
 let target = event.target; 
 switch(target.id) { 
 case "doSomething": 
 document.title = "I changed the document's title"; 
 break; 
 case "goSomewhere": 
 location.href = "http:// www.wrox.com"; 
 break; 
 case "sayHi": 
 console.log("hi"); 
 break; 
 } 
});