一、DOM
DOM 是 JavaScript 操作网页的接口,它的作用是将网页转为一个 JavaScript 对象,从而可以用脚本进行各种操作。DOM 操作是 JavaScript 最常见的任务,离开了 DOM,JavaScript 就无法控制网页。
1. 节点、节点树
DOM 的最小组成单位叫做节点,节点的类型有:Document- 整个文档树的顶层节点,Element-<body><a>等html标签,...
一个html文档的所有节点,按照所在的层级可以抽象成一种树状结构,这种树状结构就是 DOM 树。
浏览器原生提供 document 节点,代表整个文档。
2. 常见 DOM 操作
2.1 获取/查找 dom 元素的方式
document.getElementById('id属性值'); 返回拥有指定id的对象的引用
document.getElementsByClassName('class属性值'); 返回拥有指定class的对象集合
document.getElementsByTagName('标签名'); 返回拥有指定标签名的对象集合
document.getElementsByName('name属性值'); 返回拥有指定名称的对象结合
2.2 dom节点有哪些操作方法
- 创建
const divEl = document.createElement("div");
const textEl = document.createTextNode("content"); // 文本节点
- 添加、移除、替换、插入
appendChild(node)
removeChild(node)
replaceChild(new,old)
insertBefore(new,old)
二、事件
事件是程序各个组成部分之间的一种通信方式,也是异步编程的一种实现。
DOM 节点的事件操作(监听和触发),都定义在 EventTarget 接口,所有节点对象都部署了这个接口。该接口主要提供三个实例方法:
addEventListener():绑定事件的监听函数。removeEventListener():移除事件的监听函数。dispatchEvent():触发事件。
1. EventTarget.addEventListener()
在当前节点上定义一个特定事件的监听函数,一旦事件发生,就会执行监听函数,该方法没有返回值。
target.addEventListener(type, listener[, useCapture]);
共有三个参数:
type:事件名称,大小写敏感。listener:监听函数。事件发生时,会调用该监听函数。useCapture:可选,返回布尔值。默认false表示监听函数只在冒泡阶段被触发,如果设为true则表示监听函数将在 捕获(capture) 阶段触发。【常考】
function hello() {
console.log('Hello world');
}
var button = document.getElementById('btn');
button.addEventListener('click', hello, false);
// button 节点绑定了 click 事件的监听函数 hello(),该函数只在冒泡阶段触发
(1)添加的监听函数不能是箭头函数。(监听函数内部的this指向触发事件的那个元素节点)dom事件触发时,预期监听函数的this指向该 dom,但会出现this指向window。
(2)另外,第三个参数 useCapture还可以是一个对象,用来定制事件监听行为,该对象的属性有:
- capture:和原来的
useCapture含义相同。 - once:默认false,如果设为 true ,表示监听函数执行一次就会自动移除,后面将不再监听该事件。
- passive:如果设为true,表示禁止监听函数调用
preventDefault()方法。 - signal:在需要时发出信号,移除监听函数。
element.addEventListener('click', function (event) {
// 只执行一次的代码
}, {once: true});
addEventListener 可以给一个事件添加多个不同的监听函数,这些函数按照顺序触发、先添加先触发。如果是加多次相同的监听函数,那么该函数只会执行一次,多余的会自动去除。
2. EventTarget.removeEventListener()
用于移除addEventListener()方法添加的事件监听函数,该方法的参数、返回值和 addEventListener() 相同。
注意,removeEventListener()方法移除的监听函数,必须是addEventListener()方法添加的那个监听函数(包括第三个参数),而且必须在同一个元素节点,否则无效。
3. EventTarget.dispatchEvent()
EventTarget.dispatchEvent()方法在当前节点上触发指定事件,从而触发监听函数的执行。该方法返回一个布尔值,只要有一个监听函数调用了Event.preventDefault(),则返回值为false,否则为true。
para.addEventListener('click', hello, false);
var event = new Event('click');
para.dispatchEvent(event);
三、浏览器的事件模型
浏览器的事件模型,就是通过监听函数对事件作出反应。事件发生后,浏览器监听到了这个事件,就会执行相应的监听函数。
1. 给事件绑定监听函数的三种方式
1.1 html 的 on 属性:只在冒泡阶段触发
在html元素的属性中,直接定义一些事件的监听代码,注意,这些属性的值是将会执行的代码,而不是一个函数。
<body onload="doSomething()">
<div onclick="console.log('触发事件')">
不建议使用。
1.2 元素节点的事件属性:只在冒泡阶段触发
window.onload = doSomething;
div.onclick = function (event) {
console.log('触发事件');
};
与前者的区别是,它的值是函数名,不像on属性需要是完整的监听代码。
重复定义会覆盖,也就是说,只能定义一个监听函数。
1.3 EventTarget.addEventListener()
详细参考上文。
与其他两种方法相比:
- 可以指定触发的阶段:捕获或冒泡。(其他两个只能冒泡)
- 同一个事件可以添加多个监听函数。(元素节点.事件属性 只能定义一个监听函数)
- EventTarget在所有节点都部署了,方便统一操作。
2. 事件的传播(捕获冒泡流程)
一个事件发生后,会在子元素和父元素之间传播。分成三个阶段:
- 第1是捕获阶段:事件从
window对象传导到目标节点,依次检查经过的节点是否绑定了该事件监听函数,并且设定在捕获阶段执行,那么就会执行该函数。(从window开始检查!) - 第2是目标/处理阶段:在目标节点上触发。
- 第3是冒泡阶段:从目标节点传导会
window对象,反向再检查一次。
现代浏览器,默认所有事件处理程序都在冒泡阶段执行!
这样的事件传播模型,会使得同一个事件会在多个节点上触发,如下:
<div>
<p>点击</p>
</div>
div.addEventListener('click', callback, true);
p.addEventListener('click', callback, true);
div.addEventListener('click', callback, false);
p.addEventListener('click', callback, false);
// 点击以后的结果
// Tag: 'DIV'. EventPhase: 'capture'
// Tag: 'P'. EventPhase: 'target'
// Tag: 'P'. EventPhase: 'target'
// Tag: 'DIV'. EventPhase: 'bubble'
捕获阶段,触发了div的 click 事件;目标阶段,触发了两次p的 click 事件;冒泡阶段,触发了div的 click 事件。
事件传播的最上层对象是 window,顺序依次是 window、document、html、body、div、p。浏览器总是假定click事件的目标节点,就是点击位置嵌套【最深】的那个节点。
3. 事件的代理/委托:重点
背景: 事件捕获和事件冒泡分别由网景和微软公司提出,目前事件冒泡的事件流模型被所有主流的浏览器兼容。
事件的代理:利用浏览器事件冒泡的机制,由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。(<ul> 和 <li>)
- 优点
- 减少内存消耗,不必为大量元素绑定事件。
- 对于经常增删的子节点,不需要每次重新绑定事件,直接绑在了父节点。
- 适用场景
- 只适用于支持事件冒泡的事件,对于自定义事件、focus(获取到焦点)、blur(失去焦点)事件不支持冒泡的,不能使用事件委托。
- 频繁触发的事件不适合事件委托
4. 阻止事件的传播
- 阻止事件传播 stopPropagation 事件到某个节点为止,不再传播了。这种方式只能阻止传播,不是彻底取消该事件。
// 事件冒泡到 p 元素后,就不再向上冒泡了
p.addEventListener('click', function (event) {
event.stopPropagation();
}, false);
- 彻底取消某事件 stopImmediatePropagation 阻止事件传播后,p节点的第2个click监听函数还是会执行,输出1 和 2。
p.addEventListener('click', function (event) {
event.stopPropagation();
console.log(1);
});
p.addEventListener('click', function(event) {
// 会触发
console.log(2);
});
使用 stopImmediatePropagation 后会彻底取消该事件,只会输出1。
p.addEventListener('click', function (event) {
event.stopImmediatePropagation();
console.log(1);
});
p.addEventListener('click', function(event) {
// 不会被触发
console.log(2);
});
四、三级事件模型
-
DOM0 事件模型:通过
html的on属性或元素节点的事件属性来绑定事件,没有事件流的概念。所有浏览器都兼容。 -
IE 事件模型:通过
attachEvent绑定事件,事件流有 事件处理、事件冒泡 两个阶段。只在IE浏览器有效,不兼容其他的。 -
DOM2 事件模型:通过
addEventListener来绑定事件,事件流有 事件捕获、事件处理、事件冒泡 三个阶段。