事件绑定分为两种:
一种是传统事件绑定(内联模型/脚本模型);上一章内容;
一种是现代事件绑定(DOM2级模型);现代事件绑定在传统事件绑定基础上提供了更强大的功能;
一 传统事件绑定的问题
// 脚本模型将一个函数赋值给一个事件处理函数;var box = document.getElementById("box");// 获取元素;box.onclick = function(){// 元素点击触发事件;alert("Lee");}// 问题一:一个事件处理函数触发两次事件;window.onload = function(){ // 第一组程序;alert("Lee");}window.onload = function(){ // 第二组程序;alert("Mr.Lee");}// PS:当两组程序同时执行的时候,后面一个会把前面一个完全覆盖;// 导致前面的window.onload完全失效了;// 解决方案:window.onload = function(){ // 第一组事件处理程序,会被覆盖;alert("Lee");}if(typeof window.onload == "function"){ // 判断之前是否有window.onload;var saved = null;// 创建一个保存器;saved = window.onload;// 把之前的window.onload保存起来;}window.onload = function(){ // 下一个要执行的事件;// saved()=window.onload = functionif(saved)saved();// 判断之前是否有事件,如果有则先执行之前保存的事件;alert("Mr.Lee"); // 执行本事件的代码;}// 问题二:事件切换器box.onclick = boBlue; // 第一次执行toBlue();function toRed(){this.className = "red";this.onclick = toBlue;// 第三次执行roBlue(),然后来回切换;}function toBlue(){this.className = "blue";this.onclick = toRed;// 第二次执行toRed();}// 这个切换器在扩展的时候,会出现一些问题:1.如果增加一个执行函数,那么会被覆盖;box.onclick = toAlert;// 被增加的函数;box.onclick = toBlue;// toAlert被覆盖了;2.如果解决覆盖问题,就必须包含同时执行;box.onclick = function(){// 包含进去,但可读性降低;toAlert();// 第一次不会被覆盖,但第二次又被覆盖;toBlue.call(this);// 还必须把this传递到切换器里;}// 综上三个问题:覆盖问题/可读性问题/this传递为题;// 我们创建一个自定义事件处理函数;function addEvent(obj,type,fn){ // 取代传统事件处理函数;var saved = null;// 保存每次触发的事件处理函数;if(typeof obj["on"+type] == "function"){// 判断是不是存在事件;saved = obj["on"+type]; // 如果有,保存起来;}obj["on"+type] = function(){// 然后执行;if(saved)saved();// 执行上一个;fn.call(this); // 执行函数,把this传递进去;}}addEvent(window,"load",function(){alert("Lee");// 可以执行;});addEvent(window."load",function(){alert("Mr.Lee");// 可以执行;})// 用自定义事件函数注册到切换器上查看效果:addEvent(window,"load",function(){var box = document.getElementById("box");addEvent(box,"click",toBlue);});function toRed(){this.className = "red";addEvent(this,"click",toBlue);}function toBlue(){this.className = "blue";addEvent(this,"click",toRed);二 W3C事件处理函数
// "DOM2级事件"定义了两个方法,用于添加事件和删除事件的处理程序:addEventListener()和removeEventListener();
// 所有DOM节点中都包含这两个方法,并且它们都接收3个参数:事件名/函数/冒泡或捕获的布尔值(true表示捕获,false表示冒泡);window.addEventListener("load",function(){alert("Lee");},false);window.addEventListener("load",function(){alert("Mr.Lee");},false);// PS:W3C的事件绑定好处:1.不需要自定义了;2.可以屏蔽相同的函数;3.可以设置冒泡和捕获;window.addEventListener("load",init,false);// 第一次执行了;window.addEventListener("load",init,false);// 第二次被屏蔽了;function init(){alert("Lee");}// 事件切换器window.addEventListener("load",function(){var box = document.getElementById("box");box.addEventListener("click",function(){// 不会被覆盖/误删;alert("Lee");},false);box.addEventListener("click",toBlue,false);// 引入切换;},false);function toRed(){this.className = "red";this.removeEventListener("click",toRed,false); // 移除事件处理函数;this.addEventListener("click",toBlue,false);// 添加需要切换的事件处理函数; }function toBlue(){this.className = "blue";this.removeEventListener("click",toBlue,false);this.addEventListener("click",toRed,false);}// 设置冒泡和捕获阶段document.addEventListener("click",function(){alert("document");},true);// 设置为捕获;document.addEventListener("click",function(){alert("Lee");},false);// 设置为冒泡;三 IE事件处理函数
// IE中实现了与DOM中类似的两个方法:attachEvent()和detachEvent();
// 这两个方法接收相同的参数:事件名和函数;
// 在使用这两组函数的时候,区别:// 1.IE不支持捕获,只支持冒泡;// 2.IE添加事件不能屏蔽重复的函数;// 3.IE中的this指向的是window而不是DOM对象;// 4.在传统事件上,IE是无法接受到event对象的;但使用了attachEvent()却可以;window.attachEvent("onload",function(){var box = document.getElementById("box");box.attachEvent("onclick",toBlue);});function toRed(){var that = window.event.srcElement;that.className = "red";that.detachEvent("onclick",toRed);that.attachEvent("onclick",toBlue);}function toBlue(){var that = window.event.srcElement;that.className = "blue";that.detachEvent("onclick",toBlue);that.attachEvent("onclick",toRed);}// PS:IE不支持捕获;// IE不能屏蔽;// IE不能传递this,可以call过去;// 在传统绑定上,IE是无法像W3C那样通过传参接受event对象;但如果使用了attachEvent()却可以;box.onclick = function(evt){alert(evt);// undefined;}box.attachEvent("onclick",function(evt){alert(evt);// object;alert(evt.type);// click;});// 兼容IE和W3C的事件切换器函数;function addEvent(obj,type,fn){// 添加事件处理程序兼容;if(obj.addEventListener){obj.addEventListener(type,fn);}else if(obj.attachEvent){obj.attachEvent("on"+type,fn);}}function removeEvent(obj,type,fn){// 移除事件处理程序兼容;if(obj.removeEventListener){obj.removeEventListener(type,fn);}esle if(obj.detachEvent){obj.detachEvent("on"+type,fn);}}function getTarget(evt){ // 得到事件目标;if(evt.target){return evt.target;}else if(window.event.srcEleemnt){return window.event.srcElement;}}四 事件对象补充
1.relatedTarget// 这个属性可以在mouseover和mouseout事件中获取从哪里移入和从哪里移出的DOM对象;box.onmouseover = function(evt){// 鼠标移入box;alert(evt.relatedTarget);// 获取移入box之前的那个元素;}box.onmouseout = function(evt){ // 鼠标移出box;alert(evt.relatedTarget);// 获取移出box之后到的那个元素;}// IE提供了两组与之对应的属性:fromElement和toElement;// 兼容函数function getEarget(evt){var e = evt || window.event;// 得到事件对象;if(e.srcElement){// 如果支持srcElement,表示IE;if(e.type == "mouseover"){ // 如果是over事件;return e.fromeElement; // 就使用from;}else if(e.type == "mouseout"){ // 如果是out;return e.toElement; // 就使用to;}}else if(e.relatedTarget){ // 如果支持relatedTarget,表示W3C;return e.relatedTarget;}}2.阻止事件的默认行为// 一个超链接的默认行为就点击然后跳转到指定的页面;// 那么阻止默认行为就可以屏蔽跳转的这种操作,而实现自定义操作;// 取消事件默认行为还有一种不规范的做法,就是返回false;link.onclick = function(){alert("Lee");return false;// 直接返回false,就不会跳转了;}// PS:虽然return false;可以实现这个功能,但有漏洞;// 第一:代码必须写到最后,这样导致中间的代码执行后,有可能执行不到return false;// 第二:return false写到最前那么之后的自定义操作就失败了;// 解决方案:在最前面就阻止默认行为,并且后面还能执行代码;function preDef(evt){// 跨浏览器兼容阻止默认行为;var e = evt || window.event;if(e.preventDefault){e.preventDefault(); // W3C,阻止默认行为;}else{e.returnValue = false;// IE,阻止默认行为;}}3.上下文菜单事件contextmenu// 当我们右击网页的时候,会自动出现windows自带的菜单;// 那么我们可以使用contextmenu事件来修改我们指定的菜单;但前提是把右击的默认行为取消;addEvent(window,"load",function(){var text = docuemnt.getElementById("text");addEvent(text,"contextmenu",function(evt){// 添加右键菜单事件处理程序;var e = evt || window.event;preDef(e);// 阻止默认行为函数;var menu = document.getElementById("menu");// 找到自定义的menu对象;menu.style.left = e.clientX+"px"; // 确定自定义menu在屏幕上的位置;menu.style.top = e.clientX+"px";menu.style.visibility = "visible";// 设置自定义menu的属性为可见;addEvent(document,"click",function(){ // 给document添加单击事件处理程序;docuemnt.getElementById("myMenu").style.visibility = "hidden";//将自定义的menu隐藏;});});});4.卸载前事件beforeunload// 这个事件可以帮助在离开本页的时候给出相应的提示;"离开"或"返回"操作;addEvent(window."beforeunload",function(evt){var evt = event || window.event;var message = "是否离开此页?";evt.returnValue = message;return message;});5.鼠标滚轮(mousewheel)和DOMMouseScroll// 用于获取鼠标上下滚轮的距离;addEvent(docuemnt,"mousewheel",function(evt){// 非Firefox;alert(getWD(evt));});addEvent(docuemnt,"DOMMouseScroll",function(evt){// Firefox;alert(getWD(evt));}); function getWD(evt){var e = evt || window.event;if(e.wheelDelta){// mousewheel事件的滚动值保存在wheelDelta里;return e.wheelDelta;}else if(e.detail){ // DOMMouseScroll事件的滚动值保存在detail里;return -evt.detail*30;// 保持计算的统一;}}