[TOC]

说明

image.png
image.png
比如下面,给一个按钮(元素),绑定点击事件,点击时某个元素的内容变成当前的时间(事件处理程序)

<!-- 格式为:<元素 事件名="函数 或 js代码"></元素> ,单引号双引号都可以,不能混合用 -->
<button onclick='getElementById("demo").innerHTML=Date()'>The time is?</button>

如何监听事件

事件处理程序的名字以”on”开头,因此 click 事件的处理程序叫作 onclick,而 load 事件的处理程序叫作 onload。

方式1、元素的HTML属性监听

通过HTML元素的事件属性来实现
image.png
image.png

<!-- 直接放入JS代码 或者 函数 -->
<button onClick="console.log(123)">click</button>
<button onClick="divClick()">click</button>


<!-- this 值相当于事件的目标元素 -->
<button onClick="console.log(this)">123</button>

<!-- 事件自带一个 event 事件对象,可以传入 -->
<button onClick="console.log(event)">123</button>

JS - 事件监听 - 图5event对象

弊端:
1、第一个问题是时机问题

有可能 HTML 元素已经显示在页面上,用户都与其交互了,而事件处理程序的代码还无法执行。

如果函数在在按钮中代码的后面定义的,那么当用户在函数被定义之前点击按钮时,就会发生错误。

2、另一个问题是对事件处理程序作用域链的扩展(在表单中的按钮,可以直接访问表单中组件的值)在不同浏览器中可能导致不同的结果

3、最后一个问题是 HTML 与 JavaScript 强耦合(关联性太强)。

如果需要修改事件处理程序,则必须在两个地方,即 HTML 和 JavaScript 中,修改代码。

这也是很多开发者不使用 HTML事件处理程序的主要原因。

方式2、元素的on属性监听

或者先获取元素,然后通过.属性添加

// ============== 1、先从文档中取得按钮 ==============
let btn = document.getElementById("myBtn"); 

// ==============2、给它的 onclick 事件处理程序赋值一个函数 ==============
btn.onclick = function() { 
 console.log("Clicked"); 
};

btn.onclick = function() { // 会覆盖上面的
 console.log(this);  // this指元素本身,就是btn
};


// ============== 3、移除事件 ==============
btn.onclick = null;

弊端:不够灵活,多次赋值事件会覆盖上面的,就像对一个属性多次赋值一样

方式3:EventTarget 的方法监听(推荐)

在JS的后续更新,所有DOM的元素,都是继承 EventTarget 这个对象,这个对象有3个处理事件的方法,因此所有的元素都可以被添加事件。

// ============== 1、先获取元素 ==============
let btn = document.getElementById("myBtn"); 


// ============== 2、再给元素添加事件 ==============
let handler = function() { 
 console.log(this.id); 
}; 

btn.addEventListener("click", handler, false); 
btn.addEventListener("click", ()=>{
  console.log(this.id); 
}); 


// ============== 3、移除事件 ==============
btn.removeEventListener("click", handler, false); // 有效果!

// 使用 addEventListener()添加的匿名函数(箭头函数)无法移除
btn.addEventListener("click", () => { 
 console.log(this.id); 
 }, false);

btn.removeEventListener("click", function() { // 没有效果!
 console.log(this.id); 
}, false);

可以添加多个,可以同时触发执行,多个事件处理程序以添加顺序来触发

有什么方法可以查看MDN文档:https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget

=============

事件流

说明

image.png

1、事件冒泡 Event Bubble

事件触发时,是由最内层的元素开始触发,然后一层一层触发。

/*
<!DOCTYPE html> 
<html> 
  <head> 
       <title>Event Bubbling Example</title> 
  </head> 
  <body> 
       <div id="myDiv">Click Me</div> 
  </body> 
</html>
*/

在点击页面中的

元素后,click 事件会以如下顺序发生:
(1)

(2)
(3)
(4) document
JS - 事件监听 - 图7
现代浏览器中的事件会一直冒泡到 window 对象。

添加事件,默认就是事件冒泡。
image.png

2、事件捕获 Event Capture

