一、事件的概念
事件指文档或者浏览器窗口中发生的一些特定的交互。具体来说就是鼠标点击、键盘输入等用户操作。
二、事件三要素
可以分为事件源、事件类型和事件对象;
事件源也是指事件元素。指的是用户发生操作的那个元素。
事件类型是指鼠标点击事件、键盘事件、滚动事件等等;
事件对象是指当某个事件触发时产生的对象。不同事件产生的事件对象不同。
事件对象的兼容:var e=evt||event;
三、事件流
概念:当某个事件执行时,从子元素向父元素触发或者从父元素向子元素触发称为事件流。
事件流分为三个阶段:事件捕获阶段、目标阶段和事件冒泡阶段。在DOM中,实际的目标在捕获阶段不会接受到事件,所以在捕获阶段,事件从document到html再到body后就停止了。下一个阶段是处于目标阶段,于是事件在div上发生,并在事件处理中被看成冒泡阶段的一部分。
分为事件冒泡和事件捕获。
四、事件冒泡
概念:事件开始时由最具体的元素接受,然后逐级向上传播到较为不具体的节点。比如事件是由div接受的,然后向上冒泡,到body、html、document、window。
注意:并不是所有事件都会产生冒泡问题, onfocus onblur onload不会产生冒泡问题
五、事件捕获
概念:由父元素向子元素触发。也就是由顶层的元素向下传播,最具体的元素最后接受到事件。与事件冒泡的顺序正好相反。
六、事件处理程序
概念:响应某个事件的函数叫做事件处理程序
1、DOM0级事件处理程序
通过js指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。
<button id="btn">点击</button>
var toBtn=document.getElementById("btn");
toBtn.onclick=function(){
console.log(this.id) //btn
}
这种方法被认为是元素的方法,这种情况下事件处理程序是在元素的作用域中运行。也就是this引用的当前元素。通过this可以访问元素的所有属性和方法。以这种方式添加的事件处理程序会在事件流的冒泡阶段被处理。并且可以删除通过DOM0级方法指定的事件处理程序。
toBtn.onclick=null;
2、DOM2级事件处理程序
"DOM2"定义了两个方法。用于处理指定和删除事件程序的操作。addEventListener()和removeEventListener()。它有三个参数。分别是事件名、事件处理程序和一个布尔值。这个布尔值如果是true,表示在捕获阶段调用事件处理程序;如果是false,表示在冒泡阶段调用事件处理程序。
<button id="btn">点击</button>
var toBtn=document.getElementById("btn");
toBtn.addEventListener('click',function(){console.log(this.id)},false)//btn
使用DOM2级方法添加事件处理程序的主要好处是可以添加多个事件处理程序。
通过addEventListener()添加的事件处理程序只能使用removeEventListener()来移除;移除时传入的参数与添加程序时使用的参数相同。注意的是通过addEventListener()添加的匿名函数将无法移除。
3、IE事件处理程序
IE中与DOM中类似的两个方法:attachEvent()和detachEvent()。两个方法接受相同的两个参数:事件处理程序与事件处理程序函数名。IE8及更早版本只支持事件冒泡,所以通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段。
<button id="btn">点击</button>
var toBtn=document.getElementById("btn");
toBtn.attachEvent('onclick',function(){
console.log('click')//click
})
在IE中使用attachEvent()与使用DOM0级方法的主要区别在于事件程序的作用域。在使用DOM0级方法的情况下,事件处理程序会在其所属元素的作用域内运行;在使用attachEvent()方法的情况下,事件处理程序会在全局作用域中运行,因此this等于window。
<button id="btn">点击</button>
var toBtn=document.getElementById("btn");
toBtn.attachEvent('onclick',function(){
console.log(this===window)//true
})
与addEventListener()类似,attachEvent()方法也可以用来为一个元素添加多个事件处理程序。不过与DOM方法不一样,IE中添加的事件处理程序不是以添加它们顺序执行,而是以相反的顺序被触发。
4、跨浏览器的事件处理程序
var EventUtil = {
addHandler:function (element,type,handler) {
if(element.addEventListener){
element.addEventListener(type, handler,false);
} else if (element.attachEvent) {
element.attachEvent("on" + type,handler);
}else {
element["on" + type] = handler;
}
},
removeHandler:function (element,type,handler) {
if (element.removeEventListener) {
element.removeEventListener(type, handler,false);
} else if (element.detachEvent) {
element.detachEvent("on" + type,handler);
}else {
element["on" + type] = null;
}
}
};
创建addHandler方法,来处理DOM0、DOM2和IE方法来添加事件;removeHandler来移除添加的事件处理程序。
var btn = document.getElementById("myBtn");
var handler = function () {
alert("Clicked");
};
EventUtil.addHandler(btn,"click",handler);
EventUtil.removeHandler(btn,"click",handler);
七、事件对象
触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息。包括导致事件的元素、事件的类型以及其他与特定事件相关的信息。
1、DOM中的事件对象
兼容DOM的浏览器将一个event对象传入到事件处理程序中。
var toBtn=document.getElementById("btn"); toBtn.onclick=function(event){ console.log(event.type) //click } toBtn.addEventListener('click',function(event){ console.log(event.type) //click },false)
图片来自网络侵删
详细属性和方法链接:www.cnblogs.com/lbnnbs/p/66…
在事件处理程序内部,对象this等于currentTarget的值,而target则只包含事件的实际目标。如果直接将事件处理程序指定给了目标元素,则this、currentTarget和target包含相同的值。
var toBtn=document.getElementById("btn"); toBtn.onclick=function(event){ console.log(event.target===this); //true console.log(event.currentTarget===this)//true }
如果事件处理程序存在于按钮的父节点上,这些值是不相等的。
var toBtn=document.getElementById("btn"); document.body.onclick=function(event){ console.log(event.target===toBtn); //true console.log(event.currentTarget===this)//true console.log(this==document.body) }
注意:只有在事件处理程序执行期间,event对象才会存在;一旦事件处理程序执行完成,event对象就会被销毁。
2、IE中的事件对象
DOM级方法添加事件处理程序,event作为window对象的一个属性存在。
var toBtn=document.getElementById("btn"); toBtn.onclick=function(){
var event=window.event;
console.log(event.type) //click }
事件处理程序是使用attachEvent()添加的,就会有一个event对象作为参数传入函数中。
<button id="btn">点击</button>
var toBtn=document.getElementById("btn");
toBtn.attachEvent('onclick',function(event){
console.log(event.type) //click
})
因为事件处理程序的作用域是根据指定它的方式来确定的,所以不能认为this 会始终等于事件目标。故而,最好还是使用event.srcElement 比较保险。例如:
btn.onclick = function(){
alert(window.event.srcElement === this); //true
};
btn.attachEvent(“onclick”, function(event){
alert(event.srcElement === this); //false
});
3、跨浏览器的事件对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<button id="btn">Click me</button>
<script>
var EventUtil = {
//添加DOM方法
addHandler:function(element,type,handler){
//是否支持DOM2级方法
if(element.addEventListener){
element.addEventListener(type,handler,false);
}
//兼容IE8以及更早版本
else if(element.attachEvent){
element.attachEvent('on'+type,handler);
}
//DOM0级方法
else{
element['on'+click] = handler;
}
},
//删除DOM方法
removeHandler:function(element,type,handler){
if(element.removeEventListener){
element.removeEventListener(type,handler,false);
}else if(element.detachEvent){
element.detachEvent('on'+type,handler);
}else{
element['on'+type] = null;
}
},
//获得事件对象event
getEvent:function(event){
return event || window.event;
},
//获得事件目标
getTarget:function(event){
return event.target || event.srcElement;
},
//取消事件冒泡
stopPropagation:function(event){
if(event.stopPropagation){
event.stopPropagation();
}else{
//兼容IE以及低版本
event.cancleBubble = true;
}
},
//取消默认行为
preventDefault:function(event){
if(event.preventDefault){
event.preventDefault();
}else{
event.returnValue = false;
}
}
};
var btn = document.getElementById('btn');
btn.onclick = function(event){
alert('clicked');
//获取事件对象
event = EventUtil.getEvent(event);
//获取对象目标
var target = EventUtil.getTarget(event);
//取消事件冒泡,从而'body clicked 不会弹出'
EventUtil.stopPropagation(event);
};
document.body.onclick = function(){
alert('body clicked!');
}
</script>
</body>
</html>
八、事件触发器(Event emitter)
事件派发器是一种模式,它监听一个已命名的事件,触发回调,然后发出该事件并附带一个值。这被称为“发布/订阅”模型或监听器。
发布者订****阅者模式
也可以称之为消息机制,定义了一种依赖关系,这种依赖关系可以理解为1对N(注意:不一定是1对多,有时候也会1对1),观察者们同时监听某一个对象相应的状态变换,一旦变化则通知到所有观察者,从而触发观察者相应的事件,该设计模式解决了主体对象与观察者之间功能的耦合
events 模块只提供了一个对象: events.EventEmitter。EventEmitter 的核心就是事件触发与事件监听器功能的封装。
//引入events模块
var events=require('events');
//创建eventEmitter对象
var eventEmitter=new events.EventEmitter();
eventEmitter.on('say',function(){
console.log('事件触发')
})
setTimeout(()=>{
eventEmitter.emit('say')},1000)
运行这段代码,1 秒后控制台输出了 ' 事件触发'。其原理是 event 对象注册了事件 say 的一个监听器,然后我们通过 setTimeout 在 1000 毫秒以后向 event 对象发送事件say,此时会调用say 的监听器。
EventEmitter 的每个事件由一个事件名和若干个参数组成,事件名是一个字符串,通常表达一定的语义。对于每个事件,EventEmitter 支持 若干个事件监听器。
当事件触发时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递。
运行顺序:
emit(发布)——apply或者call(内部运行)——on(订阅)
注意:emit触发事件,并将参数传给事件的处理函数;
on:监听event,获得emit传递的函数,触发时调用callback函数。
参考链接:www.runoob.com/nodejs/node…
参考链接:www.cnblogs.com/penghuwan/p…
componentDidMount() { //注册监听动画的事件 this.eventEmitter = this.rootProps.emitter.addListener('eventAni', this.eventAni); //注册监听tab切换的事件 this.eventEmitter = this.rootProps.emitter.addListener('tabChange', this.tabChange); }
emit可应用于点击事件或者其他任何函数中
this.props.rootProps.emitter.emit('eventAni', { eventType: 'tabChangeScroll', parentId: this.props.id, currIndex: index })
九、自定义事件
js 一般事件像是click、blur、focus等等。除了这些之外还可以自己定义事件,但是自定义事件同样需要自己定义触发机制。
简单的创建事件方法,一般用Event构造器;
var event=new Event('eventName',{"bubbles":true,"cancelable":false})
document.dispatchEvent(event);
如果传递数据的话,需要使用CustomEvent构造器;
var myEvent = new CustomEvent('event_name', {
detail:{
// 将需要传递的数据写在detail中,以便在EventListener中获取
// 数据将会在event.detail中得到
},
});
事件的监听
JS的EventListener是根据事件的名称来进行监听的,比如我们在上文中已经创建了一个名称为**‘event_name’** 的事件,那么当某个元素需要监听它的时候,就需要创建相应的监听器:
//假设listener注册在window对象上window.addEventListener('event_name',function(event){
// 如果是CustomEvent,传入的数据在event.detail中
console.log('得到数据为:', event.detail);})
window对象上就有了对**‘event_name’** 这个事件的监听器,当window上触发这个事件的时候,相关的callback就会执行。
事件的触发
if(window.dispatchEvent){
window.dispatchEvent(myEvent)
}else{
window.fireEvent(myEvent)
}
demo:
<!DOCTYPE html><html> <head lang="zh-CN"> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title></title> <style> .button { width: 500px; height: 500px; background-color: antiquewhite; margin: 10px; text-align: center; line-height: 500px; } </style> </head> <body> <div class="button">Button</div> <script> //创建封装 if (!window.CustomEvent) { window.CustomEvent = function (type, config) { config = config || { bubbles: false, cancelable: false, detail: undefined, }; var e = document.createEvent("CustomEvent"); e.initCustomEvent( type, config.bubbles, config.cancelable, config.detail ); return e; }; window.CustomEvent.prototype = window.Event.prototype; } var btn = document.querySelector(".button"); var ev = new CustomEvent("testEvent", { bubbles: "true", cancelable: "true", detail: "yuanhl", }); btn.addEventListener( "testEvent", function (event) {
console.log(event.detail);
console.log(event.bubbles); console.log(event.cancelable); }, false ); btn.dispatchEvent(ev); </script> </body></html>
参考资料:developer.mozilla.org/zh-CN/docs/…
十、事件委托
事件委托:某个事件让其他元素来完成
不是所有事件都可以实现事件委托 常见到也就那么几个 :click、mousedown、mouseup、keydown、keyup和keypress
<ul id="ul1"> <li>111</li> <li>222</li> <li>333</li> <li>444</li> </ul>window.onload = function () { var oUl = document.getElementById("ul1"); oUl.onclick = function (ev) { var ev = ev || window.event; var target = ev.target || ev.srcElement; if (target.nodeName.toLowerCase() == 'li') { alert(target.innerHTML); } } }