0. dom节点扫盲
0.1 dom树模型简介
DOM 的最小组成单位叫做节点(node)。文档的树形结构(DOM 树),就是由各种不同类型的节点组成。每个节点可以看作是文档树的一片叶子。
节点的类型有七种。
Document:整个文档树的顶层节点DocumentType:doctype标签(比如<!DOCTYPE html>)Element:网页的各种HTML标签(比如<body>、<a>等)Attr:网页元素的属性(比如class="right")Text:标签之间或标签包含的文本Comment:注释DocumentFragment:文档的片段
浏览器提供一个原生的节点对象Node,上面这七种节点都继承了Node,因此具有一些共同的属性和方法。
0.2 顶层Node接口
所有 DOM 节点对象都继承了 Node 接口,拥有一些共同的属性和方法。这是 DOM 操作的基础。
0.2.1 公共属性
Node.prototype.nodeType
返回整数表示节点类型, Node定义了常量与之对应
// 示例:
var node = document.documentElement.firstChild;
// node.nodeType是 1
if (node.nodeType === Node.ELEMENT_NODE) {
console.log('该节点是元素节点');
}
不同节点的nodeType属性值和对应的常量如下。
- 文档节点(document):9,对应常量
Node.DOCUMENT_NODE - 元素节点(element):1,对应常量
Node.ELEMENT_NODE - 属性节点(attr):2,对应常量
Node.ATTRIBUTE_NODE - 文本节点(text):3,对应常量
Node.TEXT_NODE - 文档片断节点(DocumentFragment):11,对应常量
Node.DOCUMENT_FRAGMENT_NODE - 文档类型节点(DocumentType):10,对应常量
Node.DOCUMENT_TYPE_NODE - 注释节点(Comment):8,对应常量
Node.COMMENT_NODE
Node.prototype.nodeName
返回节点的名称
// HTML 代码如下
// <div id="d1">hello world</div>
var div = document.getElementById('d1');
div.nodeName // "DIV"
不同节点的nodeName属性值如下。
- 文档节点(document):
#document - 元素节点(element):大写的标签名
- 属性节点(attr):属性的名称
- 文本节点(text):
#text - 文档片断节点(DocumentFragment):
#document-fragment - 文档类型节点(DocumentType):文档的类型
- 注释节点(Comment):
#comment
Node.prototype.nodeValue
返回一个字符串,表示当前节点本身的文本值,该属性可读写
只有文本节点(text)、注释节点(comment)和属性节点(attr)有文本值,因此这三类节点的nodeValue可以返回结果,其他类型的节点一律返回null。同样的,也只有这三类节点可以设置nodeValue属性的值,其他类型的节点设置无效
// HTML 代码如下
// <div id="d1">hello world</div>
var div = document.getElementById('d1');
div.nodeValue // null
div.firstChild.nodeValue // "hello world"
上面代码中,div是元素节点,nodeValue属性返回null。div.firstChild是文本节点,所以可以返回文本值。
Node.prototype.textContent
Node.prototype.baseURI
Node.prototype.ownerDocument
返回当前节点所在的顶层文档对象,即
document对象
// 示例:
var d = p.ownerDocument;
d === document // true
document对象本身的ownerDocument属性,返回null。
Node.prototype.nextSibling
返回紧跟在当前节点后面的第一个同级节点(包含文本、注释等节点),如果没有则返回null,(相当于紧挨着当前元素的下一个兄弟节点)
<div id="d1">hello</div>
<div id="d2">world</div>
var d1 = document.getElementById('d1');
var d2 = document.getElementById('d2');
d1.nextSibling === d2 // true
Node.prototype.previousSibling
返回紧跟在当前节点前面的第一个同级节点(包含文本、注释等节点),如果没有则返回null,(相当于紧挨着当前元素的上一个兄弟节点)
// HTML 代码如下
<div id="d1">hello</div>
<div id="d2">world</div>
var d1 = document.getElementById('d1');
var d2 = document.getElementById('d2');
d2.previousSibling === d1 // true
Node.prototype.parentNode和Node.prototype.parentElement
parentNode: 返回当前元素的父节点(父节点有可能有3种情况:元素节点(element)、文档节点(document)和文档片段节点(documentfragment));没有返回null
parentElement: 返回当前元素的父节点(指定返回的父节点是element类型的元素节点,
parentElement属性相当于把后两种父节点都排除了);没有则返回null
// parentNode示例:
if (node.parentNode) {
node.parentNode.removeChild(node);
}
// parentElement示例:
if (node.parentElement) {
node.parentElement.style.color = 'red';
}
Node.prototype.firstChild和Node.prototype.lastChild
firstChild: 返回当前节点的第一个子节点,没有返回null lastChild: 返回当前节点的最后一个子节点,没有返回null
// HTML 代码如下
<p id="p1">
<span>First span</span>
</p>
var p1 = document.getElementById('p1');
p1.firstChild.nodeName // "SPAN"
Node.prototype.childNodes
返回当前节点的所有子节点的集合,类似数组的对象
var children = document.childNodes;
for (var i = 0; i < children.length; i++) {
console.log(children[i].nodeType);
}
// 10
// 1
Node.prototype.isConnected
isConnected属性返回一个布尔值,表示当前节点是否在文档之中。
var test = document.createElement('p');
test.isConnected // false
document.body.appendChild(test);
test.isConnected // true
上面代码中,test节点是脚本生成的节点,没有插入文档之前,isConnected属性返回false,插入之后返回true
0.2.2 公共方法
1. 原生dom
1.1 js操作css
element.style.height = '100px';
element.style.textAlgin = 'center';
element.style['text-algin'] = 'center';
///////////////
div.setAttribute(
'style',
'background-color:red;' + 'border:1px solid black;'
);
<div style="background-color:red; border:1px solid black;" />
/////////////////
element.setAttribute('style', 'height: 100px !important');
//使用setProperty 如果要设置!important,推荐用这种方法设置第三个参数
element.style.setProperty('height', '300px', 'important');
getComputedStyle():直接使用style只能获取到通过style设置的行内样式;getComputedStyle可以得到最终计算样式信息
// 语法
let style = window.getComputedStyle(elem, [pseudoElt]);
- elem: dom
- pseudoElt: 伪类(可选,可以不写或者为null或者写伪类)
// 它等价于
// let style = document.defaultView.getComputedStyle(elem1);
// let style = document.defaultView.getComputedStyle(elem1, null);
// let style = document.defaultView.getComputedStyle(elem1, '::before');
// 用法
var styleObj = window.getComputedStyle(elem);
styleObj.backgroundColor
getPropertyValue(): 不支持驼峰式
// 用法
window.getComputedStyle(element, null).getPropertyValue("float");
备注:如果我们不使用getPropertyValue方法,直接使用键值访问,其实也是可以的。但是,比如这里的的float,如果使用键值访问,则不能直接使用getComputedStyle(element, null).float,而应该是cssFloat与styleFloat,自然需要浏览器判断了,比较折腾
1.2 js操作属性
下面方法都是用来操作elem属性的,style也是元素属性,属性驼峰式
getAttribute()getAttributeNames()setAttribute()hasAttribute()hasAttributes()removeAttribute()
// `a`元素标签的属性`id`和`href`,自动成为节点对象的属性。
这些属性都是可写的
var a = document.getElementById('test');
a.id // "test"
a.href // "http://www.example.com/"
element.offsetLeft而不是element.style.offsetLeft
js操作类名
1. className操作类名
通过className属性添加、删除和替换类名比较麻烦。因为className是一个字符串,所以如果只是修改一部分字符串,也必须每次设置整个字符串
<div class="one two bd">
如果此时想删除two类名
// 获取类名列表
let classNameList = ele.className.split(/\+s/)
// 找到对应类名删除
let classNameList = classNameList.filter(item => item !== "two")
// 设置剩下的类名字符串
ele.className = classNameList.join(' ')
2. classList操作类名
h5新加classList操作类名
add(value): 给定的字符串值添加到列表中,如果值存在则不添加
contains(value): 列表存在value返回true 反之 false
remove(value): 从列表中删除字符串
toggle(value): 存在value则删除,不存在则添加value
// 增加类名 mystyle,可增加多个
document.getElementById("myDIV").classList.add("mystyle");
// 删除类名
document.getElementById("myDIV").classList.remove("mystyle");
document.getElementById("myDIV").classList.remove("mystyle", "anotherClass", "thirdClass");
检查是否含有某个CSS类
document.getElementById("myDIV").classList.contains('myCssClass'); //return true or false
// 获取类名个数
document.getElementById("myDIV").classList.length;
// item(index),获取下表为index的类名
document.getElementById("myDIV").classList.item(0);
//js 判断class 名是否存在\
function hasClass(element, cls) {\
return (' ' + element.className + ' ').indexOf(' ' + cls + ' ') > -1;\
}\
hasClass(document.querySelector("html"), 'no-js');
2. 页面声明周期(DOMContentLoaded/load/beforeunload/unload)
-
DOMContentLoaded: 浏览器已完全加载 HTML,并构建了 DOM 树,但像
<img>和样式表之类的外部资源可能尚未加载完成(同步的script也已经加载完成) -
load: 浏览器不仅加载完成了 HTML,还加载完成了所有外部资源:图片,样式等
-
beforeunload: 用户正在离开,可以检查用户是否保存了更改,并询问他是否真的要离开
-
unload: 用户几乎已经离开了,但是我们仍然可以启动一些操作,例如发送统计数据。
2.1 DOMContentLoaded
DOMContentLoaded 事件发生在 document 对象上。
我们必须使用 addEventListener 来捕获它
document.addEventListener("DOMContentLoaded", ready); // 不是 "document.onDOMContentLoaded = ..."
<script>
function ready() { alert('DOM is ready');
// 图片目前尚未加载完成(除非已经被缓存),所以图片的大小为 0x0
alert(`Image size:${img.offsetWidth}x${img.offsetHeight}`); }
document.addEventListener("DOMContentLoaded", ready);
</script>
<img id="img" src="https://en.js.cx/clipart/train.gif?speed=1&cache=0">
在示例中,DOMContentLoaded 处理程序在文档加载完成后触发,所以它可以查看所有元素,包括它下面的 <img> 元素。(换句话它会加载img标签,但是不会等待img上的src外部资源的加载)
但是,它不会等待图片加载。因此,alert 显示其大小为零。
乍一看,DOMContentLoaded 事件非常简单。DOM 树准备就绪 —— 这是它的触发条件。它并没有什么特别之处
DOMContentLoaded和脚本:
2.2 load
2.3 beforeunload
2.4 unload
3. 跨浏览器的事件处理程序
4. 事件类型
load/onload/beforeunload/DOMContentLoaded/
zh.javascript.info/onload-ondo…
4.1 用户界面事件
涉及与BOM交互的通用浏览器事件
4.1.1 load事件
load:在window上当页面加载完成后触发,在窗套<frameset>上当所有窗格<frame>都加
载完成后触发,在<img>元素上当图片加载完成后触发,在<object>元素上当相应对象加载完成后触发。
两种使用方式:直接标签上onload属性 或者 addEventListener("load")
// window
window.addEventListener("load", event => {console.log("loaded!!!")})
// img
<img src="" onload="console.log("loaded!!!")">或者
let imgObj = document.querySelector('imge')
imgObj.addEventListener("load", event => {console.log("loaded!!!")})
采用js的方式给img增加load,必须等到dom挂载结束;
另外需要先给img增加load事件之后再把img添加至dom中;
注意下载图片并不一定要将img添加进dom中,只要给img设置了src就会立即开始下载图片
window.addEventListener("load", event => {
let imgObj = document.creatElement('img')
imgObj.addEventListener("load", event => {console.log("loaded!!!")})
document.body.appendChild(imgObj)
imgObj.src = "javascript::"
})
4.1.2 unload事件
unload:在window上当页面完全卸载后触发,在窗套上当所有窗格都卸载完成后触发,在<object>元素上当相应对象卸载完成后触发。
unload一般用在从一个页面导航到另一个页面触发,用于清理引用,避免内存泄漏
同load一样,两种使用方式。
// 注意:由于unload是在页面卸载完成后触发,所以unload的回调中不能再使用页面加载成功后才有的对象或者属性
window.addEventListener("unload", event => {console.log("unloaded!!!")})
4.1.3 abort事件
abort:在<object>元素上当相应对象加载完成前被用户提前终止下载时触发。
4.1.4 error事件
error:在window上当JavaScript报错时触发,在<img>元素上当无法加载指定图片时触发,在<object>元素上当无法加载相应对象时触发,在窗套上当一个或多个窗格无法完成加载时触发。
4.1.5 select事件
select:在文本框<input>或textarea上当用户选择了一个或多个字符时触发。
4.1.6 resize事件
resize:在window或窗格上当窗口或窗格被缩放时触发。
window.addEventListener("resize", event => {})
4.1.7 scroll事件
4.1.7.1 基本使用
scroll:当用户滚动包含滚动条的元素时在元素上触发。元素包含已加载页面的滚动条。
在html元素上都有scroll事件,分别有scrollLeft和scrollTop属性
window.addEventListener("scroll", event => {})
pc端通过document.documentElement.scrollTop获取滚动;移动端通过document.body.scrollTop获取滚动。也就是说不同平台获取滚动方式不一样,所以有兼容性问题;可以通过判断平台进而增加决定采用哪种方式,但是很繁琐,于是出现了document.scrollingElement自动识别平台,在pc上document.scrollingElement就是document.documentElement, 在移动端document.scrollingElement就是document.body。
4.1.7.2 window.scrollTo()和window.scroll()和window.scrollBy()都用来滚动整个文档网页
window.scroll()是window.scrollTo()的别名
window.scrollTo()用于将文档滚动到指定位置。可以接受两个参数,表示滚动后位于窗口左上角的页面坐标
window.scrollTo(x, y)
也可以接受一个配置对象作为参数
window.scrollTo({
top: 1000,
behavior: 'smooth'
})
配置对象options有三个属性。
top:滚动后页面左上角的垂直坐标,即 y 坐标。left:滚动后页面左上角的水平坐标,即 x 坐标。behavior:字符串,表示滚动的方式,有三个可能值(smooth、instant、auto),默认值为auto。
window.scrollBy()用于将网页滚动指定像素距离,两个参数分别是:水平向右滚动的像素,垂直向下滚动的像素。
// 将网页向下滚动一屏
window.scrollBy(0, window.innerHeight)
4.1.7.3 滚动某个元素
不是要滚动整个文档,而是滚动某个元素,则可以使用:
- Element.scrollTop // y轴滚动
- Element.scrollLeft // x轴滚动
- Element.scrollIntoView()
4.1.8 session历史事件
4.1.8.1 pageshow事件
默认情况下,浏览器会在当前会话(session)缓存页面,当用户点击“前进/后退”按钮时,浏览器就会从缓存中加载页面。
pageshow 事件在页面加载时触发,包括第一次加载和从缓存加载两种情况
第一次加载时,它的触发顺序排在load事件后面。从缓存加载时,load事件不会触发,因为网页在缓存中的样子通常是load事件的监听函数运行后的样子,所以不必重复执行。同理,如果是从缓存中加载页面,网页内初始化的 JavaScript 脚本(比如 DOMContentLoaded 事件的监听函数)也不会执行
pageshow事件有一个persisted属性,返回一个布尔值。页面第一次加载时,这个属性是false;当页面从缓存加载时,这个属性是true
window.addEventListener('pageshow', function(event){
if (event.persisted) {
// ...
}
});
适用场景:比如监听判断网页是否从缓存中加载的;因为缓存中加载网页时不会触发load事件和DOMContentLoaded事件
4.1.8.2 pagehide事件
pagehide事件与pageshow事件类似,当用户通过“前进/后退”按钮,离开当前页面时触发。它与 unload 事件的区别在于,如果在 window 对象上定义unload事件的监听函数之后,页面不会保存在缓存中,而使用pagehide事件,页面会保存在缓存中.
pagehide事件实例也有一个persisted属性,将这个属性设为true,就表示页面要保存在缓存中;设为false,表示网页不保存在缓存中,这时如果设置了unload 事件的监听函数,该函数将在 pagehide 事件后立即运行。
如果页面包含<frame>或<iframe>元素,则<frame>页面的pageshow事件和pagehide事件,都会在主页面之前触发。
注意,这两个事件只在浏览器的history对象发生变化时触发,跟网页是否可见没有关系。
4.1.8.3 popstate事件
popstate事件在浏览器的history对象的当前记录发生显式切换时触发。注意,调用history.pushState()或history.replaceState(),并不会触发popstate事件。该事件只在用户在history记录之间显式切换时触发,比如鼠标点击“后退/前进”按钮,或者在脚本中调用history.back()、history.forward()、history.go()时触发。
该事件对象有一个state属性,保存history.pushState方法和history.replaceState方法为当前记录添加的state对象。
window.onpopstate = function (event) {
console.log('state: ' + event.state);
};
history.pushState({page: 1}, 'title 1', '?page=1');
history.pushState({page: 2}, 'title 2', '?page=2');
history.replaceState({page: 3}, 'title 3', '?page=3');
history.back(); // state: {"page":1}
history.back(); // state: null
history.go(2); // state: {"page":3}
上面代码中,pushState方法向history添加了两条记录,然后replaceState方法替换掉当前记录。因此,连续两次back方法,会让当前条目退回到原始网址,它没有附带state对象,所以事件的state属性为null,然后前进两条记录,又回到replaceState方法添加的记录。
浏览器对于页面首次加载,是否触发popstate事件,处理不一样,Firefox 不触发该事件。
4.1.8.4 hashchange事件
hashchange事件在 URL 的 hash 部分(即#号后面的部分,包括#号)发生变化时触发。该事件一般在window对象上监听。
hashchange的事件实例具有两个特有属性:oldURL属性和newURL属性,分别表示变化前后的完整 URL。
// URL 是 http://www.example.com/
window.addEventListener('hashchange', myFunction);
function myFunction(e) {
console.log(e.oldURL);
console.log(e.newURL);
}
location.hash = 'part2';
// http://www.example.com/
// http://www.example.com/#part2
4.1.9 readystatechange事件
readystatechange事件当 Document 对象和 XMLHttpRequest 对象的readyState属性发生变化时触发。document.readyState有三个可能的值:loading(网页正在加载)、interactive(网页已经解析完成,但是外部资源仍然处在加载状态)和complete(网页和所有外部资源已经结束加载,load事件即将触发)。
document.onreadystatechange = function () {
if (document.readyState === 'interactive') {
// ...
}
}
这个事件可以看作DOMContentLoaded事件的另一种实现方法
4.1.10 fullscreenchange全屏事件和fullscreenerror事件
fullscreenchange事件: 在进入或退出全屏状态时触发,该事件发生在document对象上面
document.addEventListener('fullscreenchange', function (event) {
console.log(document.fullscreenElement);
});
fullscreenerror事件在浏览器无法切换到全屏状态时触发
4.1.11 剪贴板事件
cut、copy、paste这三个事件的事件对象都是ClipboardEvent接口的实例。ClipboardEvent有一个实例属性clipboardData,是一个 DataTransfer 对象,存放剪贴的数据。具体的 API 接口和操作方法,请参见《拖拉事件》的 DataTransfer 对象部分
document.addEventListener('copy', function (e) {
e.clipboardData.setData('text/plain', 'Hello, world!');
e.clipboardData.setData('text/html', '<b>Hello, world!</b>');
e.preventDefault();
});
上面的代码使得复制进入剪贴板的,都是开发者指定的数据,而不是用户想要拷贝的数据
4.1.11.1 cut事件
cut: 将选中的内容从文档中移除,加入剪贴板时触发(相当于剪切内容并加入到剪贴板时触发)
4.1.11.2 copy事件
copy:进行复制动作时触发
4.1.11.3 paste事件
paste:剪贴板内容粘贴到文档后触发。
// 禁止输入框粘贴事件(用户无法在输入框里面粘贴内容)
inputElement.addEventListener('paste', e => e.preventDefault());
4.2 焦点事件
焦点事件中的两个主要事件是focus和blur,它们最大的问题是不冒泡。
4.2.1 blur事件
blur:当元素失去焦点时触发。这个事件不冒泡,所有浏览器都支持。
4.2.2 focus事件
focus:当元素获得焦点时触发。这个事件不冒泡,所有浏览器都支持。
4.2.3 focusin事件
focusin:当元素获得焦点时触发。这个事件是focus的冒泡版。
4.2.4 focusout事件
focusout:当元素失去焦点时触发。这个事件是blur的通用版。
4.3 鼠标与滚轮事件
4.3.1 MouseEvent接口实例属性
4.3.1.1 MouseEvent.altKey && MouseEvent.ctrlKey && MouseEvent.metaKey && MouseEvent.shiftKey
MouseEvent.altKey、MouseEvent.ctrlKey、MouseEvent.metaKey、MouseEvent.shiftKey这四个属性都返回一个布尔值,表示事件发生时,是否按下对应的键。它们都是只读属性。
altKey属性:Alt 键ctrlKey属性:Ctrl 键metaKey属性:Meta 键(Mac 键盘是一个四瓣的小花,Windows 键盘是 Windows 键)shiftKey属性:Shift 键
// HTML 代码如下
// <body onclick="showKey(event)">
function showKey(e) {
console.log('ALT key pressed: ' + e.altKey);
console.log('CTRL key pressed: ' + e.ctrlKey);
console.log('META key pressed: ' + e.metaKey);
console.log('SHIFT key pressed: ' + e.shiftKey);
}
4.3.1.2 MouseEvent.button && MouseEvent.buttons
MouseEvent.button属性返回一个数值,表示事件发生时按下了鼠标的哪个键。该属性只读。
- 0:按下主键(通常是左键),或者该事件没有初始化这个属性(比如
mousemove事件)。 - 1:按下辅助键(通常是中键或者滚轮键)。
- 2:按下次键(通常是右键)。
MouseEvent.buttons属性返回一个三个比特位的值,表示同时按下了哪些键。它用来处理同时按下多个鼠标键的情况。该属性只读。
- 1:二进制为
001(十进制的1),表示按下左键。 - 2:二进制为
010(十进制的2),表示按下右键。 - 4:二进制为
100(十进制的4),表示按下中键或滚轮键。
同时按下多个键的时候,每个按下的键对应的比特位都会有值。比如,同时按下左键和右键,会返回3(二进制为011)。
4.3.1.3 MouseEvent.clientX,MouseEvent.clientY
-
MouseEvent.clientX属性返回鼠标位置相对于浏览器窗口左上角的水平坐标(单位像素) -
MouseEvent.clientY属性返回垂直坐标。这两个属性都是只读属性
这两个属性还分别有一个别名MouseEvent.x和MouseEvent.y
4.3.1.4 MouseEvent.movementX,MouseEvent.movementY
4.3.1.5 MouseEvent.screenX,MouseEvent.screenY
MouseEvent.screenX属性返回鼠标位置相对于屏幕左上角的水平坐标(单位像素)MouseEvent.screenY属性返回垂直坐标。这两个属性都是只读属性。
4.3.1.6 MouseEvent.offsetX,MouseEvent.offsetY
-
MouseEvent.offsetX属性返回鼠标位置与目标节点左侧的padding边缘的水平距离(单位像素) -
MouseEvent.offsetY属性返回与目标节点上方的padding边缘的垂直距离。这两个属性都是只读属性
注意:offsetX是鼠标位置和目标元素的左边padding边缘线的差值;有可能是负数;比如下面示例图2中,鼠标在目标元素的蓝色border的左上角点击,此时offsetX = 鼠标x位置 - 目标元素的左边padding的边界线的左上角的x位置,这里差值相当于border-left的值,也就是20,但offsetX是-20.
elem.clientWidth/clientHeight:是元素宽高(包含内容+内边距)
elem.offsetWidth/offsetHeight:是元素宽高(包含内容+内边距+边框)
elem.offsetTop: 是获取元素elem元素到网页顶部的距离
scrollTop: 网页滚出手机顶端之后,网页的顶部,距离手机顶部的距离
document.documentElement.scrollTop: 获取/设置的是整个网页滚出窗口的距离;也就是滚出的网页顶部距离窗口顶部的距离
document.documentElement.offsetHeight: 获取的是整个网页文档的高度
<!-- 测试代码 -->
<!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 type="text/css">
body {
margin: 0px;
}
.son {
width: 100px;
height: 100px;
padding: 20px;
border: 20px solid blue;
margin: 20px;
}
</style>
</head>
<body>
<div class="son"></div>
<script>
document.querySelector('.son').onclick = function (e) {
console.log('e.clientX', e.clientX);
console.log('e.clientY', e.clientY);
console.log('e.pageX', e.pageX);
console.log('e.screenX', e.screenX);
console.log('e.offsetX', e.offsetX);
};
</script>
</body>
</html>
如下示例图1:
如下示例图2:
如下示例图3:
如下示例图4:
4.3.1.7 MouseEvent.pageX,MouseEvent.pageY
-
MouseEvent.pageX属性返回鼠标位置与文档左侧边缘的距离(单位像素) -
MouseEvent.pageY属性返回与文档上侧边缘的距离(单位像素)。
它们的返回值都包括文档不可见的部分。这两个属性都是只读。
4.3.2 事件集合
4.3.2.1 click事件
click:在用户单击鼠标主键(通常是左键)或按键盘回车键时触发。这主要是基于无障碍的考虑,让键盘和鼠标都可以触发onclick事件处理程序。
4.3.2.2 dblclick事件
dblclick:在用户双击鼠标主键(通常是左键)时触发。这个事件不是在DOM2 Events中定义的,但得到了很好的支持,DOM3 Events将其进行了标准化。
4.3.2.3 mousedown事件
mousedown:在用户按下任意鼠标键时触发。这个事件不能通过键盘触发。
4.3.2.4 mouseenter事件
mouseenter:在用户把鼠标光标从元素外部移到元素内部时触发。这个事件不冒泡,也不会在光标经过后代元素时触发。mouseenter事件不是在DOM2 Events中定义的,而是DOM3 Events中新增的事件。
4.3.2.5 mouseleave事件
mouseleave:在用户把鼠标光标从元素内部移到元素外部时触发。这个事件不冒泡,也不会在光标经过后代元素时触发。mouseleave事件不是在DOM2 Events中定义的,而是DOM3 Events中新增的事件。
4.3.2.6 mousemove事件
mousemove:在鼠标光标在元素上移动时反复触发。这个事件不能通过键盘触发。
4.3.2.7 mouseout事件
mouseout:在用户把鼠标光标从一个元素移到另一个元素上时触发。移到的元素可以是原始元素的外部元素,也可以是原始元素的子元素。这个事件不能通过键盘触发。
4.3.2.8 mouseover事件
mouseover:在用户把鼠标光标从元素外部移到元素内部时触发。这个事件不能通过键盘触发。
4.3.2.9 mouseup事件
mouseup:在用户释放鼠标键时触发。这个事件不能通过键盘触发。
4.3.2.10 contextmenu事件
contextmenu: 按下鼠标右键时(上下文菜单出现前)触发,或者按下“上下文”菜单键时触发。
4.3.2.11 wheel事件
wheel:滚动鼠标的滚轮时触发,(该事件继承的是WheelEvent接口;其他鼠标事件继承MouseEvent;WheelEvent接口又继承MouseEvent)
4.3.3 MouseEvent实例方法
4.3.4 wheelEvent接口
4.4 键盘与输入事件
4.5 合成事件
在使用某种IME(Input MethodEditor,输入法编辑器)输入字符时触发
4.6 进度事件
进度事件用来描述资源加载的进度,主要由 AJAX 请求、<img>、<audio>、<video>、<style>、<link>等外部资源的加载触发,继承了ProgressEvent接口。它主要包含以下几种事件。
abort:外部资源中止加载时(比如用户取消)触发。如果发生错误导致中止,不会触发该事件。error:由于错误导致外部资源无法加载时触发。load:外部资源加载成功时触发。loadstart:外部资源开始加载时触发。loadend:外部资源停止加载时触发,发生顺序排在error、abort、load等事件的后面。progress:外部资源加载过程中不断触发。timeout:加载超时时触发。
注意,除了资源下载,文件上传也存在这些事件。
4.6.1 process进度条事件
lengthComputable:布尔值,表示加载的总量是否可以计算,默认是false。loaded:整数,表示已经加载的量,默认是0。total:整数,表示需要加载的总量,默认是0。
// 下载进度事件
var xhr = new XMLHttpRequest();
xhr.addEventListener('progress', updateProgress, false);
xhr.addEventListener('load', transferComplete, false);
xhr.addEventListener('error', transferFailed, false);
xhr.addEventListener('abort', transferCanceled, false);
xhr.open();
function updateProgress(e) {
if (e.lengthComputable) {
var percentComplete = e.loaded / e.total;
} else {
console.log('不能计算进度');
}
}
function transferComplete(e) {
console.log('传输结束');
}
function transferFailed(evt) {
console.log('传输过程中发生错误');
}
function transferCanceled(evt) {
console.log('用户取消了传输');
}
// 上传进度事件必须放在`XMLHttpRequest.upload`对象上面
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', updateProgress, false);
xhr.upload.addEventListener('load', transferComplete, false);
xhr.upload.addEventListener('error', transferFailed, false);
xhr.upload.addEventListener('abort', transferCanceled, false);
xhr.open();
4.7 表单事件
4.7.1 input事件
input事件当<input>、<select>、<textarea>的值发生变化时触发。对于复选框(<input type=checkbox>)或单选框(<input type=radio>),用户改变选项时,也会触发这个事件。另外,对于打开contenteditable属性的元素,只要值发生变化,也会触发input事件
input事件的一个特点,就是会连续触发,比如用户每按下一次按键,就会触发一次input事件
input和change的区别是如果内容连续变化,input事件会触发多次,而change事件只在失去焦点时触发一次
4.7.2 select事件
select事件:当input和textarea中的文本被选中时触发
// <input id="test" type="text" value="Select me!" />
var elem = document.getElementById('test');
elem.addEventListener('select', function (e) {
console.log(e.type); // "select"
}, false);
4.7.3 change事件
4.7.4 invalid事件
用户提交表单时,如果表单元素的值不满足校验条件,就会触发invalid事件
<form>
<input type="text" required oninvalid="console.log('invalid input')" />
<button type="submit">提交</button>
</form>
上面代码中,输入框是必填的。如果不填,用户点击按钮提交时,就会触发输入框的invalid事件,导致提交被取消
4.7.5 reset事件和submit事件
这两个事件发生在表单对象<form>上,而不是发生在表单的成员上。
reset事件: 当表单重置(所有表单成员变回默认值)时触发。
submit事件: 当表单数据向服务器提交时触发。注意,submit事件的发生对象是<form>元素,而不是<button>元素,因为提交的是表单,而不是按钮。
4.8 touch触摸事件
4.8.1 简介
浏览器的触摸 API 由三个部分组成。
- Touch:一个触摸点
- TouchList:多个触摸点的集合
- TouchEvent:触摸引发的事件实例
Touch接口的实例对象用来表示触摸点(一根手指或者一根触摸笔),包括位置、大小、形状、压力、目标元素等属性。有时,触摸动作由多个触摸点(多根手指)组成,多个触摸点的集合由TouchList接口的实例对象表示。TouchEvent接口的实例对象代表由触摸引发的事件,只有触摸屏才会引发这一类事件。
很多时候,触摸事件和鼠标事件同时触发,即使这个时候并没有用到鼠标。这是为了让那些只定义鼠标事件、没有定义触摸事件的代码,在触摸屏的情况下仍然能用。如果想避免这种情况,可以用event.preventDefault方法阻止发出鼠标事件。
4.8.2 相关属性
4.8.2.1 Touch接口
Touch 接口代表单个触摸点。触摸点可能是一根手指,也可能是一根触摸笔
var touch = new Touch(touchOptions);
Touch构造函数接受一个配置对象作为参数,它有以下属性。
identifier:必需,类型为整数,表示触摸点的唯一 ID。target:必需,类型为元素节点,表示触摸点开始时所在的网页元素。clientX:可选,类型为数值,表示触摸点相对于浏览器窗口左上角的水平距离,默认为0。clientY:可选,类型为数值,表示触摸点相对于浏览器窗口左上角的垂直距离,默认为0。screenX:可选,类型为数值,表示触摸点相对于屏幕左上角的水平距离,默认为0。screenY:可选,类型为数值,表示触摸点相对于屏幕左上角的垂直距离,默认为0。pageX:可选,类型为数值,表示触摸点相对于网页左上角的水平位置(即包括页面的滚动距离),默认为0。pageY:可选,类型为数值,表示触摸点相对于网页左上角的垂直位置(即包括页面的滚动距离),默认为0。radiusX:可选,类型为数值,表示触摸点周围受到影响的椭圆范围的 X 轴半径,默认为0。radiusY:可选:类型为数值,表示触摸点周围受到影响的椭圆范围的 Y 轴半径,默认为0。rotationAngle:可选,类型为数值,表示触摸区域的椭圆的旋转角度,单位为度数,在0到90度之间,默认值为0。force:可选,类型为数值,范围在0到1之间,表示触摸压力。0代表没有压力,1代表硬件所能识别的最大压力,默认为0。
4.8.2.2 TouchList接口
TouchList接口表示一组触摸点的集合。它的实例是一个类似数组的对象,成员是Touch的实例对象,表示所有触摸点。用户用三根手指触摸,产生的TouchList实例就会包含三个成员,每根手指的触摸点对应一个Touch实例对象。
它的实例主要通过触摸事件的TouchEvent.touches、TouchEvent.changedTouches、TouchEvent.targetTouches这几个属性获取。
它的实例属性和实例方法只有两个。
TouchList.length:数值,表示成员数量(即触摸点的数量)。TouchList.item():返回指定位置的成员,它的参数是该成员的位置编号(从零开始)。
4.8.2.3 TouchEvent接口
TouchEvent 接口继承了 Event 接口,表示由触摸引发的事件实例,通常来自触摸屏或轨迹板
-
touches:TouchList实例,代表所有的当前处于活跃状态的触摸点,默认值是一个空数组[]。 -
targetTouches:TouchList实例,代表所有处在触摸的目标元素节点内部、且仍然处于活动状态的触摸点,默认值是一个空数组[]。 -
changedTouches:TouchList实例,代表本次触摸事件的相关触摸点,默认值是一个空数组[]。 -
touchstart事件:被激活的触摸点 -
touchmove事件:发生变化的触摸点 -
touchend事件:消失的触摸点(即不再被触碰的点) -
ctrlKey:布尔值,表示 Ctrl 键是否同时按下,默认值为false。 -
shiftKey:布尔值,表示 Shift 键是否同时按下,默认值为false。 -
altKey:布尔值,表示 Alt 键是否同时按下,默认值为false。 -
metaKey:布尔值,表示 Meta 键(或 Windows 键)是否同时按下,默认值为false。
TouchEvent.changedTouches属性返回一个TouchList实例,成员是一组Touch实例对象,表示本次触摸事件的相关触摸点。
对于不同的时间,该属性的含义有所不同。
touchstart事件:被激活的触摸点touchmove事件:发生变化的触摸点touchend事件:消失的触摸点(即不再被触碰的点)
someElement.addEventListener('touchmove', function (e) {
for (var i = 0; i < e.changedTouches.length; i++) {
console.log(e.changedTouches[i].identifier);
}
}, false);
TouchEvent.touches属性返回一个TouchList实例,成员是所有仍然处于活动状态(即触摸中)的触摸点。一般来说,一个手指就是一个触摸点。
someElement.addEventListener('touchstart', function (e) {
switch (e.touches.length) {
// 一根手指触摸
case 1: handle_one_touch(e); break;
// 两根手指触摸
case 2: handle_two_touches(e); break;
// 三根手指触摸
case 3: handle_three_touches(e); break;
// 其他情况
default: console.log('Not supported'); break;
}
}, false);
4.8.3 事件
-
touchcancel: 触摸点取消时触发,比如在触摸区域跳出一个模态窗口(modal window)、触摸点离开了文档区域(进入浏览器菜单栏)、用户的触摸点太多,超过了支持的上限(自动取消早先的触摸点)
-
mousedown:鼠标按下 touchstart:手指触摸
-
mousemove:鼠标移动 touchmove:手指移动
-
moveup:鼠标抬起 touchend:手指抬起
-
touch事件只能在移动端使用;mouse事件只能在PC端使用;
-
touch事件的touchstart只能在元素内触发,touchmove和touchend可以在屏幕任意位置触发;
-
mouse事件都只能在元素内触发;
-
touch事件只有在touchstart触发后,才能触发touchmove和touchend事件;但mouse事件的mousemove只要在元素内没有鼠标按下也能触发
Box.addEventListener('touchstart',function (){
console.log('手指按下')
})
Box.addEventListener('touchmove',function (){
console.log('手指移动')
})
Box.addEventListener('touchend',function (){
console.log('手指抬起')
})}
4.9 拖拉事件
5. Event类
5.1 event.preventDefault()
Event.preventDefault方法取消浏览器对当前事件的默认行为。比如点击链接后,浏览器默认会跳转到另一个页面,使用这个方法以后,就不会跳转了;再比如,按一下空格键,页面向下滚动一段距离,使用这个方法以后也不会滚动了。该方法生效的前提是,事件对象的cancelable属性为true,如果为false,调用该方法没有任何效果。
注意,该方法只是取消事件对当前元素的默认影响,不会阻止事件的传播。如果要阻止传播,可以使用stopPropagation()或stopImmediatePropagation()方法。
<!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>
<a class="aa" href="http://www.baidu.com">
<span>click me</span>
</a>
<script>
document.querySelector('a').addEventListener('click', e => {
console.log(111)
})
document.querySelector('span').addEventListener('click', e => {
e.preventDefault()
console.log(222)
})
</script>
</body>
</html>
解析:点击click me之后打印 222 111,但是a标签不会跳转,span中的e.preventDefault()对外层的a标签也生效,阻止了a标签的默认href跳转; 但是只能阻止同类事件,比如只能阻止这里的click事件,如果其他的change事件是无法阻止的
// 示例1:浏览器的默认行为是单击会选中单选框,取消这个行为,就导致无法选中单选框。
<input type="checkbox" id="my-checkbox" />
var cb = document.getElementById('my-checkbox');
cb.addEventListener(
'click',
function (e){ e.preventDefault(); },
false
);
// 示例2:
// HTML 代码为
// <input type="text" id="my-input" />
var input = document.getElementById('my-input');
input.addEventListener('keypress', checkName, false);
function checkName(e) {
if (e.charCode < 97 || e.charCode > 122) {
e.preventDefault();
}
}
利用preventDefault这个方法,可以为文本输入框设置校验条件。如果用户的输入不符合条件,就无法将字符输入文本框。
上面代码为文本框的`keypress`事件设定监听函数后,将只能输入小写字母,否则输入事件的默认行为(写入文本框)将被取消,导致不能向文本框输入内容。
5.2 event.stopPropagation()
stopPropagation方法阻止事件在 DOM 中继续传播(向上冒泡或者向下捕获),但是不包括在当前节点上后续click事件或者其他事件监听函数的执行。
<body>
<div class="dd">
<div class="son1">hhhh</div>
</div>
<script>
var son = document.querySelector('.son1')
var dd = document.querySelector('.dd')
dd.addEventListener('click', e => {
console.log('dddd')
})
son.addEventListener('click', e => {
console.log(1111)
})
son.addEventListener('click', e => {
console.log(222)
})
</script>
</body>
// 输出: 1111 222 dddd
<body>
<div class="dd">
<div class="son1">hhhh</div>
</div>
<script>
var son = document.querySelector('.son1')
var dd = document.querySelector('.dd')
dd.addEventListener('click', e => {
console.log('dddd')
})
son.addEventListener('click', e => {
console.log(1111)
// 阻止事件的向上面的父节点冒泡,但是不影响同一个节点的事件
e.stopPropagation()
})
son.addEventListener('click', e => {
console.log(222)
})
</script>
</body>
// 输出:1111 222
5.3 event.stopImmediatePropagation()
如果同一个节点对于同一个事件指定了多个监听函数,这些函数会根据添加的顺序依次调用。只要其中有一个监听函数调用了Event.stopImmediatePropagation方法,其他的监听函数就不会再执行了。
<body>
<div class="dd">
<div class="son1">hhhh</div>
</div>
<script>
var son = document.querySelector('.son1')
var dd = document.querySelector('.dd')
dd.addEventListener('click', e => {
console.log('dddd')
})
son.addEventListener('click', e => {
console.log(1111)
// 除了阻止事件向上冒泡,同时也阻止同一个节点上事件回调的执行
e.stopImmediatePropagation()
})
son.addEventListener('click', e => {
console.log(222)
})
</script>
</body>
// 输出:1111
5.4 event.composedPath()
Event.composedPath()返回一个数组,成员是事件的最底层节点和依次冒泡经过的所有上层节点。
<body>
<div class="dd">
<div class="son1">hhhh</div>
</div>
<script>
var son = document.querySelector('.son1')
var dd = document.querySelector('.dd')
dd.addEventListener('click', e => {
console.log('dddd')
})
son.addEventListener('click', e => {
console.log(e.composedPath())
})
</script>
</body>
6. 事件流
事件流描述的是从页面中接收事件的顺序。
6.1 绑定事件的3种方式
6.1.1 on绑定
使用on绑定监听代码,只会在冒泡阶段触发。
注意: 这些属性的值是将会执行的代码,而不是一个函数。一旦指定的事件发生,on-属性的值是原样传入 JavaScript 引擎执行。因此如果要执行函数,不要忘记加上一对圆括号。
<!-- 正确 -->
<body onload="doSomething()">
<div onclick="console.log('触发事件')">
el.setAttribute('onclick', 'doSomething()');
// 等同于
// <Element onclick="doSomething()">
<!-- 错误 -->
<body onload="doSomething">
<div onclick="console.log(2)">
<button onclick="console.log(1)">点击</button>
</div>
// 所以点击结果是先输出`1`,再输出`2`,即事件从子元素开始冒泡到父元素。
缺点:违反了 HTML 与 JavaScript 代码相分离的原则,将两者写在一起,不利于代码分工,因此不推荐使用
6.1.2 元素节点的事件属性绑定
元素节点对象的事件属性绑定,也只会在冒泡阶段触发。
window.onload = doSomething;
div.onclick = function (event) {
console.log('触发事件');
};
注意,这种方法与 HTML 的on-属性的差异是,它的值是函数名(doSomething),而不像后者,必须给出完整的监听代码(doSomething())
缺点:同一个事件只能定义一个监听函数,也就是说,如果定义两次onclick属性,后一次定义会覆盖前一次。因此,也不推荐使用。
6.1.3 元素节点的addEventListener()
所有 DOM 节点实例都有
addEventListener方法,用来为该节点定义事件的监听函数。
// 语法:
- `type`:事件名称,大小写敏感。
- `listener`:监听函数。事件发生时,会调用该监听函数。
- `useCapture`:`true`,表示监听函数将在捕获阶段(capture)触发;该参数可选,默认值为`false`只冒泡阶段被触发。
target.addEventListener(type, listener[, useCapture]);
// 示例1:
function hello() {
console.log('Hello world');
}
var button = document.getElementById('btn');
button.addEventListener('click', hello, false);
// 示例2:第二个参数除了监听函数,还可以是一个具有`handleEvent`方法的对象,效果与监听函数一样。
buttonElement.addEventListener('click', {
handleEvent: function (event) {
console.log('click');
}
});
// 示例3:第三个参数除了布尔值`useCapture`,还可以是一个监听器配置对象,定制事件监听行为
- `capture`:布尔值,如果设为`true`,表示监听函数在捕获阶段触发,默认为`false`,在冒泡阶段触发。
- `once`:布尔值,如果设为`true`,表示监听函数执行一次就会自动移除,后面将不再监听该事件。该属性默认值为`false`。
- `passive`:布尔值,设为`true`时,表示禁止监听函数调用`preventDefault()`方法。如果调用了,浏览器将忽略这个要求,并在控制台输出一条警告。该属性默认值为`false`。
- `signal`:该属性的值为一个 AbortSignal 对象,为监听器设置了一个信号通道,用来在需要时发出信号,移除监听函数。
element.addEventListener('click', function (event) {
// 只执行一次的代码
}, {once: true});
addEventListener()方法可以为针对当前对象的同一个事件,添加多个不同的监听函数。这些函数按照添加顺序触发,即先添加先触发。如果为同一个事件多次添加同一个监听函数,该函数只会执行一次,多余的添加将自动被去除(不必使用removeEventListener()方法手动去除)。
function hello() {
console.log('Hello world');
}
document.addEventListener('click', hello, false);
document.addEventListener('click', hello, false);
// 只会打印一次Hello world
如果希望向监听函数传递参数,可以用匿名函数包装一下监听函数。
function print(x) {
console.log(x);
}
var el = document.getElementById('div1');
el.addEventListener('click', function () { print('Hello'); }, false);
推荐优点:
- 同一个事件可以添加多个监听函数。
- 能够指定在哪个阶段(捕获阶段还是冒泡阶段)触发监听函数。
- 除了 DOM 节点,其他对象(比如
window、XMLHttpRequest等)也有这个接口,它等于是整个 JavaScript 统一的监听函数接口。
6.2 removeEventListener()
EventTarget.removeEventListener()方法用来移除addEventListener()方法添加的事件监听函数。该方法没有返回值。
注意,removeEventListener()方法移除的监听函数,必须是addEventListener()方法添加的那个监听函数,而且必须在同一个元素节点,否则无效。
// 正确
div.addEventListener('click', listener, false);
div.removeEventListener('click', listener, false);
// 错误:回调函数不是同一个对象
div.addEventListener('click', function (e) {}, false);
div.removeEventListener('click', function (e) {}, false);
// 错误:第3个参数不一样
element.addEventListener('mousedown', handleMouseDown, true);
element.removeEventListener("mousedown", handleMouseDown, false);
6.3 dispatchEvent()
触发监听函数的执行,该方法返回一个boolean值,只要有一个监听函数调用了Event.preventDefault(),则返回false,否则返回true
para.addEventListener('click', hello, false);
var event = new Event('click');
para.dispatchEvent(event);
上述代码在当前节点手动触发了click事件。
如果dispatchEvent()方法的参数为空,或者不是一个有效的事件对象,将报错。
下面代码根据dispatchEvent()方法的返回值,判断事件是否被取消了。
var canceled = !cb.dispatchEvent(event);
if (canceled) {
console.log('事件取消');
} else {
console.log('事件未取消');
}
6.4 事件传播
一个事件发生后,会在子元素和父元素之间传播(propagation)。这种传播分成三个阶段。
- 第一阶段:从
window对象传导到目标节点(上层传到底层),称为“捕获阶段”(capture phase)。 - 第二阶段:在目标节点上触发,称为“目标阶段”(target phase)。
- 第三阶段:从目标节点传导回
window对象(从底层传回上层),称为“冒泡阶段”(bubbling phase)。
这种三阶段的传播模型,使得同一个事件会在多个节点上触发。
<div>
<p>点击</p>
</div>
var phases = {
1: 'capture',
2: 'target',
3: 'bubble'
};
var div = document.querySelector('div');
var p = document.querySelector('p');
div.addEventListener('click', callback, true);
p.addEventListener('click', callback, true);
div.addEventListener('click', callback, false);
p.addEventListener('click', callback, false);
function callback(event) {
var tag = event.currentTarget.tagName;
var phase = phases[event.eventPhase];
console.log("Tag: '" + tag + "'. EventPhase: '" + phase + "'");
}
// 点击以后的结果
// Tag: 'DIV'. EventPhase: 'capture'
// Tag: 'P'. EventPhase: 'target'
// Tag: 'P'. EventPhase: 'target'
// Tag: 'DIV'. EventPhase: 'bubble'
点击p元素,click事件被触发了四次:<div>节点的捕获阶段和冒泡阶段各1次,<p>节点的目标阶段触发了2次
事件顺序:点击p元素后,先捕获阶段再目标再冒泡。
依次为window、document、html、body、div、p,在冒泡阶段依次为p、div、body、html、document、window。
6.5 事件代理(事件委托)
由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。这种方法叫做事件的代理或者事件委托
// 未使用事件代理
<div id="box">
<input type="button" id="add" value="添加" />
<input type="button" id="remove" value="删除" />
</div>
window.onload = function(){
var Add = document.getElementById("add");
var Remove = document.getElementById("remove");
var Move = document.getElementById("move");
var Select = document.getElementById("select");
Add.onclick = function(){
alert('添加');
};
Remove.onclick = function(){
alert('删除');
};
}
// 使用事件代理
<div id="box">
<input type="button" id="add"/>
<input type="button" id="remove"/>
</div>
window.onload = function(){
var oBox = document.getElementById("box");
oBox.onclick = function (ev) {
var ev = ev || window.event;
var target = ev.target || ev.srcElement;
if(target.nodeName.toLocaleLowerCase() == 'input'){
switch(target.id){
case 'add' :
alert('添加');
break;
case 'remove' :
alert('删除');
break;
}
}
}
}
使用事件委托,Event对象提供了一个属性叫target,可以返回事件的目标节点,我们成为事件源,也就是说,target就可以表示为当前的事件操作的dom,但是不是真正操作dom,当然,这个是有兼容性的,标准浏览器用ev.target,IE浏览器用event.srcElement,此时只是获取了当前节点的位置,并不知道是什么节点名称,这里我们用nodeName来获取具体是什么标签名,这个返回的是一个大写的,我们需要转成小写再做比较(习惯问题)
事件委托优点:
- 适合事件委托的事件有:click,mousedown,mouseup,keydown,keyup,keypress
- 减少DOM操作的原因,减少浏览器的重绘(repaint)和重排(reflow),从而提高性能;
- 减少内存空间的占用率,因为每一个函数都是一个对象,对象越多,内存占有率就越大,自然性能就越差,使用事件委托,只需要在其父元素中定义一个事件就可以。