和冒泡相反,事件捕获是由最外层开始触发,直到最里面那层

如果上面的例子使用事件捕获,则点击

元素会以下列顺序触发 click 事件:
(1) document
(2)
(3)
(4)

在事件捕获中,click 事件首先由 document 元素捕获,然后沿 DOM 树依次向下传播,直至到达实际的目标元素

JS - 事件监听 - 图9

添加事件时,第三个参数传true,就是事件捕获
image.png

3、DOM事件流

image.png
因此现代浏览器,两种情况都添加了,看你自己实际代码采取哪种,用的多的是事件冒泡

JS - 事件监听 - 图12

=============

事件对象 Event

说明

image.png

let btn = document.getElementById("myBtn"); 

// DOM 0
btn.onclick = function(event) { 
 console.log(event); // 见下
}; 

// DOM 2
btn.addEventListener("click", (event) => { 
 console.log(event); // 见下
}, false);

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

JS - 事件监听 - 图14
事件对象MDN文档:https://developer.mozilla.org/zh-CN/docs/Web/API/Event
image.png

事件类型 type见下

this

在事件处理程序内部,this 对象始终等于 currentTarget 的值,而 target 只包含事件的实际目标。

如果事件处理程序直接添加在了意图的目标,则 this、currentTarget 和 target 的值是一样的。

如果这个事件处理程序是添加到按钮的父节点(如 document.body)上,那么它们的值就不一样了。

// 在 document.body 上添加了单击处理程序
document.body.onclick = function(event) { 
 console.log(event.currentTarget === document.body); // true 
 console.log(this === document.body); // true 
 console.log(event.target === document.getElementById("myBtn")); // true 
};

这种情况下点击按钮,this 和 currentTarget 都等于 document.body,这是因为它是注册事件处理程序的元素。

而 target 属性等于按钮本身,这是因为那才是 click 事件真正的目标。

由于按钮本身并没有注册事件处理程序,因此 click 事件冒泡到 document.body,从而触发了在它上面注册的处理程序。

阻止特定事件

preventDefault()方法用于阻止特定事件的默认动作。

比如,链接的默认行为就是在被单击时导航到 href 属性指定的 URL。

// 1、拿到要阻止的元素
let link = document.getElementById("myLink"); 

// 2、调用方法取消默认行为的事件
link.addEventListener("click",function(event) { 
  event.preventDefault(); // 阻止默认行为方法
})

阻止事件传播

stopPropagation()方法用于立即阻止事件流在 DOM 结构中传播,取消后续的事件捕获或冒泡。

// 1、拿到要阻止的元素
let btn = document.getElementById("myBtn"); 

// 2、阻止传播
btn.onclick = function(event) { 
 console.log("Clicked"); 
 event.stopPropagation(); // 阻止了父组件的事件执行
}; 

// 父组件注册了事件,但由于被子元素阻止,不会执行
document.body.onclick = function(event) { 
 console.log("Body clicked"); // 不会执行
};

=============

事件类型

参考:https://developer.mozilla.org/zh-CN/docs/Web/Events

1、用户界面事件

用户界面事件或 UI 事件不一定跟用户操作有关。

load 加载

load 事件可能是 JavaScript 中最常用的事件。

window 对象
在 window 对象上,load 事件会在整个页面(包括所有外部资源如图片、JavaScript 文件和 CSS 文件)加载完成后触发。

一般来说,任何在 window 上发生的事件,都可以通过给元素上对应的属性赋值来指定,这是因为 HTML 中没有 window 元素。

img 对象
图片上也会触发load事件,包括DOM中的图片和非DOM中的图片。

<img src="smile.gif" onload="console.log('Image loaded.')">
let image = document.getElementById("myImage"); 
image.addEventListener("load", (event) => { 
  console.log(event.target.src); 
});

使用新 Image 对象实现了图片预加载:

window.addEventListener("load", () => { 
 let image = new Image(); 
 image.addEventListener("load", (event) => { 
   console.log("Image loaded!"); 
 }); 
 image.src = "smile.gif"; 
});

script 元素