Created By JishuBao on 2019-04-17 12:38:22
Recently revised in 2019-04-17 17:38:22javascript
欢迎你们来到技术宝的掘金世界,您的star是我写文章最大的动力!GitHub地址 html
不知道各位小伙伴在平时撸码中会不会常常看到诸如
event.preventDefault()、event.stopPropagation()
之类的方法,可能一开始看到只是随便的百度了下,大概了解这是干吗的,可是时间长了你只记得要点击事件的时候加上一下,可是这对于咱们对代码的掌控仍是远远不够,因此技术宝深入百度,带你们理一理这个javascript大头:事件机制java
什么是事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间。js与html的交互就是经过事件实现的。可使用侦听器(或处理程序)来预订事件,以便事件发生时执行相应的代码。git
事件最先出现于IE3、Netscape Navigator2,IE九、Firefox、Opera、Safari、Chrome所有已经实现DOM2事件.IE8是最后一个仍然使用其专有事件系统的浏览器。github
事件流描述的是从页面中接收事件的顺序。可是,IE和Netscape提出了差很少彻底相反的概念。IE的事件流是事件冒泡流,而Netscape的事件流是事件捕获流。浏览器
IE9/Opera/Firefox/Chrome/Safari都支持DOM事件流;IE8以及更早版本不支持DOM事件流。bash
IE的事件流叫作冒泡事件,即事件开始时由最具体的元素接收,而后逐级向上传播到较为不具体的节点。click事件沿dom树向上传播,在每一级节点上都会发生,直至传播到document对象。dom
IE5.5以及更早版本中的事件冒泡会跳过<html>元素,从body直接跳到document,
IE9/Firefox/Chrome/Safari则将事件一直冒泡到window对象
函数
Netscape提出了另外一种事件流叫作事件捕获。事件捕获的用意在于在事件达到预约目标以前捕获它。post
IE9/Safari/Chrome/Opera和Firefox目前也都支持这种事件流模型。尽管"DOM2级事件"规范要求事件应该从document对象开始传播,可是这些浏览器都是从window对象开始捕获事件的。
"dom2级事件"规定的事件流包括三个阶段:
事件捕获阶段、处于目标阶段和事件冒泡阶段
。首先发生的是事件捕获,为截获事件提供了机会。而后实际的目标接受到事件。最后一个阶段是冒泡阶段,能够在这个阶段对事件做出相应。
在dom事件流中,实际的目标在捕获阶段不会接收到事件。这意味着在捕获阶段,事件从document到html再到body后就中止了,下一个阶段是处于目标阶段,因而事件再div上发生,并在事件处理中被当作冒泡阶段的一部分。而后,冒泡阶段发生,事件又传播回文档。
虽然“dom2级事件”规范明确要求捕获阶段不会涉及到事件目标,可是IE九、Safari、Chrome、Safari及更高版本都会在捕获阶段触发事件对象上的事件。结果,就有2个机会在目标对象上操做事件。
事件就是用户或浏览器自身执行的某种动做。诸如click、load、mouseover,都是事件的名字。而相应某个事件的函数就叫作事件处理程序(事件侦听器)。事件处理程序以“on”开头,所以click的事件处理程序就是onclick。为事件指定处理程序的方式有好几种:
某个元素支持的每种事件,均可以使用一个与相应事件处理程序同名的Html特性来指定。这个特性的值应该是可以执行的js代码。
<input type="button" value="click me" onclick="alert('clicked')"/>
复制代码
因为值是js,因此不能使用未转义的HTML语法字符,如:&、""、>、<,为了不使用HTML实体,这里使用了单引号。若是想使用双引号:
<input type="button" value="click me" onclick="alert("clicked me")" />
复制代码
除了包含具体的执行动做外,还能够调用其余地方定义的脚本(括号不可不加)
<input type="button" value="click me" onclick="showMessage()" />
<script type="text/javascript"> function showMessage(){ alert('Hello world') } </script>
复制代码
事件处理程序中的代码在执行时,有权访问全局做用域中的任何代码
这样指定事件处理程序会建立一个封装着元素属性值的函数。这个函数中有一个局部变量event,也就是事件对象。
<input type="button" value="click me" onclick="alert(event.type)"/>//click
复制代码
在这个函数内部,this的值等于事件的目标元素。
<input type="button" value="click me" onclick="alert(this.value)"/>//click me
复制代码
在这个函数内部,能够像访问局部变量同样访问document以及该元素自己的成员。这个函数使用with像下面这样扩展做用域:
function(){
with(document){
with(this){
//元素属性值
}
}
}
复制代码
这样以来,访问本身的属性简单的多
<input type="button" value="click me 5" onclick="alert(type)"/>//button
复制代码
若是当前元素是一个表单输入元素,则:
function(){
with(document){
with(this.form){
with(this){
//元素属性值
}
}
}
}
复制代码
则不须要引用表单元素就能访问其余表单字段
<form method="post">
<input type="text" name="username" value="jsbao"> <input type="button" value="Echo" onclick="alert(username.value)"> //jsbao </form> 复制代码
这样存在3个缺点:
时差问题,用户可能会在html元素一出现就触发相应的事件,但当时的事件处理程序有可能尚不具有执行条件。为避免没必要要的错误,咱们通常会用try-catch块包住:
<input type="button" value="click me" onclick="try{showMessage();}catch(ex){}"/>
复制代码
扩展事件处理程序的做用域链在不一样浏览器中会致使不一样结果,由于不一样Js引擎遵循的标识符解析规则略有差别!
html与js代码耦合度过高!
经过js指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。每一个元素都是本身的事件处理程序属性,这些属性一般所有小写,例如onclick。将这种属性的值设置为一个函数,就能够指定事件处理程序。
<input type="button" value="click me 5" id="button1"/>
var button1=document.getElementById('button1')
button1.onclick=function(){
alert('hello world')
}
复制代码
要注意,在这些代码运行以前不会指定事件处理程序,所以若是这些代码在页面中处于按钮后面,就有可能在一段时间内怎么单击都没反应。
使用dom0级方法指定的事件处理程序被认为是元素的方法。所以,这时候的事件处理程序是在元素的做用域中进行,程序中的this引用当前元素。
<input type="button" value="click me 5" id="button2"/>
var button2=document.getElementById('button2');
button2.onclick=function(){
alert(this.id)//button2
}
复制代码
这种方式添加的事件处理程序会在事件流的冒泡阶段被处理
也能够经过dom0级方法指定的事件处理程序,只要像下面这样将事件处理程序属性的值设置为null便可:
btn.onclick=null;//删除事件处理程序
复制代码
设置为null之后,再单击按钮将不会有任何动做发生
dom2级事件定义了俩个方法:
addEventListener()
和removeEventListener()
,分别用于处理指定和删除事件处理程序。全部dom节点都包含这2个方法,而且他们都接受3个参数:要处理的事件名、做为事件处理程序的函数、一个布尔值,最后这个参数若是是true,表示在捕获阶段调用事件处理程序;若是是false,表示在冒泡阶段调用事件处理程序。
<input type="button" value="click me 5" id="button3"/>
var button3=document.getElementById('button3');
button3.addEventListener("click",function(){
alert(this.id)//button3
},false)
复制代码
与dom0级事件同样,这里添加的事件处理程序也是在其依附的元素的做用域中运行,使用dom2级的好处是能够添加多个事件处理程序。
<input type="button" value="click me 5" id="button3"/>
var button3=document.getElementById('button3');
button3.addEventListener("click",function(){
alert(this.id)//button3
},false)
button3.addEventListener("click",function(){
alert(this.value)//click me 5
},false)
//会按次序执行
复制代码
经过addEventListener添加的事件处理程序只能经过removeEventListener来移除,而传入的参数与添加处理程序使用的参数相同,这意味着经过addEventListener添加的匿名函数将没法移除;
button3.addEventListener("click",function(){
alert(this.value)
},false)
button3.removeEventListener("click",function(){
alert(this.value)
},false)
//无效,由于传入的匿名函数不同
复制代码
var handler=function(){
alert(this.value);
}
button3.addEventListener("click",handler,false);
button3.removeEventListener("click",handler,false);//有效!
复制代码
大多数状况下,都是将事件处理程序添加到事件流的冒泡阶段,这样能够最大限度的兼容各类浏览器
IE、Firefox、Safari、Chrome、Opera支持dom2级事件处理程序
IE实现了俩个方法:attachEvent()/detachEvent()
。第一个参数为事件程序处理名称,第二个参数为事件处理函数名称。因为IE8以及更早版本只支持冒泡,因此经过attachEvent()添加的事件处理程序都会被添加到冒泡阶段。
以下为给按钮添加事件处理程序:
var btn=document.getElementById('myBtn');
btn.attachEvent("onclick",function(){
alert('clicked')
})
复制代码
与dom0事件的主要区别在于attachEvent的做用域会在全局做用域内执行,即
this===window
var btn=document.getElementById('myBtn');
btn.attachEvent("onclick",function(){
alert(this===window);//true
})
复制代码
与addEventListener相似,也能够为一个元素添加多个事件处理程序,可是不一样的是,他是按照相反的顺序被触发,detachEvent用法和removeEventListener用法同样,再也不赘述。
var btn=document.getElementById('myBtn');
btn.attachEvent("onclick",function(){
alert("clicked")
})
btn.attachEvent("onclick",function(){
alert("hello world!")
})
复制代码
支持IE事件处理程序的浏览器有IE和Opera
要保证处理事件的代码能带大多数浏览器下保持一致的运行,只需关注冒泡阶段
这个方法的做用是视状况分别使用DOM0级方法、DOM2级方法、或者IE方法来添加事件。三个参数依次是:要操做的对象、事件名称、事件处理程序函数
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;
}
}
复制代码
这个方法的做用是移除以前添加的事件处理程序,参数和addHandler的参数一一对应
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
}
}
复制代码
在触发dom上的某个事件时,会产生一个事件对象event
。这个对象包含着全部与事件有关的信息。全部浏览器都支持event对象,但支持方式不一样。
兼容dom的浏览器会将一个event对象传入事件处理程序中。event对象包含与建立它的特定事件有关的属性和方法,不过,全部event对象包含下表所列的成员。
属性/方法 | 类型 | 读/写 | 说明 |
---|---|---|---|
bubbles | Boolean | 只读 | 代表事件是否冒泡 |
cancelable | Boolean | 只读 | 表面是否能够取消事件的默认行为 |
currentTarget | Element | 只读 | 其事件处理程序当前正在处理事件的那个元素 |
defaultPrevented | Boolean | 只读 | 为true表示以及调用了preventDefault()(DOM3级事件新增) |
detail | Integer | 只读 | 与事件相关的细节信息 |
eventPhase | Integer | 只读 | 调用事件处理程序的阶段 1.捕获2.处于目标3.冒泡 |
preventDefault | Function | 只读 | 取消事件的默认行为。若是cancelable是true,则可使用这个方法 |
stopImmediatePropagation | Function | 只读 | 取消事件的进一步冒泡或捕获,同时阻止任何事件处理程序被调用(dom3级事件中新增) |
stopPropagation | Function | 只读 | 取消事件的进一步冒泡或捕获.若是bubbles是true,则可使用这个方法 |
target | Element | 只读 | 事件的目标 |
trusted | Boolean | 只读 | 为true表示事件是浏览器生成的,为false表示事件是由开发人员经过js建立的(dom3级事件中新增) |
type | String | 只读 | 被触发的事件的类型 |
view | AbstractView | 只读 | 与事件关联的抽象视图。等同与发生事件的window对象 |