文章目录
DOM(Document Object Moel)是js操作HTML的api,全称文档对象模型。
- 文档:整个html网页文档
- 对象:将网页中的每一部分都转换成一个对象
- 模型:使用模型表示对象之间的关系,方便获取对象
节点
节点的类型有七种:Document、DocumentType、Element、Text、Comment、DocumentFragment。
-
常用节点
文档节点(document):
整个HTML文档document对象作为window对象的属性存在的,我们不用获取可以直接使用。
元素节点(Element):HTML文档中的HTML标签。
属性节点(Attribute):
元素的属性 表示的是标签中的一个一个的属性,这里要注意的是属性节点并非是元素节点的子节点,而是元素节点的一部分。
文本节点(Text):HTML标签中的文本内容。
- 其他节点
DocumentType:doctype标签(比如<!DOCTYPE html>
)。
Comment:注释
DocumentFragment:文档的片段
节点树
除了根节点以外,其他节点对于周围的节点都存在三种关系。
-
父节点关系(parentNode):直接的那个上级节点。
-
子节点关系(childNode):直接的下级节点。
-
同级节点关系(sibling):拥有同一父节点的节点。
DOM提供操作接口,用来获取三种关系的节点。其中,子节点接口包括firstChild
(第一个子节点)和lastChild
(最后一个子节点)等属性,同级节点接口包括nextSibling
(紧邻在后的那个同级节点)和previousSibling
(紧邻在前的那个同级节点)属性。
Node类型的属性和方法
属性
- nodeName:
nodeName属性返回节点的名称
//获取body节点
var body=document.body;
//获取节点名称 返回纯大写节点名称
console.log(body.nodeName)
- nodeValue:
nodeValue属性返回一个字符串,表示当前节点本身的文本值,该属性可读写只有文本节点(text)、注释节点(comment)和属性节点(attr)有文本值.
<div id="d1">hello world</div>
<script>
var div = document.getElementById('d1');
console.log(div.nodeValue); // null
// 读
console.log(div.firstChild.nodeValue); //hello world
// 写
div.firstChild.nodeValue = '123';
</script>
- textContent:
textContent属性返回当前节点和它的所有后代节点的文本内容:包括换行和空格
<div id="d1">Hello <span>JavaScript</span> DOM</div>
<script>
var div = document.getElementById('d1');
console.log(div.textContent); //Hello JavaScript DOM
</script>
- nextSibling:
nextSibling属性返回紧跟在当前节点后面的第一个同级节点。如果当前节点后面没有同级节点,则返回null
(注意:可能会获取到“空格”或“回车”这样的文本节点)
<div id="d1">hello</div><div id="d2">world</div>
<script>
var div1 = document.getElementById('d1');
var div2 = document.getElementById('d2');
console.log(div1.nextSibling); //<div id="d2">world</div>
console.log(div1.nextSibling === div2); // true
</script>
- previousSibling:
previousSibling属性返回当前节点前面的、距离最近的一个同级节点。如果当前节点前面没有同级节点,则返回null
<div id="d1">hello</div><div id="d2">world</div>
<script>
var div1 = document.getElementById('d1');
var div2 = document.getElementById('d2');
console.log(div2.previousSibling); //<div id="d1">hello</div>
console.log(div2.previousSibling === div1); // true
</script>
- parentNode:
parentNode属性返回当前节点的父节点。对于一个节点来说,它的父节点只可能是三种类型:元素节点(element)、文档节点(document)和文档片段节点(documentfragment)
<div id="d1">hello world</div>
<script>
var div1 = document.getElementById('d1');
console.log(div1.parentNode); // body
</script>
- parentElement:
parentElement属性返回当前节点的父元素节点。如果当前节点没有父节点,或者父节点类型不是元素节点,则返回null
<div id="d1">hello world</div>
<script>
var div1 = document.getElementById('d1');
console.log(div1.parentElement); // body
// 将父元素节点的背景颜色设置为红色
div1.parentElement.style.backgroundColor = 'red';
</script>
- firstChild和lastChild:
firstChild属性返回当前节点的第一个子节点,如果当前节点没有子节点,则返回null,last则返回最后一个子节点。
<div id="d1">hello world<div>我是子节点</div></div>
<div id="d2"><div>我是子节点</div></div>
<script>
var div1 = document.getElementById('d1');
console.log(div1.firstChild); // hello world
console.log(div1.lastChild); // <div>我是子节点</div>
var div2 = document.getElementById('d2');
console.log(div2.firstChild); // <div>我是子节点</div>
</script>
- childNodes:
childNodes属性返回一个类似数组的对象(NodeList集合),成员包括当前节点的所有子节点
<div id="d1">hello world<div>我是子节点</div></div>
<script>
var div1 = document.getElementById('d1');
console.log(div1.childNodes); //NodeList[text, div]
</script>
注意: div1 得到的是一个类数组对象
操作方法
注意:以下四个方法都需要父节点对象进行调用!
- appendChild()
appendChild方法接受一个节点对象作为参数,将其作为最后一个子节点,插入当前节点。该方法的返回值就是插入文档的子节点。
<script>
// 创建元素节点p
var p = document.createElement('p');
// 向p标签插入内容
p.innerHTML = '我是一个p标签';
// 将节点插入到body中
document.body.appendChild(p);
</script>
- insertBefore()
insertBefore方法用于将某个节点插入父节点内部的指定位置。
insertBefore(newNode, referenceNode)
insertBefore方法接受两个参数:
第一个参数是所要插入的节点newNode;
第二个参数是父节点parentNode内部的一个子节点referenceNode。newNode将插在referenceNode这个子节点的前面。返回值是插入的新节点newNode
<div id="parentElement">
<span id="childElement">foo bar</span>
</div>
<script>
//创建一个新的、普通的<span>元素
var sp1 = document.createElement("span");
// 向span标签插入内容
sp1.innerHTML = '我是span标签'
//插入节点之前,要获得节点的引用
var sp2 = document.getElementById("childElement");
//获得父节点的引用
var parentDiv = sp2.parentNode;
//在DOM中在sp2之前插入一个新元素
parentDiv.insertBefore(sp1, sp2);
</script>
- removeChild()
removeChild方法接受一个子节点作为参数,用于从当前节点移除该子节点。返回值是移除的子节点。
<div id="d1">
<span id="s1">我是span标签</span>
</div>
<script>
var span1 = document.getElementById('s1');
span1.parentNode.removeChild(span1);
</script>
- replaceChild()
replaceChild方法用于将一个新的节点,替换当前节点的某一个子节点。
parentNode.replaceChild(newChild, oldChild);
replaceChild方法接受两个参数:
第一个参数newChild是用来替换的新节点;
第二个参数oldChild是将要替换走的子节点。返回值是替换走的那个节点oldChild。
<div id="d1">
<span id="s1">我是span标签</span>
</div>
<script>
var span1 = document.getElementById('s1');
//创建一个新的div标签
var div1 = document.createElement("div");
// 向div标签插入内容
div1.innerHTML = '我是div1标签';
// 节点替换
span1.parentNode.replaceChild(div1, span1);
</script>
其他方法:
cloneNode() :复制节点 有参数true代表深复制
//浅复制 只复制节点 不复制内容
var cloneTwo=two.cloneNode(); parent.appendChild(cloneTwo);
// 深复制 复制节点和内容
var cloneTwo=two.cloneNode(true); parent.appendChild(cloneTwo);
Document类型
Javascript通过使用Document类型表示文档。在浏览器中,document对象是HTMLDocument的一个实例,表示整个HTML页面。document对象是window对象的一个属性,因此可以直接调用。HTMLDocument继承自Document。
Document类型的属性和方法
属性
- documentElement 始终指向HTML页面中的元素。
body 直接指向body元素 - doctype 访问<!DOCTYPE>, 浏览器支持不一致,很少使用
- title 获取文档的标题
- URL 取得完整的URL
- domain 取得域名,并且可以进行设置,在跨域访问中经常会用到。
- referrer 取得链接到当前页面的那个页面的URL,即来源页面的URL。
- images 获取所有的img对象,返回HTMLCollection类数组对象
- forms 获取所有的form对象,返回HTMLCollection类数组对象
- links 获取文档中所有带href属性的元素
查找元素的方法
方法 | 描述 |
---|---|
document.getElementById(id) | 通过元素 id 来查找元素 |
document.getElementsByTagName(name) | 通过标签名来查找元素 |
document.getElementsByClassName(name) | 通过类名来查找元素 |
document.querySelector() | 返回文档中匹配指定的CSS选择器的第一元素 |
document.querySelectorAll() | document.querySelectorAll() 是 HTML5中引入的新方法,返回文档中匹配的CSS选择器的所有元素节点列表 |
<div id="d1">我是一个div标签</div>
<script>
// 查找id为d1的标签
var div = document.getElementById('d1');
console.log(div);
</script>
<p>我是p标签</p>
<p>我是p标签</p>
<p>我是p标签</p>
<script>
// 查找所有p标签
var p = document.getElementsByTagName('p');
console.log(p);
</script>
<div class="div1">我是div标签</div>
<div class="div1">我是div标签</div>
<div class="div1">我是div标签</div>
<script>
// 查找class为div1的标签
var div = document.getElementsByClassName('div1');
console.log(div);
</script>
<div id="div1">我是一个div</div>
<div id="div1">我是一个div</div>
<script>
document.querySelector("#div1").innerHTML = "Hello World!";
</script>
<div class="div1">我是一个div</div>
<div class="div1">我是一个div</div>
<script>
console.log(document.querySelectorAll(".div1"));
var x = document.querySelectorAll(".div1");
x[0].innerHTML = '我是新的div';
</script>
添加元素
document.createElement(element)
<script>
// 创建元素节点p
var p = document.createElement('p');
// 向p标签插入内容
p.innerHTML = '我是一个p标签';
// 将节点插入到body中
document.body.appendChild(p);
</script>
Element类型
Element类型的属性和方法
属性
-
attributes:返回一个与该元素相关的所有属性的集合。
-
classList:返回该元素包含的 class 属性的集合。
-
className:获取或设置指定元素的 class 属性的值。
-
clientHeight:获取元素内部的高度,包含内边距,但不包括水平滚动条、边框和外边距。
-
clientTop:返回该元素距离它上边界的高度。
-
clientLeft:返回该元素距离它左边界的宽度。
-
clientWidth:返回该元素它内部的宽度,包括内边距,但不包括垂直滚动条、边框和外边距。
-
innerHTML:设置或获取 HTML 语法表示的元素的后代。
-
tagName:返回当前元素的标签名。
方法
方法 | 描述 |
---|---|
element.innerHTML = new html content | 改变元素的 innerHTML |
element.attribute = value | 修改属性的值 |
element.getAttribute() | 返回元素节点的指定属性值。 |
element.setAttribute(attribute, value) | 设置或改变 HTML 元素的属性值 |
element.style.property = new style | 改变 HTML 元素的样式 |
- element.innerHTML
属性设置或获取HTML语法表示的元素的后代。
<div id="div1">我是一个div</div>
<script>
var d1 = document.getElementById('div1');
// 获取
console.log(d1.innerHTML);
// 设置
d1.innerHTML = '我是新的内容'
</script>
- element.attribute = value
修改已经存在的属性的值
<div id="div1">123</div>
<script>
var d1 = document.getElementById('div1');
// 直接将已经存在的属性进行修改
d1.id = 'div2';
</script>
- element.getAttribute()
返回元素节点的指定属性值。
<div id="div1">我是一个div</div>
<script>
var div = document.getElementById('div1');
console.log(div.getAttribute('id')); // div1
</script>
- element.setAttribute(attribute, value)
把指定属性设置或更改为指定值。
<div id="div1">我是一个div</div>
<script>
var d1 = document.getElementById('div1');
// 设置div1的class为divCla
d1.setAttribute('class', 'divCla');
</script>
- element.style.property
设置或返回元素的 style 属性。
<div id="div1">我是一个div</div>
<script>
var d1 = document.getElementById('div1');
// 获取div1的style样式
console.log(d1.style);
// 设置div1的style
d1.style.backgroundColor = 'red';
</script>
Text类型
Text类型的属性和方法
- length
文本长度
- appendData(text)
追加文本
- deleteData(beginIndex,count)
删除文本
- insertData(beginIndex,text)
插入文本
- replaceData(beginIndex,count,text)
替换文本
- splitText(beginIndex)
从beginIndex位置将当前文本节点分成两个文本节点
- document.createTextNode(text)
创建文本节点,参数为要插入节点中的文本
- substringData(beginIndex,count)
从beginIndex开始提取count个子字符串
<div id="container"></div>
<script>
// 创建文本节点
var textNode = document.createTextNode('Hello World!');
// 获取container
var div = document.getElementById('container');
// 将文本节点插入container
div.appendChild(textNode);
// 替换文本
textNode.replaceData(0,5,'Hi');
// 插入文本
textNode.insertData(0, 'Hello')
</script>
DOM事件
事件:就是用户和浏览器之间的交互行为,比如:点击按钮、鼠标移动、关闭窗口……
事件是由三部分组成
事件源 事件类型 事件处理程序 我们也称为事件三要素
1.事件源:事件被触发的对象 -->按钮对象
2.事件类型:如何触发?触发什么事件?例如鼠标点击,键盘按下等…
3.事件处理程序:通过一个函数赋值的方式
执行事件的步骤
1.获取事件源
2.注册事件(绑定事件)
3.采用函数赋值形式添加事件处理程序
事件流
事件流描述了页面接收事件的顺序;
事件冒泡(IE事件流)
IE 事件流被称为事件冒泡,这是因为事件被定义为从最具体的元素(文档树中最深的节点)开始触发,然后向上传播至没有那么具体的元素(文档)。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
*{
color: white;
font-size: 20px;
}
#outer{
width: 300px;
height: 300px;
background-color: red;
}
#center{
width: 200px;
height: 200px;
background-color: blue;
}
#inner{
width: 100px;
height: 100px;
background-color: green;
}
</style>
</head>
<body>
<div id="outer">outer
<div id="center">center
<div id="inner">inner</div>
</div>
</div>
<script>
var inner = document.getElementById('inner');
var center = document.getElementById('center');
var outer = document.getElementById('outer');
// 当我们只有一个inner点击方法的时候 我们发现想要实现的效果和我们预期的一样
inner.onclick = function () {
console.log('我是inner点击的');
}
// 但是当我们给inner的父元素和祖先元素也添加点击事件时 一点击inner 所有祖先元素的事件都会被触发,这就是事件冒泡现象
center.onclick = function () {
console.log('我是center点击的');
}
outer.onclick = function () {
console.log('我是outer点击的');
}
</script>
</body>
</html>
在点击页面中的id为inner的div元素,click事件会以如下顺序发生
- div#inner
- div#center
- div#outer
- body
- html
- document
div#inner元素,即被点击的元素,最先触发 click 事件。然后,click 事件沿DOM 树一路向上,在经过的每个节点上依次触发,直至到达 document 对象。
阻止事件冒泡:
使用阻止事件冒泡之前,先要知道DOM事件默认提供的一个对象,HTML DOM Event对象。
Event 对象代表事件的状态,比如事件在其中发生的元素、键盘按键的状态、鼠标的位置、鼠标按钮的状态。
事件通常与函数结合使用,函数不会在事件发生前被执行!
event.stopPropagation()
直接在对应方法中使用event.stopPropagation()便可阻止事件冒泡。
注意:如果点击方法时需要同时传递其他参数和event,直接传递event这个单词即可
οnclick="test(123,event)"
1.2.事件捕获(Netscape事件流)
事件捕获的意思是最不具体的节点应该最先收到事件,而最具体的节点应该最后收到事件。事件捕获实际上是为了在事件到达最终目标前拦截事件。如果前面的例子使用事件捕获,则点击div元素会以下列顺序触发 click 事件:
- document
- html
- body
- div
也就是说,div#inner元素,即被点击的元素,最先触发 click 事件。然后,click 事件沿DOM 树一路向上,在经过的每个节点上依次触发,直至到达 document 对象。
通常建议使用事件冒泡
事件流 DOM2 Events 规范规定事件流分为 3 个阶段:事件捕获、到达目标和事件冒泡。事件捕获最先发生,为提前拦截事件提供了可能。然后,实际的目标元素接收到事件。最后一个阶段是冒泡,最迟要在这个阶段响应事件。
事件处理程序
事件处理程序的名字以"on"开头,因此 click 事件的处理程序叫作 onclick,而 load 事件的处理程序叫作 onload。有很多方式可以指定事件处理程序。
HTML 事件处理程序
特定元素支持的每个事件都可以使用事件处理程序的名字以 HTML 属性的形式来指定。此时属性的值必须是能够执行的 JavaScript 代码。例如,要在按钮被点击时执行某些 JavaScript 代码,可以使用以下 HTML 属性:
<button onclick="console.log('Clicked')">点我啊</button>
点击这个按钮后,控制台会输出一条消息。这种交互能力是通过为 onclick 属性指定 JavaScript代码值来实现的。注意,因为属性的值是 JavaScript 代码,所以不能在未经转义的情况下使用 HTML 语法字符,比如和号(&)、双引号(")、小于号(<)和大于号(>)。此时,为了避免使用 HTML 实体,可以使用单引号代替双引号。如果确实需要使用双引号,则要把代码改成下面这样:
<button onclick="console.log("Clicked")">点我啊</button>
在 HTML 中定义的事件处理程序可以包含精确的动作指令,也可以调用在页面其他地方定义的方法。
<!-- 需要加() -->
<button onclick="showMsg()">点我啊</button>
<script>
function showMsg() {
console.log('Hello Wolrd!');
}
</script>
在这个函数中,this 值相当于事件的目标元素,如下面的例子所示:
<button onclick="this.innerHTML = '我被改变了'">点我啊</button>
DOM0 事件处理程序
在 JavaScript 中指定事件处理程序的传统方式是把一个函数赋值给(DOM 元素的)一个事件处理程序属性。这也是在第四代 Web 浏览器中开始支持的事件处理程序赋值方法,直到现在所有现代浏览器仍然都支持此方法,主要原因是简单。要使用 JavaScript 指定事件处理程序,必须先取得要操作对象的引用。每个元素(包括 window 和 document)都有通常小写的事件处理程序属性,比如 onclick。只要把这个属性赋值为一个函数即可:
<button id='btn'>点我啊</button>
<script>
var btn = document.getElementById('btn');
btn.onclick = function () {
console.log('我被点击了');
}
</script>
这里先从文档中取得按钮,然后给它的 onclick 事件处理程序赋值一个函数。注意,前面的代码在运行之后才会给事件处理程序赋值。因此如果在页面中上面的代码出现在按钮之后,则有可能出现用户点击按钮没有反应的情况。
像这样使用 DOM0 方式为事件处理程序赋值时,所赋函数被视为元素的方法。因此,事件处理程序会在元素的作用域中运行,即 this 等于元素。下面的例子演示了使用 this 引用元素本身:
<button id='btn'>点我啊</button>
<script>
var btn = document.getElementById('btn');
btn.onclick = function () {
console.log('我被点击了');
console.log(this.id); //btn
}
</script>
点击按钮,这段代码会显示元素的 ID。这个 ID 是通过 this.id 获取的。不仅仅是 id,在事件处理程序里通过 this 可以访问元素的任何属性和方法。以这种方式添加事件处理程序是注册在事件流的冒泡阶段的。
通过将事件处理程序属性的值设置为 null,可以移除通过 DOM0 方式添加的事件处理程序,如下面的例子所示:
btn.onclick = null; // 移除事件处理程序
把事件处理程序设置为 null,再点击按钮就不会执行任何操作了。
注意:
如果事件处理程序是在 HTML 中指定的,则 onclick 属性的值是一个包装相应HTML 事件处理程序属性值的函数。这些事件处理程序也可以通过在 JavaScript 中将相应属性设置为 null 来移除。
DOM2 事件处理程序
DOM2 Events 为事件处理程序的赋值和移除定义了两个方法:
addEventListener()和removeEventListener()。
这两个方法暴露在所有 DOM 节点上,它们接收 3 个参数:事件名、事件处理函数和一个布尔值,true 表示在捕获阶段调用事件处理程序,false(默认值)表示在冒泡阶段调用事件处理程序。
addEventListener()
仍以给按钮添加 click 事件处理程序为例,可以这样写:
<button id='btn'>点我啊</button>
<script>
var btn = document.getElementById("btn");
btn.addEventListener("click", function () {
console.log('我被点击了');
}, false);
</script>
以上代码为按钮添加了会在事件冒泡阶段触发的 onclick 事件处理程序(因为最后一个参数值为false)。与 DOM0 方式类似,这个事件处理程序同样在被附加到的元素的作用域中运行。使用 DOM2方式的主要优势是可以为同一个事件添加多个事件处理程序。来看下面的例子:
<button id='btn'>点我啊</button>
<script>
var btn = document.getElementById("btn");
btn.addEventListener("click", function () {
console.log('我被点击了');
}, false);
btn.addEventListener("click", function () {
console.log(this.id); // btn
});
</script>
这里给按钮添加了两个事件处理程序。多个事件处理程序以添加顺序来触发,因此前面的代码会先打印’我被点击了’,然后显示元素ID。
removeEventListener()
通过 addEventListener()添加的事件处理程序只能使用 removeEventListener()并传入与添加时同样的参数来移除。这意味着使用 addEventListener()添加的匿名函数无法移除,如下面的例子所示:
<button id='btn'>点我啊</button>
<script>
var btn = document.getElementById("btn");
btn.addEventListener("click", function () {
console.log('我被点击了');
}, false);
// 移除
btn.removeEventListener("click", function () {
console.log(this.id); //没有效果
})
</script>
这个例子通过 addEventListener()添加了一个匿名函数作为事件处理程序。然后,又以看起来相同的参数调用了 removeEventListener()。但实际上,第二个参数与传给 addEventListener()的完全不是一回事。传给 removeEventListener()的事件处理函数必须与传给 addEventListener()的是同一个,如下面的例子所示:
<button id='btn'>点我啊</button>
<script>
var btn = document.getElementById("btn");
// 新增一个方法
var handler = function () {
console.log(this.id);
}
btn.addEventListener("click", handler, false);
// 移除
btn.removeEventListener("click", handler, false); // 有效果
</script>
这个例子有效,因为调用 addEventListener()和 removeEventListener()时传入的是同一个函数。大多数情况下,事件处理程序会被添加到事件流的冒泡阶段,主要原因是跨浏览器兼容性好。把事件处理程序注册到捕获阶段通常用于在事件到达其指定目标之前拦截事件。如果不需要拦截,则不要使用事件捕获。
3.事件对象
在 DOM 中发生事件时,所有相关信息都会被收集并存储在一个名为 event 的对象中。这个对象包含了一些基本信息,比如导致事件的元素、发生的事件类型,以及可能与特定事件相关的任何其他数据。例如,鼠标操作导致的事件会生成鼠标位置信息,而键盘操作导致的事件会生成与被按下的键有关的信息。所有浏览器都支持这个 event 对象,尽管支持方式不同。
详见:https://www.w3school.com.cn/htmldom/dom_obj_event.asp
注意:event 对象只在事件处理程序执行期间存在,一旦执行完毕,就会被销毁。
3.1.阻止默认事件发生
preventDefault()方法
用于阻止特定事件的默认动作。比如,链接的默认行为就是在被单击时导航到 href 属性指定的 URL或是修改表单提交的默认事件。如果想阻止这些行为,可以在 onclick 事件处理程序中取消,如下面的例子所示:
<a href="http://www.baidu.com">跳转</a>
<form action="./1-HTML事件处理程序.html">
<button id="btn">提交按钮</button>
</form>
<script>
var a = document.getElementsByTagName('a')[0];
var btn = document.getElementById('btn');
a.onclick = function (event) {
// alert(1);
// 阻止事件默认行为
event.preventDefault();
}
btn.onclick = function (event) {
// 阻止事件默认行为
event.preventDefault();
}
</script>
4.事件委托
在 JavaScript 中,页面中事件处理程序的数量与页面整体性能直接相关。原因有很多。首先,每个函数都是对象,都占用内存空间,对象越多,性能越差。其次,为指定事件处理程序所需访问 DOM 的次数会先期造成整个页面交互的延迟。只要在使用事件处理程序时多注意一些方法,就可以改善页面性能。
事件委托就是当事件触发时,把要做的事委托给父元素(或父元素的父元素)来处理。也就是:利用冒泡的原理,把事件加到父级上,通过判断事件来源的子集,执行相应的操作。使用事件委托技术能让你避免对特定的每个节点添加事件监听器。
事件委托利用事件冒泡,可以只使用一个事件处理程序来管理一种类型的事件。例如,click 事件冒泡到 document。这意味着可以为整个页面指定一个 onclick 事件处理程序,而不用为每个可点击元素分别指定事件处理程序。比如有以下HTML:
<ul id="myLinks">
<li id="li1">Go somewhere</li>
<li id="li2">Do something</li>
<li id="li3">Say hi</li>
</ul>
这里的 HTML 包含 3 个列表项,在被点击时应该执行某个操作。对此,通常的做法是像这样指定 3个事件处理程序:
<script>
var item1 = document.getElementById("li1");
var item2 = document.getElementById("li2");
var item3 = document.getElementById("li3");
item1.addEventListener('click', function () {
this.innerHTML = 'SuZhou';
})
item2.addEventListener('click', function () {
this.innerHTML = 'Coding';
})
item3.addEventListener('click', function () {
this.innerHTML = 'Hi';
})
</script>
如果对页面中所有需要使用 onclick 事件处理程序的元素都如法炮制,结果就会出现大片雷同的只为指定事件处理程序的代码。使用事件委托,只要给所有元素共同的祖先节点添加一个事件处理程序,就可以解决问题。
比如:
<script>
var list = document.getElementById("myLinks");
list.addEventListener("click", function (event) {
var target = event.target;
console.log(target);
switch (target.id) {
case "li1":
target.innerHTML = 'SuZhou';
break;
case "li2":
target.innerHTML = 'Coding';
break;
case "li3":
target.innerHTML = 'Hi';
break;
}
});
</script>
这里只给
- 元素添加了一个 onclick 事件处理程序。因为所有列表项都是这个元素的后代,所以它们的事件会向上冒泡,最终都会由这个函数来处理。但事件目标是每个被点击的列表项,只要检查 event 对象的 id 属性就可以确定,然后再执行相应的操作即可。相对于前面不使用事件委托的代码,这里的代码不会导致先期延迟,因为只访问了一个 DOM 元素和添加了一个事件处理程序。结果对用户来说没有区别,但这种方式占用内存更少。所有使用按钮的事件(大多数鼠标事件和键盘事件)都适用于这个解决方案。
扩展:
只要可行,就应该考虑只给 document 添加一个事件处理程序,通过它处理页面中所有某种类型的事件。相对于之前的技术,事件委托具有如下优点。
-
document 对象随时可用,任何时候都可以给它添加事件处理程序(不用等待 DOMContentLoaded或 load 事件)。这意味着只要页面渲染出可点击的元素,就可以无延迟地起作用。
-
节省花在设置页面事件处理程序上的时间。只指定一个事件处理程序既可以节省 DOM 引用,也可以节省时间。
-
减少整个页面所需的内存,提升整体性能。
最适合使用事件委托的事件包括:click、mousedown、mouseup、keydown 和 keypress。
5.事件类型
Web 浏览器中可以发生很多种事件。如前所述,所发生事件的类型决定了事件对象中会保存什么信息。DOM3 Events 定义了如下事件类型。
用户界面事件(UIEvent):涉及与 BOM 交互的通用浏览器事件。
焦点事件(FocusEvent):在元素获得和失去焦点时触发。
鼠标事件(MouseEvent):使用鼠标在页面上执行某些操作时触发。
滚轮事件(WheelEvent):使用鼠标滚轮(或类似设备)时触发。
键盘事件(KeyboardEvent):使用键盘在页面上执行某些操作时触发。
输入事件(InputEvent):向文档中输入文本时触发。
用户界面事件-UIEvent
-
load
在 window 上当页面加载完成后触发,在窗套()上当所有窗格()都加载完成后触发,在元素上当图片加载完成后触发,在元素上当相应对象加载完成后触发。
<script> window.onload = function () { console.log('onload'); } </script>
-
unload
当页面完全卸载后在window上触发,当所有框架都卸载后在框架集上触发,当嵌入的内容卸载完毕后在上触发。
-
select
在文本框(或 textarea)上当用户选择了一个或多个字符时触发。
<input type="text" id="inp"> <script> var inp = document.getElementById('inp'); inp.onselect = function (event) { console.log(event); // 可以通过window.getSelection()获取到选中的部分 console.log(window.getSelection().toString()); } </script>
-
resize
在 window 或窗格上当窗口或窗格被缩放时触发。
<body onresize="myFun()"> <script> function myFun() { console.log(window.outerHeight, window.outerWidth); } </script> </body>
-
scroll
当用户滚动包含滚动条的元素时在元素上触发。元素包含已加载页面的滚动条。
大多数 HTML 事件与 window 对象和表单控件有关。
<div id="d1" style="width: 100px;height: 100px;border: 1px solid; overflow: auto;">我是div标签我是div标签我是div标签我是div标签我是div标签我是div标签我是div标签我是div标签我是div标签我是div标签我是div标签</div> <script> var d1 = document.getElementById('d1'); d1.onscroll = function () { console.log('onscroll'); } </script>
###焦点事件-FocusEvent
-
blur
当元素失去焦点时触发。这个事件不冒泡,所有浏览器都支持。
-
focus
当元素获得焦点时触发。这个事件不冒泡,所有浏览器都支持。
<input type="text" id="inp1">
<script>
var inp1 = document.getElementById('inp1');
// 失去焦点触发
inp1.onblur = function () {
console.log('失去焦点');
console.log(this.value);
}
// 获得焦点触发
inp1.onfocus = function () {
console.log('获得焦点');
}
</script>
-
focusin
当元素获得焦点时触发。这个事件是 focus 的冒泡版。
-
focusout
当元素失去焦点时触发。这个事件是 blur 的冒泡版。
鼠标事件-MouseEvent和鼠标滚轮事件-WheelEvent
鼠标事件是 Web 开发中最常用的一组事件,这是因为鼠标是用户的主要定位设备。DOM3 Events定义了 9 种鼠标事件。
-
click
在用户单击鼠标主键(通常是左键)或按键盘回车键时触发。这主要是基于无障碍的考虑,让键盘和鼠标都可以触发 onclick 事件处理程序。
-
dblclick
在用户双击鼠标主键(通常是左键)时触发。这个事件不是在 DOM2 Events 中定义的,但得到了很好的支持,DOM3 Events 将其进行了标准化。
-
mousedown
在用户按下任意鼠标键时触发。这个事件不能通过键盘触发。
-
mouseenter
在用户把鼠标光标从元素外部移到元素内部时触发。这个事件不冒泡,也不会在光标经过后代元素时触发。mouseenter 事件不是在 DOM2 Events 中定义的,而是 DOM3 Events中新增的事件。
-
mouseleave
在用户把鼠标光标从元素内部移到元素外部时触发。这个事件不冒泡,也不会在光标经过后代元素时触发。mouseleave 事件不是在 DOM2 Events 中定义的,而是 DOM3 Events中新增的事件。
-
mousemove
在鼠标光标在元素上移动时反复触发。这个事件不能通过键盘触发。
-
mouseout
在用户把鼠标光标从一个元素移到另一个元素上时触发。移到的元素可以是原始元素的外部元素,也可以是原始元素的子元素。这个事件不能通过键盘触发。
-
mouseover
在用户把鼠标光标从元素外部移到元素内部时触发。这个事件不能通过键盘触发。
-
mouseup
在用户释放鼠标键时触发。这个事件不能通过键盘触发。
-
mousewheel
鼠标滚轮事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#d1{
width: 300px;
height: 300px;
background-color: aqua;
}
</style>
</head>
<body>
<div id="d1"></div>
<script>
var d1 = document.getElementById('d1');
// 单击事件
d1.onclick = function (event) {
console.log('click');
console.log(event);
}
// 双击事件
d1.ondblclick = function () {
console.log('dblclick');
}
// 鼠标移入事件
d1.onmouseenter = function () {
console.log('mouseenter');
}
// 鼠标移出事件
d1.onmouseleave = function () {
console.log('mouseleave');
}
// 鼠标在元素内部移动时触发
d1.onmousemove = function () {
console.log('mousemove');
}
</script>
</body>
</html>
键盘事件-KeyboardEvent与输入事件-InputEvent
键盘事件是用户操作键盘时触发的。DOM2 Events 最初定义了键盘事件,但该规范在最终发布前删除了相应内容。因此,键盘事件很大程度上是基于原始的 DOM0 实现的。
DOM3 Events 为键盘事件提供了一个首先在 IE9 中完全实现的规范。其他浏览器也开始实现该规范,但仍然存在很多遗留的实现。
键盘事件包含 3 个事件:
-
keydown,用户按下键盘上某个键时触发,而且持续按住会重复触发。
-
keypress,用户按下键盘上某个键并产生字符时触发,而且持续按住会重复触发。Esc 键也会触发这个事件。DOM3 Events 废弃了 keypress 事件,而推荐 textInput 事件。
-
keyup,用户释放键盘上某个键时触发。
虽然所有元素都支持这些事件,但当用户在文本框中输入内容时最容易看到。
输入事件只有一个,即 textInput。这个事件是对 keypress 事件的扩展,用于在文本显示给用户之前更方便地截获文本输入。textInput 会在文本被插入到文本框之前触发。当用户按下键盘上的某个字符键时,首先会触发 keydown 事件,然后触发 keypress 事件,最后触发 keyup 事件。注意,这里 keydown 和 keypress 事件会在文本框出现变化之前触发,而 keyup事件会在文本框出现变化之后触发。如果一个字符键被按住不放,keydown 和 keypress 就会重复触发,直到这个键被释放。对于非字符键,在键盘上按一下这个键,会先触发 keydown 事件,然后触发 keyup 事件。如果按住某个非字符键不放,则会重复触发 keydown 事件,直到这个键被释放,此时会触发 keyup 事件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text" id="inp1">
<script>
var inp1 = document.getElementById('inp1');
// 按下某个按键是触发
inp1.onkeydown = function (event) {
if (event.keyCode == 13) {
console.log('确认');
console.log(this.value);
}
}
// 按下键盘上某个键并产生字符时触发,而且持续按住会重复触发
inp1.onkeypress = function (event) {
console.log(event.keyCode);
}
// 用户释放键盘上某个键时触发
inp1.onkeyup = function (event) {
console.log(event.keyCode);
}
// 键盘输入事件 必须使用DOM2级事件
inp1.addEventListener('textInput', function (event) {
console.log(event.data);
})
</script>
</body>
</html>