JavaScript与HTML的交互就是通过事件进行,事件指定在浏览器窗口或者文件中发生的时刻,事件可以被监听者订阅,其执行只能在事件发生时,事件API增添在三级DOM上。
事件流
事件流描述的是事件接收顺序,IE浏览器的事件流是冒泡事件,网景是事件捕获机制(这俩的浏览器,懂的都懂)。
DOM事件流
事件流在二级DOM被指定了三个阶段,事件捕获阶段、目标阶段、事件冒泡阶段
第一阶段:事件捕获,提供必要拦截事件的机会
第二阶段:实际目标接收事件
第三阶段:事件冒泡,允许事件的最后响应
事件处理器
事件行为是由用户发出或者浏览器自身,这些事件有点击、加载和鼠标移动,一种函数在事件响应时被调用,被叫做事件处理器,通常以on开头
HTML事件处理器
每个事件都支持被HTML元素赋值到事件处理器属性上,并且属性值可以被JavaScript代码执行,例如
<input type="button" value="Click Me" onclick="console.log('Clicked')"/>
当点击按钮,被赋值的事件就会执行。
注意:由于JavaScript这种情况下是作为HTML标签的属性,使用特殊符号需要记得转义
HTML的事件处理会存在的弊端:
1、HTML元素出现在页面并且与用户交互可能早于事件处理器的准备完成
2、作用域链的增加,可能导致事件处理器的在不同的浏览器得到的结果不同
3、写在标签内部会使JavaScript和HTML绑定在一起,修改起来非常不方便
零级DOM的事件处理器
零级DOM的事件处理器可以通过属性设置,例如:
let btn = document.getElementById("myBtn");
btn.onclick = function() {
console.log("Clicked");
}
二级DOM事件处理器
二级DOM定义了两个有关事件处理器方法,addEventListener()和removeEventListener(),这两个方法存在于所有DOM节点上,可以传递三个参数:name、处理器、处理方法。例如:
let btn = document.getElementById("myBtn");
btn.addEventListener("click", () => {
console.log(this.id);
}, false)
这种二级DOM写法的好处是,可以添加事件处理器,同时也可以修改相应的处理方法。
事件对象
当与DOM关联的事件被触发,所有有关的信息都将被集合和存储起来,叫做事件对象。这个对象包含基本的信息如:元素的事件、事件触发和一些其他数据。
DOM事件对象
在兼容DOM的浏览器中,事件对象只允许传入一个事件处理器参数,在事件处理器的内部对象与currentTarget值相等,目标只包含这个目标事件,如果事件处理器直接分配给预期目标,那么this、currentTarget、target的值都一样,简单测一下
let btn = document.getElementById("myBtn");
btn.onclick = function(event) {
console.log(event.currentTarget === this);
console.log(event.target === this);
};
事件类型
UserInterface:用户交互BOM,发生在浏览器
Focus events:焦点事件,获得或者失去焦点时触发
Mouse events:鼠标事件,鼠标移动到指定位置,或者移开时触发
Wheel events:鼠标滚轮滚动时触发
Text events:在文件中输入文字时触发
Keyboard events:使用键盘操作页面时触发
Composition events:输入法组合输入时触发的组合事件
UI Events
UI事件是这些事件中不可缺少的用户行为事件类型,存在于表单中或者其他的特殊DOM操作中,主要包含以下:
DOMActive:元素在用户行为下激活时触发,在三级DOM已经弃用
load:页面在窗口上加载完成时触发,frame加载完成时触发,元素加载完成时触发,加载完成时触发
unload:与刚才load这部分反过来,都卸载完成时触发
abort:在没有完全加载完成时,用户取消下载的情况下触发
error:在加载元素时出错触发
select:在用户选择一个或者多个字符时触发
resize:调整窗口尺寸时触发
scroll:在滚动页面时触发
以上这些除了DOMActive都是二级DOM中的事件。
load事件
load事件是JavaScript中最常用的事件,对于window来说页面加载完成时load事件就会被触发,包括外部资源,如:JavaScript文件,CSS样式或者图片。
window.addEventListener("load", (event) => {
console.log("Loaded!");
});
(其他部分就不记录了,用到再查API,太多写完也忘)
内存和性能
在JavaScript中处理事件的数量直接关系到页面的性能,主要有以下两个原因
1、每个函数都是对象会放到内存中,内存中对象越多性能越差
2、大量的DOM操作赋值给事件处理器,会影响交互
需要通过以下几种方法提高性能
事件代理
这个解决方案是去解决过多的事件处理器,事件代理机制利用事件冒泡管理单个事件,有助于管理所有特定类型的事件。
举例:当HTML中是列表结构,点击事件应该绑定在list上而不是item上,事件应该绑定在DOM树的最高层,item是list的子元素,通过事件冒泡向上响应,从而减少了每次都要绑定在item上,提高了事件处理的性能。
绑定事件处理到document上进行事件代理,会有以下几种优势:
1、document对象可以立刻分配事件处理器在页面的任何生命周期中,这意味着,可点击元素一被渲染就可以正常工作,没有延迟
2、在页面上花费更少的时间设置事件处理器,分配一个事件处理器会取更少的DOM引用,节省时间
3、页面使用更少的内存
移除事件处理器
当出现长时间不使用的事件处理器,移除这些长时间不使用的事件处理器,非常有助于提升性能,如果持续挂载大量的时间处理器会占用大量内存,严重影响内存。
模拟事件
事件的的触发通常需要用户交互进行触发,而事实上,一些特殊的事件,在JavaScript中可以在任何时候触发。三级DOM规范了模拟特定类型事件的方式。
DOM事件模拟
一个事件对象可以通过createEvent()方法在文件中的任何时候被创建出来,创建之后就需要设置初始化信息,模拟事件最后一步就是调用dispatchEvent()方法,在此之后事件就变成官方的了,可以冒泡了。