上一篇是谈的事件流,博客地址:点我;这次我们说说具体的事件处理。
0x00:事件处理程序
现在有三种方式注册事件处理程序:
- HTML事件处理程序
- DOM0级事件处理程序
- DOM2级事件处理程序
001:HTML事件处理程序
就是说给html标签的属性设置事件处理程序;例如:
<p onclick="alert(‘hello‘)">点我</p>
p标签的属性onclick,把它的值设置成javascript代码字符串;这就是早期的js的用法
现在这个方式,用的不多了,是因为HTML与JavaScript代码耦合太紧密。就是说html代码里有javascript代码,现在都讲究行为,样式,结构分离;
还有就是这里的javascript代码字符串中的this,event对象是可以直接使用的。例如:
<p onclick="console.log(this);console.log(event)">点我</p>
打印是这样:
这是因为当指定一串javascript代码作为HTML事件处理程序属性值时,浏览器会把代码字符串为类似下面的函数中运行:
function (event) { with(document) { with(this.form || {}) { with(this) { //这里是代码字符串 } } } }
这就解释了为什么能直接使用event对象和this对象了。
但是还要注意有时我们也这样用
<body> <p onclick="aa();">点我</p> <script type="text/javascript"> function aa() { console.log(this); console.log(event); } </script> </body>
这样打印什么呢?
这时候的this就是window对象,这就是this对象有的时候让人很难确定到底指的那个作用域。这这里的函数就是window调用的;还有就是如果你想引用指向元素的作用域的this,你可以向这个函数把这个this对象传递当参数过去,就像这样:
<body> <p onclick="aa(this)">点我</p> <script type="text/javascript"> function aa(_this) { console.log(_this); console.log(event); } </script> </body>
打印:
002:DOM0级事件处理程序
就是说,首先获取对这个元素DOM对象的引用,用DOM的getElementById()等这种方式获取到对这个元素对象的引用,然后就是每个事件都在这个对象上用相应的属性,例如click事件,那么这个对象就有一个onclick属性与之对应,那你在这个对象的属性上绑定事件处理程序,例obj.onclick = function() {//相应代码};那么当你在点击的时候就会触发事件了;
这种方式大家都太熟悉了我就不举例了。
这种方式的优点就是几乎所有浏览器都兼容这种方式,一般不存在兼容性问题,我们平时写代码现在也用的很多。简单方便嘛。
但是他也是有缺点的。每个事件元素目标对于每个事件类型只能最多注册一个事件处理程序。
例如:
obj.onclick = function () { //这里是代码1 }; obj.onclick = function () { //这里是代码2 }
那么后面的会覆盖前面的,前面这个注册程序就不会执行了。
这种事件处理的程序在事件流中只能会冒泡;之前的在事件流说过这个;
要是想删除这个事件处理程序,就这样:
obj.onclick = null;
003:DOM2级事件处理程序
DOM2级事件定义了两个方法,addEventListener()方法和removeEventListener()方法来处理和删除事件处理程序。它们可以接收三个参数,第一个参数:要处理的事件名,是一个字符串,但是要记得不要加“on”作为前缀,第二个参数:作为事件处理的函数,第三个参数:一个布尔值,这个布尔值为true时,那就在事件捕获阶段调用事件处理程序,如果是false那就在冒泡阶段调用事件处理程序。ps。这两个方法支持除IE8及以下的其他版本的所有浏览器
它的主要优点是可以为同一个对象的同一个事件绑定多个事件处理程序。并且注册的多个事件是按顺序执行的;
上例子:
<p id="test">点我</p> <script type="text/javascript"> var test = document.getElementById("test"); test.addEventListener("click",function () { alert("first click"); },false); test.addEventListener("click",function () { alert("secend click"); },false);</script>
如果你想移除事件处理程序,你就需要用到removeEventListener()方法,它也接收三个参数,这三个参数要和你用addEventListener添加事件时的参数一样,必须要一模一样;并且如果addEventListener()的第二个参数,如果是匿名函数,那么用removerEventListener()是删除不了的;并且它也就只能处理用addEventListener注册的事件,那么用DOM0级注册的事件是不能用它删除的。
上离子:
test.addEventListener("click",fn,false); test.removeEventListener(‘click‘,fn,false);
这样就跟你没有注册事件似的;
但是如果你这样:
test.addEventListener("click",fn,false); test.addEventListener("click",fn,false); test.addEventListener("click",fn,false);
却只会弹出一个alert,而不是三个,这就是如果你使用相同的参数在同一对象调用多次,处理程序只是注册一次;
IE永远都是一朵奇葩,IE8及以下不支持,但是有两个跟DOM2级的方法相似的事件处理方法,attachEvent()和detachEvent()方法。需要两个参数,第一参数:事件名,注意要在事件名前加“on”,第二个参数:事件处理程序函数;它也是可以绑定多个事件处理程序的;
这两个方法支持IE5 ——IE10,IE11就不再支持了;
我就说说他和标准DOM2级的不同,相同点就不提了,
1.事件处理的作用域:
不管是DOM0级还是DOM2级他们的事件处理程序都是在它们所属元素的的作用域中运行,但是attachEvent却不是,事件处理程序会在全局作用域中运行,即事件处理程序中的this === window的,一定要记住这里面的this是指向window的
2.当调用多个事件处理程序时:
第一点:当为同一DOM对象同一个事件添加多个不同的事件处理程序函数时:在IE5-IE8,这个调用顺序也很奇葩,后注册的函数,先被调用,但是IE9,IE10这个顺序就变过来了。
第二点:当为同一个DOM对象的同一个事件添加多个相同的事件处理程序函数时:在IE5——IE8是你添加几个就执行几个,但是在IE9,10却不是这样,它只会为你注册一个事件处理程序,即你添加多个执行一个;ps。但是你如果用匿名函数,其实每个匿名函数都是不同的函数。即使你里面的内容一样。
由于IE的这种奇葩,我们想用DOM2级的方法,但是又要照顾IE8及以下,这就产生了一个跨浏览器的事件处理程序
//通过能力检测来确定浏览器支持 var EventUtil = { //param ele 元素对象 //param type 事件类型 //param handler 事件处理程序函数 addHandler: function (ele,type,handler) { if(ele.addEventListener) { ele.addEventListener(type,handler,false); } else if (ele.attachEvent) { ele.attachEvent("on" + type,handler); } else { ele["on" + type] = handler; } }, removeHandler: function (ele,type,handler) { if (ele.removeEventListener) { ele.removeEventListener(type,handler,false); } else if(ele.attachEvent) { ele.detachEvent("on" + type,handler); } else { ele["on" + type] = null; } } }
跨浏览器事件处理程序
这个程序没有考虑IE的attachEvent()函数的作用域问题,也就是说它里面this不能用。但你可以直接引用这个对象,或者用event对象的srcElement获取到这个对象;
0x01 事件对象
在触发DOM上的事件时就会自动产生一个事件对象,这个对象包含这个事件的相关信息。
001.获取event
就是直接在事件处理程序函数传递第一个参数就是event对象,这个是浏览器自动传递的。但是我今天亲测了一下发现:
DOM0级 |
DOM2级(addEventListener方法) |
attachEvent (仅IE5—IE10支持) |
|
chrome |
支持传参; 也支持window.event |
支持传参; 也支持window.event |
|
IE |
支持传参; 也支持window.event (但是IE8及以下不支持传参方式) |
支持传参; 也支持window.event (IE9-IE11支持下) |
支持传参; 也支持window.event |
firefox |
仅支持传参; |
仅支持传参; |
|
opera |
支持传参; 也支持window.event |
支持传参; 也支持window.event |
我用的浏览器都是最新的;
但是一般为了兼容性一般在用到的是时候这样写:
event = event || window.event;
002.取消默认操作行为
比如a标签只要你点击就跳转到href属性值的url,要是你不想让它跳转,你就要阻止默认行为;
DOM0级事件处理程序中的取消默认事件:方式1:在事件处理程序的函数中返回值设置为false,这个所有浏览器都支持;
方式2:event对象的方法,这样用:event.preventDefault()方法,但是这个只是一部分浏览器支持:chrome,opera,IE11;那就是说其他的都不支持了;还有IE自家的returnValue属性,event.returnValue = false;也可取消默认行为,仅IE5-IE10支持;
总结:如果你想用,那你就用返回值这种方式,兼容性最好了。
DOM2级中:那就直接用event.preventDefault();但是IE的attachEvent中要取消默认行为,要用event.returnValue = false;
003.阻止事件传播(冒泡或捕获)
DOM0级和DOM2级都可以用event对象的stopPropagation(),但是IE8及以下不支持;
IE8及以下可以用事件对象的cancelBubble属性,设置为true就取消冒泡;
004.在介绍一个event对象的一个type属性的用法:它指的是事件目标
在例子中说明:
function handler (evt) { switch(evt.type) { case "click": //相应代码 console.log("clicked"); break; case "mouseover": // 相应代码 console.log("mouseover"); break; case "mouseout": //相应代码 console.log("mouseout"); break; } } test.onclick = handler; test.onmouseover = handler; test.onmouseout = handler;
多了不解释;
005.event中的属性
不同的事件它的event对象所包含的属性也会有所不同。
下列只是列出了每个事件都会有的
属性/方法 | 类型 | 读/写 | 说明 |
bubbles | Boolean | read | 表明事件是否冒泡 |
cancelable | Boolean | read | 表明是否可以取消事件的默认行为 |
currentTarget | Element | read | 其事件处理程序当前正在处理事件的那个元素 |
defaultPrevented | Boolean | read | 为true表示已经调用了preventDafult() |
detail | Integer | read | 与事件相关的细节信息 |
eventPhase | Integer | read |
表明调用事件处理程序的阶段:1表示捕获阶段 2表示处于目标,3表示冒泡阶段 |
preventDefault() | Function | read | 取消事件的默认行为。如果cancelable是true,则可以使用这个方法 |
stopImmediatePropagation() | Function | read | 取消事件的进一步冒泡或捕获,同时阻止任何事件处理程序被调用 |
stopPropagation() | Function | read | 取消事件的进一步捕获或冒泡 |
target | Element | read | 事件目标 |
trusted | Boolean | read | 为true表示事件是浏览器生成的,false表示是由开发人员生成的 |
type | String | read | 被触发的事件类型 |
view | AbstractView | read | 与事件相关的抽象视图 |
其实还有很多,比如鼠标事件还要有坐标位置信息,如clientX,clientY,screenX,screenY等,但是在键盘事件中就没有坐标信息
IE中所共有的
属性/方法 | 类型 | 读写 | 说明 |
cancelBubble | Boolean | read,write | 默认为false,但是将其设置为true就取消事件的冒泡,(与DOM中的stopPropagation对应 |
returnValue | Boolean | read,write | 默认为true,设置为false取消事件的默认行为(与DOM的preventDefault) |
srcElement | Element | read | 事件目标(与DOM中的target对应) |
type | String | read | 被触发的事件类型 |
006.跨浏览器事件对象
var EventUtil = { getEvent:function (event) { return event ? event : window.event; }, getTarget: function (event) { return event.target || event.srcElement; }, preventDefault: function (event) { if (event.preventDefault) { event.preventDefault(); } else { event.returnValue = false; } }, stopPropagation: function (event) { if(event.stopPropagation) { event.stopPropagation(); } else { event.cancelBubble = true; } } };
如有不准或错误,欢迎指正!
参考:javascript高级程序设计;
javasript权威指南;