什么是事件其实不好描述。它可能是由用户发起的,例如鼠标事件、键盘事件、窗口事件、表单事件或页面加载过程中的事件。常见事件如:click、dbclick、keydown、keypress、keyup、mousemove、wheel、scroll、focus、blur、load、unload、abort、error、resize、change、select、submit,大概这些和相关。事件传播机制标准事件是如何触发和传播的?一般来说,事件传播分为三个阶段:1.事件捕获阶段(capturephase),也就是事件从窗口逐层传递到目标的阶段。2.目标阶段(targetphase),当事件到达事件发生的场景Stage3。事件冒泡阶段(bubblephase),与事件捕获阶段相反,事件从目标传递到窗口的阶段。IE8及以下的事件只冒泡不捕获。事件委托/代理其中提到较多的是事件委托。因为事件有冒泡机制,我们可以不在目标元素中监听事件,而是在其父元素中监听。一个比较经典的例子,一个有很多子项的无序列表,需要监听子项的点击事件。此时,我们将监听器绑定到父元素ul。当点击li元素时,由于事件冒泡机制,ul层也可以触发点击事件。但是如何判断你点击的是你想要的li元素,而不是ul元素本身,就用event对象event的target元素属性target来判断,即event.target。这样做的好处是:提高性能,从监控多个事件减少到监控一个事件,效率肯定会提高;可以动态自适应,比如在ul元素里面加一个li子元素,传统的方法需要加一个监听事件,使用事件委托可以适应所有的变化而不改变。事件实现方法原来的事件实现方法(DOM0)也称为DOM0事件处理方法。DOM0不是W3C标准。由于历史的发展,它一直存在,而且一直存在。很简单,给html元素加上on+事件的属性即可。按钮或者在js中给元素加上这个属性。varbtEle=document.getElementById('myButton');btEle.onclick=doSomething;这样做的好处是简单方便,兼容所有浏览器;缺点也很多,违反了行为和表现分离的原则,只能添加一个事件不能使用事件委托机制做更多的事情。IE事件的实现方法上面说过,IE8及以下的事件传播机制只有冒泡,没有捕获,实现方法是//monitoreventelement.attachEvent('on'+eventType,callback);//unlistenelement.detachEvent('on'+eventType,callback);//手动触发事件,兼容IE6-10element.fireEvent('on'+eventType)需要注意的是匿名函数不能unlistened。StandardEventImplementation(DOM2)DOM2规定的标准应该统一浏览器,然后不会有很多。目前W3C最新的标准是2015年发布的DOM4,我们先来看看。//监听事件element.addEventListener(eventType,callback,useCapture);//去掉监听element.removeEventListener(eventType,callback,useCapture);//手动触发事件element.dispatchEvent(eventType)//里面有个事件对象回调函数记录事件的属性functioncallback(event){//dosomething}因为标准的事件传播机制有捕获和冒泡阶段,所以这里最后一个参数useCapture表示在事件捕获阶段是否执行回调函数,以及默认为假。一般情况下,我们使用默认值,即在事件冒泡阶段执行函数。为什么?主要是为了兼容老版本的IE。事件公共属性我们已经知道事件回调函数中有一个事件对象参数。从第一部分我们已经看到了Event对象的继承关系,比如我们最常见的鼠标事件或者键盘事件,MouseEvent继承自UIEvent,UIEvent继承自Event。让我们参考最新的W3C规范来详细了解一下。[Constructor(DOMStringtype,optionalEventIniteventInitDict),Exposed=(Window,Worker)]interfaceEvent{//事件类型,如点击、提交、加载等readonly属性DOMString类型;//触发事件的目标元素只读属性EventTarget?目标;//事件传播的当前元素只读属性EventTarget?当前目标;const无符号短无=0;constunsignedshortCAPTURING_PHASE=1;constunsignedshortAT_TARGET=2;constunsignedshortBUBBLING3_PHASE=stage,枚举值如上readonly属性unsignedshorteventPhase;//防止事件的冒泡,当前元素的冒泡执行完后voidstopPropagation();//立即停止冒泡,包括当前元素和其他回调Event//DOM3新增了一个属性voidstopImmediatePropagation();//是否处于冒泡阶段readonly属性booleanbubbles;//是否可以取消,使用preventDefault取消readonly属性booleancancelable;//取消当前元素的默认事件,前提是cancelable为truevoidpreventDefault();//是否已经preventDefaulted,DOM3新增一个属性readonlyattributebooleandefaultPrevented;//事件是否由用户触发[Unforgeable]readonlyattributebooleanisTrusted;//当前时间戳readonlyattributDOMTimeStamp时间戳;voidinitEvent(DOMStringtype,booleanbubbles,booleancancelable);};dictionaryEventInit{booleanbubbles=false;布尔可取消=假;};[Constructor(DOMStringtype,optionalMouseEventIniteventInitDict)]interface:MouseEvent{UIEvent//相对于屏幕的x,y坐标readonly属性longscreenX;只读属性长屏幕Y;//相对于浏览器可见区域的x,y坐标readonly属性longclientX;只读属性longclientY;//是否按下这些特殊键readonly属性booleanctrlKey;只读属性布尔值shiftKey;只读属性布尔值altKey;只读属性布尔元键;//按下哪个鼠标键,0主键,1滚轮,2附加键,只在mouseup和mousedown事件中有效readonly属性shortbutton;//激活的鼠标可以是多个,0没有,1个主键,2个附加键,4个滚轮,8个后退?,16前锋?//也可以加上,比如按下主键和滚轮,即1+4=5个readonly属性unsignedshortbuttons;只读属性EventTarget?相关目标;booleangetModifierState(DOMStringkeyArg);};上面是规范,但是实际上browse浏览器的实现好像有很多杂七杂八的属性。以下是Chrome56的输出。MouseEvent感觉比较养眼,常用的不多,所以也看看场景吧。一般比较常用的是target、preventDefalut、stopPropagation、type、coordinates等属性。由于浏览器的历史原因,关于鼠标坐标的属性确实有很多对:属性描述了x,y在整个浏览器窗口screenX中的位置,screenY在整个屏幕clientX中的位置,以及位置clientY在整个浏览器窗口中的位置与x,y属性相同。layerX,layerY是鼠标在整个元素区域内的位置(但算上滚动的距离),不随页面滚动变化时,pageX,pageY在整个页面的位置,不随页面滚动变化时随页面滚动改变offsetX,offsetY在整个元素区域,鼠标的位置,当不随页面滚动改变时movementX,movementY鼠标在两个事件之间的位移差最好用两对W3C规范中的attribute,这是最安全最保险的选项,screenX,screenY,clientX,clientY。简单兼容实现主要考虑IE8及以下版本的兼容性。functionaddEventListener(target,event,callback)if(window.addEventListener){target.addEventListener(event,callback)}elseif(window.attachEvent){target.attachEvent('on'+event,callback);}else{target['on'+event]=callback;}}事件执行顺序我们用一个简单的例子在Chrome56中测试。点击父div时,输出如下:parentcapturetargetparentdom0jstargetparentbubbletarget点击子div时,输出如下:parentcapturecapturechilddom0htmltargetchildbubbletargetchildcapturetargetparentdom0jsbubbleparentbubblebubble当你不断调整那些事件的顺序,或者引入js的onclick功能时,你会发现如下规律:1.当html中有onclick事件,js中有onclick事件时,js中的onclick事件会覆盖html中的事件。因为它们实际上是相同的属性。2.目标元素的事件执行顺序与定义的顺序一致,不是先捕获再冒泡的顺序。html中定义的事件优先。3、非目标事件的函数执行遵循先捕获后冒泡的规律,DOM0级元素定义的事件在冒泡阶段会先执行,不管是在html还是js中定义的。另外,如果一个事件被多次绑定,则只会执行一次。this事件指向DOM0层的this情况如下。而attachEvent中的this指向window,addEventListener指向当前元素。自定义事件机制的抽象就是发布-订阅模型。我们可以使用自定义事件来实现我们想要的发布-订阅模型。