EventTarget接口、事件模型
需要理解的部分: 1、addEventListene三个参数的含义 2、DOM的this指向 3、事件的传播 4、事件的代理
addEventListener()
target.addEventListener(type, listener[, useCapture]);
- type:事件名称,大小写敏感。
- listener:监听函数。事件发生时,会调用该监听函数。
useCapture:布尔值,如果设为true,表示监听函数将在捕获阶段(capture)触发(参见后文《事件的传播》部分)。该参数可选,默认值为false(监听函数只在冒泡阶段被触发)。
事件的传播

一个事件发生后,会在子元素和父元素之间传播(propagation)。这种传播分成三个阶段。第一阶段:从window对象传导到目标节点(上层传到底层),称为“捕获阶段”(capture phase)。
- 第二阶段:在目标节点上触发,称为“目标阶段”(target phase)。
- 第三阶段:从目标节点传导回window对象(从底层传回上层),称为“冒泡阶段”(bubbling phase)。
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'
上面代码表示,click事件被触发了四次:
节点的目标阶段触发了2次。
- 捕获阶段:事件从向
传播时,触发
的click事件; - 目标阶段:事件从到达
时,触发
的click事件;
- 冒泡阶段:事件从
传回
时,再次触发的click事件。
其中,
节点有两个监听函数(addEventListener方法第三个参数的不同,会导致绑定两个监听函数),因此它们都会因为click事件触发一次。所以,
会在target阶段有两次输出。
注意,浏览器总是假定click事件的目标节点,就是点击位置嵌套最深的那个节点(本例是
节点)。所以,
节点的捕获阶段和冒泡阶段,都会显示为target阶段。
事件传播的最上层对象是window,接着依次是document,html(document.documentElement)和body(document.body)。也就是说,上例的事件传播顺序,在捕获阶段依次为window、document、html、body、div、p,在冒泡阶段依次为p、div、body、html、document、window。
事件代理
在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。这种方法叫做事件的代理
var ul = document.querySelector('ul');ul.addEventListener('click', function (event) {if (event.target.tagName.toLowerCase() === 'li') {// some code}});
event.stopPropagation();阻止冒泡的方法
<!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>#container {width: 100px;height: 100px;border: 1px solid red;}</style></head><body><div id="container"><button id="btn" attr="111">点击</button></div><script>var button = document.getElementById('btn');button.addEventListener('click', function (event) {event.stopPropagation();alert('Hello world');}, false);var div = document.getElementById('container');// 这里重点理解捕获阶段和冒泡阶段的区别div.addEventListener('click', hellos, false);function hellos() {alert('Hello world');}</script></body></html>
Event对象
重点掌握实例方法:
- Event.preventDefault()
- Event.stopPropagation()
- Event.stopImmediatePropagation()
Event.preventDefault()
Event.preventDefault方法取消浏览器对当前事件的默认行为。该方法生效的前提是,事件对象的cancelable属性为true,如果为false,调用该方法没有任何效果。
该方法只是取消事件对当前元素的默认影响,不会阻止事件的传播。如果要阻止传播,可以使用stopPropagation()或stopImmediatePropagation()方法。
// <input type="checkbox" id="my-checkbox" />var cb = document.getElementById('my-checkbox');cb.addEventListener('click',function (e){ e.preventDefault(); },false);
Event.stopPropagation()
stopPropagation方法阻止事件在 DOM 中继续传播,防止再触发定义在别的节点上的监听函数,但是不包括在当前节点上其他的事件监听函数。
Event.stopImmediatePropagation()
Event.stopImmediatePropagation方法阻止同一个事件的其他监听函数被调用,不管监听函数定义在当前节点还是其他节点。也就是说,该方法阻止事件的传播,比Event.stopPropagation()更彻底。
鼠标事件
MouseEvent.clientX,MouseEvent.clientY
MouseEvent.clientX属性返回鼠标位置相对于浏览器窗口左上角的水平坐标(单位像素),MouseEvent.clientY属性返回垂直坐标。这两个属性都是只读属性。
MouseEvent.movementX,MouseEvent.movementY
MouseEvent.movementX属性返回当前位置与上一个mousemove事件之间的水平距离(单位像素)。数值上,它等于下面的计算公式。
currentEvent.movementX = currentEvent.screenX - previousEvent.screenX
MouseEvent.movementY属性返回当前位置与上一个mousemove事件之间的垂直距离(单位像素)。数值上,它等于下面的计算公式。
currentEvent.movementY = currentEvent.screenY - previousEvent.screenY。
MouseEvent.screenX,MouseEvent.screenY
MouseEvent.screenX属性返回鼠标位置相对于屏幕左上角的水平坐标(单位像素)
MouseEvent.screenY属性返回垂直坐标。这两个属性都是只读属性。
// HTML 代码如下// <body onmousedown="showCoords(event)">function showCoords(evt) {console.log('screenX value: ' + evt.screenX + '\n','screenY value: ' + evt.screenY + '\n');}
MouseEvent.offsetX,MouseEvent.offsetY
MouseEvent.offsetX属性返回鼠标位置与目标节点左侧的padding边缘的水平距离(单位像素),MouseEvent.offsetY属性返回与目标节点上方的padding边缘的垂直距离。这两个属性都是只读属性。
/* HTML 代码如下<style>p {width: 100px;height: 100px;padding: 100px;}</style><p>Hello</p>*/var p = document.querySelector('p');p.addEventListener('click',function (e) {console.log(e.offsetX);console.log(e.offsetY);},false);
MouseEvent.pageX,MouseEvent.pageY
MouseEvent.pageX属性返回鼠标位置与文档左侧边缘的距离(单位像素),MouseEvent.pageY属性返回与文档上侧边缘的距离(单位像素)。它们的返回值都包括文档不可见的部分。这两个属性都是只读。
/* HTML 代码如下<style>body {height: 2000px;}</style>*/document.body.addEventListener('click',function (e) {console.log(e.pageX);console.log(e.pageY);},false);
拖拉事件
1、拖拽的几个事件
实现元素块的任意拖拉拽
<!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><div class="move-container"><div class="move" style="position:absolute; width:100px; height:100px; background:gray"></div></div><script>var moveElem = document.querySelector('.move'); //待拖拽元素var dragging; //是否激活拖拽状态var tLeft, tTop; //鼠标按下时相对于选中元素的位移//监听鼠标按下事件document.addEventListener('mousedown', function (e) {if (e.target == moveElem) {dragging = true; //激活拖拽状态var moveElemRect = moveElem.getBoundingClientRect();tLeft = e.clientX - moveElemRect.left; //鼠标按下时和选中元素的坐标偏移:x坐标tTop = e.clientY - moveElemRect.top; //鼠标按下时和选中元素的坐标偏移:y坐标}});//监听鼠标放开事件document.addEventListener('mouseup', function (e) {dragging = false;});//监听鼠标移动事件document.addEventListener('mousemove', function (e) {if (dragging) {var moveX = e.clientX - tLeft,moveY = e.clientY - tTop;moveElem.style.left = moveX + 'px';moveElem.style.top = moveY + 'px';}});</script></body></html>
其他事件
1、资源事件 2、session 历史事件 3、窗口事件【scroll】、resize 事件 4、剪贴板事件
资源事件
beforeunload事件在窗口、文档、各种资源将要卸载前触发。它可以用来防止用户不小心卸载资源。
大多数浏览器在对话框中不显示指定文本,只显示默认文本。
window.addEventListener('beforeunload', function (e) {var confirmationMessage = '确认关闭窗口?';e.returnValue = confirmationMessage;return confirmationMessage;});
popstate 事件
popstate事件在浏览器的history对象的当前记录发生显式切换时触发。
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: nullhistory.go(2); // state: {"page":3}
hashchange 事件
hashchange事件在 URL 的 hash 部分(即#号后面的部分,包括#号)发生变化时触发。该事件一般在window对象上监听。
// 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
scroll 事件
这里需要注意解决大量触发的事件。也就是常说的节流
(function () {var throttle = function (type, name, obj) {var obj = obj || window;var running = false;var func = function () {if (running) { return; }running = true;requestAnimationFrame(function() {obj.dispatchEvent(new CustomEvent(name));running = false;});};obj.addEventListener(type, func);};// 将 scroll 事件转为 optimizedScroll 事件throttle('scroll', 'optimizedScroll');})();window.addEventListener('optimizedScroll', function() {console.log('Resource conscious scroll callback!');});
resize 事件
resize事件在改变浏览器窗口大小时触发,主要发生在window对象上面。
可以动态计算iframe的大小视口
var resizeMethod = function () {if (document.body.clientWidth < 768) {console.log('移动设备的视口');}};window.addEventListener('resize', resizeMethod, true);
剪贴板事件
需求:选中文字并把文字复制到粘贴板中
<input type="text" value="被选中的对象" id="input"/><script>const inputElement = document.querySelector('#input');inputElement.select();inputElement.addEventListener('click',function () {document.execCommand('copy');})</script>
