JavaScript — 事件

183 阅读7分钟

1.JS 中有几种注册事件的方法,分别有什么区别,以一个点击事件为例,它的完整流程是怎么样的?

  1. 直接在 HTML 元素上注册事件处理程序:可以通过在 HTML 标签中添加 onclick 属性来直接注册事件处理函数(简单易用,但不宜用于大规模的应用开发,因为事件处理逻辑与 HTML 代码紧密耦合)
// 当按钮被点击时,handleClick() 函数将被执行
<button onclick="handleClick()">点击</button>
  1. 使用 DOM 对象的属性注册事件处理程序:可以通过访问 DOM 对象的属性来注册事件处理函数(比较原始,容易出现冲突和覆盖,不够灵活)
// 使用这种方法注册的事件处理程序只能注册一个,如果为同一个事件绑定多个处理程序,后面的处理程序会覆盖前面的处理程序
const button = document.querySelector("button");
button.onclick = handleClick;
  1. 使用 addEventListener() 方法注册事件处理程序:可以使用 addEventListener() 方法来注册事件处理程序(可以在一个元素上同时注册多个相同类型的事件处理函数,而且支持更细粒度的事件操作,如事件的捕获和冒泡等)
// 使用这种方法注册的事件处理程序可以通过 removeEventListener() 方法进行移除
const button = document.querySelector("button");
button.addEventListener("click", handleClick);
// 移除事件处理程序
button.removeEventListener("click", handleClick);
  • 点击事件处理流程如下:
    • 用户在浏览器中点击页面上的按钮或其他交互元素。
    • 浏览器检测到点击事件,并根据事件类型(例如 click)创建一个事件对象。
    • 浏览器根据事件对象确定目标元素,即被点击的按钮。
    • 浏览器遍历目标元素的事件处理程序列表,按照注册顺序依次执行这些处理程序。
    • 执行事件处理程序代码,实现特定的功能或逻辑。
    • 如果事件处理程序中有相应的操作(如禁止默认行为或阻止事件冒泡),浏览器根据处理程序的代码执行相应的操作。
    • 事件处理程序执行完毕,返回到浏览器事件循环,等待下一个事件触发。

2.Input 中监听值的变化是在监听什么事件

  1. input 事件:当输入元素的值发生任何改变时触发,包括键盘输入、粘贴、剪切等操作
const inputElement = document.querySelector("input"); 
inputElement.addEventListener("input", function(event) { 
  console.log("输入值变化了:", event.target.value);
});
  1. change 事件:当输入元素的值发生改变且失去焦点时触发,适用于对输入完成后再进行监听
const inputElement = document.querySelector("input"); 
inputElement.addEventListener("change", function(event) { 
  console.log("输入值已改变:", event.target.value);
});
  1. keyup 事件:当抬起键盘上的按键时触发,可以用于监听实时的键盘输入
const inputElement = document.querySelector("input"); 
inputElement.addEventListener("keyup", function(event) { 
  console.log("键盘抬起:", event.target.value);
});
  1. keydown 事件:当按下键盘上的按键时触发,也可以用于监听实时的键盘输入
const inputElement = document.querySelector("input"); 
inputElement.addEventListener("keydown", function(event) {
  console.log("键盘按下:", event.target.value);
});
  1. keypress 事件:在用户按下键盘上的字符键时触发。只会触发字符键,而不会触发非字符键(如 Shift、Ctrl 等)
<input type="text" id="myInput"> 
<script> 
const inputElement = document.getElementById('myInput'); 
inputElement.addEventListener('keypress', function(event) { 
  console.log('按下的键:', event.key); 
  console.log('按下的键的 ASCII 值:', event.keyCode);
}); 
</script>

3.浏览器中监听事件函数 addEventListener 第三个参数有哪些值? 他们的作用是什么?

  • addEventListener(event, listener, options)
    • event:要监听的事件类型,如 "click"、"keydown" 等。
    • listener:事件处理函数,即当指定的事件被触发时执行的代码块。
    • options:一个可选的对象,用于设置一些附加选项,包括以下属性:
      • capture:一个布尔值,表示事件是在捕获阶段还是冒泡阶段进行处理。默认为 false(在冒泡阶段处理事件)
      • once:一个布尔值,表示事件处理程序是否只会在首次触发事件时执行一次。当设置为 true 时,事件处理函数将在执行后自动移除
      • passive:一个布尔值,表示事件处理程序是否是被动的,即不会调用 event.preventDefault()。在性能优化方面有一定的作用
