JavaScript事件
搬运自个人博客:JavaScript事件
注册事件(绑定事件)
给元素添加事件,称为注册事件或者绑定事件。
有传统方式和方法监听方式
传统方式
利用on开头的事件,如onclick, 同一个元素同一个事件只能设置一个处理函数,出现多个处理函数的话,后面的会覆盖前面的。
例子:
<script>
var btn = document.querySelector("button");
btn.onclick = function () {
console.log(1);
};
btn.onclick = function () {
console.log(2);
}
//结果是2
</script>
方法监听注册方式
addEventListener():
eventTarget.addEventListener(type, listener[, useCapture])
type: 事件类型字符串,如click、mouseover等,不带on
listener: 事件处理函数,事件发生会调用该监听函数
useCapture: 可选参数,是一个布尔值,默认是false。决定监听器的触发阶段是捕获阶段还是冒泡阶段详见。
addEventListener() 是 W3C DOM 规范中提供的注册事件监听器的方法。
优点:
- 允许给一个事件注册多个监听器
例子:
addEventListener:
const btn = document.getElementById("btn");
btn.addEventListener("click", function () {
console.log(1);
});
btn.addEventListener("click", function () {
console.log(1);
});
//结果:1, 1
此处是个人见解:
当两个监听函数一样时,由上可发现会输出两次1,这个其实是因为上面两个匿名函数看似一样,实际它们所开辟的内存空间不一样。
把相同的方法抽出来后会发现,无法实现多个监听,就是因为两个方法变成完全一样了。
const btn = document.getElementById("btn");
btn.addEventListener("click", fn)
btn.addEventListener("click", fn)
function fn() {
console.log(1);
}
//结果:1
- 可以控制监听器的触发阶段(可选捕获或冒泡)
- 对任何 DOM 元素都是有效的,而不仅仅只对 HTML 元素有效。(未找到合适案例)
参数是匿名函数和是箭头函数区别:
它们绑定不同的this对象。匿名函数和传统方式一样会创建独有的this对象(即触发事件的元素),而箭头函数是继承绑定它所在函数的this对象。
例子:
匿名函数:
<div id="div">
<button id="btn">点击</button>
</div>
<script>
const div = document.getElementById("div");
const btn = document.getElementById("btn");
div.addEventListener("mouseenter", function () {
console.log(this);
btn.addEventListener("click", function () {
console.log(this);
})
});
</script>
结果:
箭头函数:
<div id="div">
<button id="btn">点击</button>
</div>
<script>
const div = document.getElementById("div");
const btn = document.getElementById("btn");
div.addEventListener("mouseenter", function () {
console.log(this);
btn.addEventListener("click", () => {
console.log(this);
})
});
</script>
结果:
attachEvent():
eventTarget.attachEvent(eventNameWithOn, callback)
eventNameWithOn: 事件类型字符串。如onclick、onmouseover,要带on
callback: 事件处理函数,事件发生会调用该回调函数
IE9之前的IE不支持,对应有attachEvent(),用法和addEventListener 近似,不过事件需要变回传统方式的on系列。
attachEvent缺点:this的值会变成window对象的引用而不是触发事件的元素。
删除事件(解绑事件)
传统方式
eventTarget.onclick = null;
例子:
const btn = document.getElementById("btn");
btn.onclick = function () {
console.log(1);
};
btn.onclick = null;
方法监听注册方式
对应addEventListener
eventTarget.removeEventListener(type, listener[, useCapture]);
移除完全匹配的监听,只有事件处理函数完全一样,包括开辟的内存空间。
完全匹配例子:
const btn = document.getElementById("btn");
btn.addEventListener("click", fn); //第二个参数只要函数名就可以,不需要调用
btn.removeEventListener("click", fn);
function fn() {
console.log(1);
}
不完全匹配例子(开辟的内存空间不一样):
const btn = document.getElementById("btn");
btn.addEventListener("click", function () {
console.log(1);
});
btn.removeEventListener("click", function () {
console.log(1);
});
DOM事件流
事件流描述的是从页面中接收事件的顺序。
事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程就是DOM事件流。
例如给一个div注册了事件:
DOM事件流分为3个阶段:
- 捕获阶段
- 当前目标阶段
- 冒泡阶段
事件捕获:网景最早提出,由DOM最顶层节点开始,然后逐级向下传播到绑定事件的元素接受的过程。
事件冒泡:IE最早提出,事件逐级向上传播到DOM最顶层节点的过程。
事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即DOM事件流。
注意:
- JS代码只能执行捕获或者冒泡其中一个阶段
- onclick和attachEvent只能得到冒泡阶段
const btn = document.getElementById("btn");
btn.onclick = () => alert(1);
document.body.onclick = () => alert(2);
document.documentElement.onclick = () => alert(3);
document.onclick = () => alert(4);
//点击按钮后,弹出顺序1、2、3、4
- addEventListener(type, listener[, useCapture])第三个参数默认是false,表示在冒泡阶段调用事件处理程序,如果是true,则表示在事件捕获阶段调用事件处理程序。
为false:
const btn = document.getElementById("btn");
btn.addEventListener("click", () => alert(1), false);
document.body.addEventListener("click", () => alert(2), false);
document.documentElement.addEventListener("click", () => alert(3));
document.addEventListener("click", () => alert(4));
//点击按钮后,弹出顺序1、2、3、4
为true:
const btn = document.getElementById("btn");
btn.addEventListener("click", () => alert(1), true);
document.body.addEventListener("click", () => alert(2), true);
document.documentElement.addEventListener("click", () => alert(3), true);
document.addEventListener("click", () => alert(4), true);
//点击按钮后,弹出顺序4、3、2、1
- 有些事件是没有冒泡的,如blur, focus, mouseenter, onmouseleave
例如:
const btn = document.getElementById("btn");
btn.addEventListener("mouseleave", () => alert(1));
document.body.addEventListener("mouseleave", () => alert(2));
document.documentElement.addEventListener("mouseleave", () => alert(3));
document.addEventListener("mouseleave", () => alert(4));
//当鼠标放在按钮里后,离开按钮,只会弹出1
- 事件冒泡有时候会带来麻烦,可以通过 e.stopPropagation()方法阻止事件冒泡
事件对象
事件处理函数可以带参数,带的参数就是事件对象。
eventTarget.onclick = function(event) {}
eventTarget.addEventListener("click", function(event){})
如上式所示,event就是事件对象,,它代表事件的状态,如键盘按键的状态、鼠标的位置、鼠标按钮的状态等。事件发生后,跟事件相关的一系列信息的集合都在这个对象里面。
不需要传递实参
注册事件时,event对象会被系统自动创建,并依次传递给事件监听器(事件处理函数)。
在IE6~8中,浏览器不会给方法传递参数,需要的话,要到window.event中获取。
e = e || window.event;
事件对象的常见属性和方法
e.target和this的区别:
this是事件绑定的元素(匿名函数形式),函数的调用者。
e.target是事件触发的元素。
例子:
const ul = document.querySelector("ul");
ul.addEventListener("click", function (e) {
console.log(this);
console.log(e.target);
});
//点击li里面的文字,依次打印出的是ul和li
阻止默认行为
e.preventDefault()
例子:
const a = document.querySelector("a");
a.addEventListener("click", (e) => e.preventDefault());
阻止事件冒泡
e.stopPropagation()
例子:
const btn = document.getElementById("btn");
btn.addEventListener("click", (e) => {
alert(1);
e.stopPropagation(); //只要在事件处理函数里使用就会执行完这个函数,才阻止冒泡。
//不过建议放在最后面,更有逻辑性的感觉
});
document.body.addEventListener("click", () => alert(2));
document.documentElement.addEventListener("click", () => alert(3));
document.addEventListener("click", () => alert(4));
没加e.stopPropagation()之前会依次弹出1、2、3、4,在按钮绑定的事件中,加上之后只会弹出1
事件委托
事件委托也被称为事件代理,在jQuery里面称为事件委派。
事件委托原理
不需要给每个子结点单独设置事件监听器,而是把事件监听器设置在其父节点上,然后利用冒泡原理去影响子节点。
例子:
const ul = document.querySelector("ul");
ul.addEventListener("click", (e) => e.target.style.color = "red");
上面例子:直接给li的父节点绑定监听器,然后利用e.target找到当前点击的li,点击li,事件会冒泡到ul上,而ul上有注册事件,就会触发事件监听器。
作用
只需要操作一次DOM,提高了程序性能。
常用的鼠标事件
ontextmenu:鼠标右键菜单,可用于取消默认的菜单
selectstart:开始选中,可用于禁止选中文字
常用鼠标事件对象属性
案例
跟随鼠标的天使
常用的键盘事件
onkeypress不识别功能键,如ctrl、shift等
执行顺序是: keydown-->keypress-->keyup
首先,keyup是弹起时才会触发的,所以顺序是最后的,所以只需要记得keydown优先级更高就行
document.addEventListener("keyup", () => console.log("up"));
document.addEventListener("keydown", () => console.log("down"));
document.addEventListener("keypress", () => console.log("press"));
//按非功能键,依次输出顺序down、press、up
//按功能键,则依次输出down、up
常用键盘事件对象属性
keyCode:返回该键的ASCII值(数字)
onkeydown和onkeyup不区分字母大小写,onkeypress区分字母大小写
案例
模拟京东快递单号查询案例