JavaScript-[事件]-学习与总结

2022/05/03

JavaScriptHTML的交互是通过事件实现的,事件代表文档或浏览器窗口中某个有意义的时刻。

注册与解绑事件

传统方法

在JavaScript中指定事件处理程序的传统方法是**把一个函数赋值给DOM元素的一个事件处理程序**。

事件处理程序的名字以on开头,因此click的事件处理程序叫做onclick

let btn = document.querySelector('button');
btn.onclick = function() {
  alert('Clicked!');
}

同理,如果想解绑一个使用传统方法设置的事件,只需要把事件处理程序赋值为null

btn.onclick = null;
  • 唯一性同一个DOM元素对于同一事件只可以设置一个处理函数

监听注册方式(推荐)

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

  • element.addEventListener(type, listener [, useCapture]);
    • type: 事件类型字符串
      • 注意事件类型前不加on
    • listener: 事件处理函数
    • useCapture:
      • true为使用事件捕获
      • false或默认为使用事件冒泡
let btn = document.querySelector('button');
btn.addEventListener('click', function() {
  alert('Clicked!');
});

这么看可能无法感觉出监听注册方式的优势,但它最大的优势就是在于可以对于同一个事件,设置多个处理函数

<button>Click Me!</button>

let btn = document.querySelector('button');
btn.addEventListener('click', () => alert('First'));
btn.addEventListener('click', () => alert('Second'));
btn.addEventListener('click', () => alert('Third'));

// First
// Second
// Third

如果点击这个按钮,可以看到总共有三个对话框弹出,说明已经成功在这个按钮的click事件上设置多个处理函数。

当想要解绑一个使用监听注册方式注册的事件时,只可以使用removeEventListener()

此时需要注意,使用匿名函数作为事件处理函数的事件则无法被删除

let btn = document.querySelector('button');
btn.addEventListener('click', function() {
  alert('Clicked!');
});
// 下面这样是错误的
btn.removeEventListener('click', function() {
	alert('Clicked!');
})

看似这两个方法内的处理函数是相同的,但它们是两个完全不同的函数对象

let btn = document.querySelector('button');
function sayHello () {
	alert('Hello');
}
btn.addEventListener('click', sayHello);
// 这样才对
btn.removeEventListener('click', sayHello)

DOM事件流

事件流描述了页面接受事件的顺序。

当你点击一个按钮时,实际上不光点击了这个按钮,还点击了它的容器以及整个页面,那么事件触发的顺序会是怎么样?

现代主流浏览器都支持两种事件流模型事件冒泡事件捕获,下文将详细阐述二者的区别。

事件冒泡

事件冒泡是IE提出的模型,即从内到外,从深到浅触发事件,就像一个泡泡从水底一直上升到水面。

举个例子:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Document</title>
  </head>
  <body>
    <div>Click Me</div>
  </body>
</html>

在点击页面中的<div>后,click事件会如以下事件发生:

  1. <div>
  2. <body>
  3. <html>
  4. document

也就是说,点击div后,click事件沿着DOM树一路向上,在经过的每个节点依次触发,直至到达document对象。

<div class="father">
	<div class="son"></div>
</div>

let father = document.querySelector('.father');
let son = father.firstElementChild;

father.addEventListener('click', () => console.log('Father'));
son.addEventListener('click', () => console.log('Son'));

// Son
// Father

事件捕获

事件捕获是网景提出的另外一种模型,与事件执行顺序事件冒泡相反,即从外到内,从浅到深,就像一颗石头掉入水,被水捕获。

同样使用上方的例子,在捕获模型中的执行顺序是:

  1. document
  2. <html>
  3. <body>
  4. <div>

由于旧版本浏览器不支持,通常建议使用事件冒泡,特殊情况下可以使用事件捕获。

<div class="father">
	<div class="son"></div>
</div>

let father = document.querySelector('.father');
let son = father.firstElementChild;

father.addEventListener('click', () => console.log('Father'), true);
son.addEventListener('click', () => console.log('Son'), true);

// Father
// Son
// 与事件冒泡输出的顺序相反

事件对象

在浏览器中,event对象是传给事件处理程序的唯一参数。

事件对象包含了事件的基本信息,比如导致事件的元素,发生的事件类型,鼠标的坐标,键盘的按键等等....

先简单输出一个click事件的事件对象看看:

let btn = document.querySelector('button');
btn.addEventListener('click', event => console.log(event));

可以看到有这么一大堆属性,本文将会选几个最重要最常用的进行说明。

  • event.target 返回触发事件的对象
    • this 返回的是绑定事件的对象
    • event.currentTargetthis 功能相同,返回绑定该事件的对象
  • event.type 返回事件类型
    • click, mouseon, keydown 等等
  • event.preventDefault() 阻止默认事件
    • 禁用链接跳转
    • 禁用提交按钮提交数据
  • event.stopPropagation() 阻止事件冒泡
// 阻止链接跳转
<a href="www.google.com">Google</a>

let a = document.querySelector('a');
a.addEventListener('click', e => e.preventDefault());
// 阻止事件冒泡
<div class="father">
	<div class="son"></div>
</div>

let father = document.querySelector('.father');
let son = father.firstElementChild;

father.addEventListener('click', e => console.log('Father'));
son.addEventListener('click', e => {
  console.log('Son');
  e.stopPropagation();
});

// Son [不会输出Father] 

事件委托

如果我们页面中有非常多的按钮,每一个按钮都需要绑定一个click事件,这样生成的每一个事件处理函数都是一个对象,都占用内存空间,对象越多,性能越差。

为了解决这个问题,可以利用事件委托。事件委托利用事件冒泡,可以只用一个事件处理程序来管理一种类型的事件。

  • 使用事件委托,只要给所用元素共同的祖先节点添加一个事件处理程序即可。

例:

<ul>
  <li id="one">Click</li>
  <li id="two">Click</li>
  <li id="three">Click</li>
</ul>

let ul = document.querySelector('ul');
ul.addEventListener('click', function (e) {
  switch (e.target.id) {
    case 'one':
      // do something
      break;
    case 'two':
      // do something
      break;
    case 'three':
      // do something
  }
});

事件类型

由于Web浏览器中可以发生的事件有太多太多,在此只列举最常用的一些,具体还请参考官方文档事件参考

鼠标事件

  • contextmenu 禁用右键菜单
document.addEventListener('contextmenu', e => e.preventDefault());
  • selectstart 禁止鼠标选中
document.addEventListener('selectstart', e => e.preventDefault());

键盘事件

  • onkeyup 松开时触发
  • onkeydown 按下时触发
  • onekypress 按下时触发 (不能识别功能键
  • e.key 获得被触发的键

执行顺序:先keydown,再keypress,最后keyup

keydown 和 keypress在文本框中,先触发,然后文字才被输入