const button = document.querySelector("button"); 
button.addEventListener("click", function(event) { console.log("按钮被点击了"); }, { once: true }); 
// 按钮被点击时,事件处理函数将只执行一次

4.什么是事件冒泡和事件捕获?

  • 事件发生时会在元素之间按照特定的顺序传播,路径所经过的元素都会收到该事件,这个传播过程即—DOM事件流 image.png
  • 当触发事件时,会经历两个阶段,分别是捕获阶段、冒泡阶段
  • 捕获阶段是—从父到子 ,冒泡阶段是—从子到父(实际工作都是使用事件冒泡为主)
  1. 事件捕获:当一个元素的事件被触发时,会从DOM的根元素开始去执行对应的事件 (从外到里)
DOM.addEventListener(事件类型, 事件处理函数, 是否使用捕获机制);
// 说明: 
// addEventListener第三个参数传入 true 代表在捕获阶段触发(很少使用) 
// 若传入false代表冒泡阶段触发,默认就是false 
// 若是用 L0 事件监听,则只会在冒泡阶段触发
  1. 事件冒泡:当一个元素的事件被触发时,事件会依次传播给其所有祖先元素,这一过程被称为事件冒泡(当一个元素触发事件后,会依次向上调用所有父级元素的 同名事件)
const father = document.querySelector(".father"); const son = 
documnet.querySelector(".son"); 
document.addEventListener("click", function() { 
  alert("我是爷爷");
}); 
fa.addEventListener("click", function() { 
  alert("我是爸爸");
}); 
son.addEventListener("click", function() {
  alert("我是儿子"); 
});
  1. 阻止冒泡—因为默认就有冒泡模式的存在,所以容易导致事件影响到父级元素,想把事件就限制在当前元素内,就需要阻止事件的传播
// 阻止事件的传播方法 事件对象.stopPropagation();
const father = document.querySelector(".father"); 
const son = document.querySelector(".son"); document.addEventListener("click", function() { 
  alert("我是爷爷"); 
}); 
fa.addEventListener("click", function(e) { 
  alert("我是儿子"); 
  // 阻止冒泡 
  e.stopPropagation();
});

5.什么是事件委托,e.currentTarget 和 etarget 有何区别?

  • 事件委托的原理是将事件监听器添加到父级元素,而不是直接添加到每个子元素上。当事件冒泡到父级元素时,通过判断事件的目标元素来执行相应的操作。这样可以减少事件监听器的数量,提高页面性能,并且还可以动态地添加或删除子元素而无需重新注册事件监听器
  • e.currentTarget 和 e.target 是事件对象的两个属性,用于表示事件的当前目标(当前事件正在冒泡或捕获的阶段所经过的元素)和触发目标(用户实际触发事件的元素)
    • e.currentTarget 表示注册事件监听器的元素,即事件当前正在经过的那个元素。在事件处理函数中,e.currentTarget 通常用于引用父级元素,从而进行事件委托的操作
    • e.target 表示实际触发事件的元素,即用户实际点击或操作的那个元素。在事件处理函数中,e.target 通常用于判断用户点击的具体元素,从而执行相应的操作
  • 区别:
    • e.currentTarget 指向事件监听器所注册的元素,在事件处理函数中一般使用它来进行事件委托
    • e.target 指向实际触发事件的元素,在事件处理函数中一般通过它来判断用户的操作目标,并执行相应的操作
<ul> 
  <li>我是第一个li</li> 
  <li>我是第二个li</li> 
  <li>我是第三个li</li> 
</ul> 

const ul = document.querySelector("ul"); 
ul.addEventListener("click", function(e) { 
  // 使用e.currentTarget获取绑定事件处理程序的元素 
  console.log('Current Target:', e.currentTarget); 
  // 使用e.target获取实际触发事件的元素 
  console.log('Target:', e.target); 
  // 判断事件发生在哪个具体的子元素上 
  if(e.target.tagName === 'LI') { 
    console.log('Clicked on:', e.target.textContent); 
  } 
}); 

// 不给子元素注册事件,给父元素注册事件,当触发事件时,会冒泡到父元素身上,从而触发父元素的事件 
// 使用事件对象.target可以获得真正触发事件的子元素,e.target.tagName可以获得其标签名 
// 优点:减少注册次数,提高了程序性能