DOM高级程序设计笔记/事件处理程序Function.prototype添加getCallBack事件冒泡捕获

来源:互联网 发布:2017凤凰网软件打不开 编辑:程序博客网 时间:2024/06/05 20:39
注:大多数情况下添加到冒泡阶段,可以最大限度兼容浏览器,不建议在捕获阶段注册处理程序IE事件处理程序实现与DOM类型的方法:attachEvent,detachEvent,接收相同的两个参数:程序名称,程序函数var btn = document.getElementById(“myBtn”);btn.attachEvent(“onclick”,function(){ alert(this.id);});attachEvent是在全局作用域运行,因此this指向windowattachEvent也可以添加多个处理程序,不过,触发按添加相反顺序触发attachEvent添加的只能detachEvent移除,一样不能使用匿名函数跨浏览器的事件处理程序//跨浏览器事件处理程序var EventUtil = { addHandler:function(element,type,handler){ if(element.addEventListener){ element.addEventListener(type,handler,false); //DOM2级 }else if(element.attachEvent){ element.attachEvent(“on” + type,handler); //兼容IE8及更早版本,加上“on”,IE方法 }else{ element[“on” + type] = handler; //DOM0 } }, removeHandler:function(element,type,handler){ if(element.removeEventListener){ element.removeEventListener(type,handler,false);//DOM2级 }else if(element.detachEvent){ element.detachEvent(“on” + type,handler) //兼容IE8及更早版本,加上“on”,IE方法 }else{ element[“on” + type] = null; } }};使用示例://使用示例var btn = document.getElementById(“myBtn”);var handler = function(){ alert(“hello”);};EventUtil.addHandler(btn,”click”,handler);//其他代码EventUtil.removeHandler(btn,”click”,handler);事件对象兼容DOM的浏览器会传入一个event对象到事件处理程序中event对象包含与创建它的特定事件有关的属性和方法,触发类型不一样,可用属性方法不一样,但都有下表成员 Simulating IE Mouse Events Example

This example works only in Internet Explorer.

(function(){ var btn = document.getElementById("myBtn"); var btn2 = document.getElementById("myBtn2"); EventUtil.addHandler(btn, "click", function(event){ alert("Clicked!"); alert(event.screenX); //100 }); EventUtil.addHandler(btn2, "click", function(event){ //create event object var event = document.createEventObject(); //initialize the event object event.view = window; event.detail = 0; event.screenX = 100; event.screenY = 0; event.clientX = 0; event.clientY = 0; event.ctrlKey = false; event.altKey = false; event.metaKey = false; event.shiftKey = false; event.button = 0; event.relatedTarget = null; //fire the event btn.fireEvent("onclick", event); }); })();事件流描述的是从页面中接收事件的顺序,IE和Netscape提出来差不多完全相反的事件流的概念,IE事件流是事件冒泡流,Netscape事件流是事件捕获流。 事件冒泡  IE的事件流叫做事件冒泡,即事件开始时由最具体的元素(文档中嵌套最深的那个节点)接收,然后逐级向上(一直到文档);如下代码:
事件测试

  JS如下:
document.getElementById(“aTag”).addEventListener(‘click’,aTag);
document.getElementById(“span”).addEventListener(‘click’,span);
document.getElementById(“div”).addEventListener(‘click’,div);
function aTag(e) {
alert(“点击的是a标签”);
}
function span(e) {
alert(“点击的是span标签”);
}
function div(e) {
alert(“点击的是div标签”);
}
  当单击 “事件测试”文字后,那么click事件会按照如下顺序传播;
  1)先打印出:点击的是a标签
  2) 再打印出:点击的是span标签
  3) 最后打印出:点击的是div标签
  4) 最后肯定是document文档。
  所有现代浏览器都支持事件冒泡。
 事件捕获:
  事件捕获与事件冒泡事件流正好相反的顺序,事件捕获的事件流是最外层逐级向内传播,也就是先document,然后逐级div标签 , span标签 , a标签;
  上面的JS代码改成如下:
document.getElementById(“div”).addEventListener(‘click’,div,true);
document.getElementById(“aTag”).addEventListener(‘click’,aTag,true);
document.getElementById(“span”).addEventListener(‘click’,span,true);
  第三个参数设置为true,即为捕获事件,默认为false;否则的话,事件流还是和上面的一样,因为不管是在IE还是标准浏览器下,事件冒泡浏览器都支持的。
 DOM事件流
  DOM2级事件规定的事件流包括三个阶段,分别是:事件捕获阶段,处于目标阶段和事件冒泡阶段。示意图就不画了,具体的可以看看书。
 DOM0级事件处理程序
  如下代码是DOM0级事件处理程序:
var btn = document.getElementById(“btn”);
btn.onclick = function(){
alert(“Clicked”);
};
  使用DOM0级方法指定的事件处理程序被认为是元素的方法,处理程序是在元素的作用域进行的,程序中this是引用的是当前元素。

btn
var btn = document.getElementById(“btn”);btn.onclick = function(){ alert(this.id); // 弹出btn}  单击元素btn后,通过this.id取得元素的属性id,还可以通过this访问元素的任何属性和方法,以这种方式添加的事情处理程序在事件流的冒泡阶段处理。  也可以删除通过DOM0级方法指定的事件处理程序,只要将事件处理程序的属性值设置为null即可。  btn.onclick = null; // 删除事件处理程序;  如下JS代码改成如下:var btn = document.getElementById(“btn”);btn.onclick = function(){ alert(this.id);}btn.onclick = null;  再单击btn后,没有任何反应; DOM2级事件处理程序  DOM2级事件定义了2个方法,用于处理指定和删除事件处理程序的操作;  addEventListener()和removeEventListener()。所有DOM节点都包含这两个方法,他们包含三个参数,第一个参数为事件类型;第二个参数为事件函数,第三个参数为布尔值,如果是true的话,说明是事件流是捕获事件,如果是false的话,那么事件流是冒泡事件;  比如如上的btn代码,我们改成如下:var btn = document.getElementById(“btn”);btn.addEventListener(‘click’,function(e){ alert(this.id);},false);  上面的点击事件是在冒泡阶段被触发,与DOM0级方法一样,这里添加的事件处理程序也是在其依副的元素作用域中运行,使用DOM2级添加事件处理程序的好处是可以添加多个事件处理程序,如下代码:var btn = document.getElementById(“btn”);btn.addEventListener(‘click’,function(e){ alert(this.id);},false);btn.addEventListener(‘click’,function(e){ alert(“我是来测试的”);},false);  上面的代码被弹出2次对话框,而在DOM0级是不可以的;它永远是执行最后一次的。  addEventListener添加的事件只能使用removeEventListener来删除相对应的事件,那么如上的JS不能按照上面的方式来编写哦!需要给定义一个函数;如下:btn.addEventListener(‘click’,handler,false);function handler(e){ alert(this.id);}  可以使用如下方式对click事件删除;如下代码:  btn.removeEventListener(‘click’,handler);  上面的是在标准浏览器下处理的事件,下面我们来看看在IE下处理的事件; IE事件处理的程序  IE实现了与DOM类似的2个方法,分别是attachEvent()和detachEvent(),这两个方法只接受2个参数,第一个参数是事件名称,第二个参数是要处理的函数;由于IE8及更早版本只支持事件冒泡,所以通过attachEvent()添加的事件处理程序会被添加到冒泡阶段;  下面是IE事件处理程序的代码如下:btn.attachEvent(‘onclick’,handler);function handler(e){ alert(this); // window}  注意:attachEvent的事件名称是onclick,而addEventListener的事件名称是click,且IE中使用的attachEvent()与使用DOM0级方法的的主要区别在于事件处理程序的作用域,在使用dom0级情况下,事件处理程序在其所属元素的作用域内运行,在使用attachEvent()方法的情况下,事件处理程序在全局作用域下运行,其中的this等于window。  与addEventListener一样,attachEvent也可以注册多个点击click事件,如下代码:btn.attachEvent(‘onclick’,function(e){ alert(“1”);});btn.attachEvent(‘onclick’,function(e){ alert(“2”);});  但是与Dom方法不同的是,这些事件处理程序不是以添加他们的顺序执行,而是以相反的顺序触发,比如如上代码,会先弹出2,然后弹出1对话框;  使用attachEvent注册的事件只能使用detachEvent()方法来移除;  下面我们可以来编写跨浏览器的事件处理程序;代码如下: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; } }};  下面我们可以使用这个封装的函数代码来测试之前的代码了,代码改成如下所示:function handler(){ alert(1);}EventUtil.addHandler(btn,’click’,handler);  在IE或者标准浏览器下都会弹出1;如果我们需要移除click事件的话,我们可以使用如下代码:  EventUtil.removeHandler(btn,’click’,handler);  然后在标准浏览器下或者IE下点击btn元素都没有反应; 事件对象:  在触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息;包括导致事件的元素,事件的类型以及其他与特定事件相关的信息。我们来看看dom0级和dom2级的事件对象Event;  比如如下代码:var btn = document.getElementById(“btn”);btn.onclick = function(e){ console.log(e);}  打印如下:  下面我们来看看最基本的成员的含义吧;如下:属性/方法 类型 含义bubbles Boolean 事件是否冒泡cancelable Boolean 是否可以取消事件的默认行为currentTarget Boolean 事件处理程序当前正在处理事件的那个元素defaultPrevented Boolean 为true 表示已经调用了preventDefault()detail Integer 与事件相关的细节信息eventPhase Integer 调用事件处理程序的阶段:1表示捕获阶段,2表示“处于目标”,3表示冒泡阶段preventDefault() Function 取消事件的默认行为。如果cancelable是true,则可以使用这个方法stopImmediatePropagation() Function 取消事件的进一步捕获或冒泡,同时阻止任何事件处理程序被调用stopPropagation() Function 取消事件的进一步捕获或冒泡。如果bubbles为true,则可以使用这个方法target Element 事件的目标type String 被触发的事件的类型view AbstractView 与事件关联的抽象视图。等同于发生事件的window对象 理解currentTarget与target  在事件处理程序内部,this始终等于currentTarget值,即currentTarget是指当前被触发或者说正在处理事件的那个元素,而target是指当前的目标元素;比如如下代码,对btn按钮触发点击事件,那么e.currentTraget指向了this,e.target也指向了this;如下代码:var btn = document.getElementById(“btn”);btn.onclick = function(e){ console.log(e.currentTarget == this); // true console.log(e.target == this); // true}  但是如果我对document.body触发点击的话,那么e.currentTarget就指向了document.body了,那么e.target 指向与 btn那个元素了,如下代码:document.body.onclick = function(e){ console.log(e.currentTarget === document.body); // true console.log(document.body === this); // true console.log(e.target === document.getElementById(“btn”)); //true};  现在应该能理解currentTarget与target的区别吧!currentTarget就是指被点击的那个元素,但是target是当前点击的目标元素,如上代码,由于btn上并没有注册事件,结果click事件就冒泡到了document.body,在那里事件才得到了处理。 理解标准浏览器下的事件对象与IE下的事件对象  标准浏览器下的事件对象是event,比如btn点击后;如下代码:var btn = document.getElementById(“btn”);btn.onclick = function(){ console.log(event); //标准浏览器下打印事件对象 console.log(event.type);//’click’}btn.onclick = function(){ // IE下打印的事件对象window.event console.log(window.event); console.log(window.event.type); // ‘click’ }  上面的写法是在DOM0级上注册事件,如果我们在Dom2级上注册事件的话,那么就会有一个事件对象event作为参数传入事件到函数中,如下:var btn = document.getElementById(“btn”);EventUtil.addHandler(btn,’click’,function(e){ console.log(e);}); 理解特定事件的默认行为事件  在标准浏览器下,在阻止特定事件的默认行为,可以使用preventDefault()方法,比如如下,我点击一个连接,按道理是打开一个新连接窗口,但是我使用preventDefault()方法可以阻止默认行为,阻止打开新窗口;如下代码:HTML:打开新连接JS如下:var alink = document.getElementById(“alink”);alink.onclick = function(e){ console.log(e) e.preventDefault();}  就可以阻止页面进行跳转了~ 这是标准浏览器下处理方式,下面我们来看看IE是如何处理默认事件的;  IE下使用returnValue属性来取消给定事件的默认行为,只要将returnValue属性值设置为false即可,就可以阻止浏览器的默认行为,如下代码:alink.onclick = function(){ console.log(window.event) window.event.returnValue = false;} 标准浏览器下与IE下的事件目标的区别  标准浏览器下使用e.target来指定当前被点击的目标元素,如下代码所示:var btn = document.getElementById(“btn”);btn.onclick = function(){ console.log(event); console.log(event.target); // 打印事件目标元素}  IE下是使用event.srcElement来指定当前的目标元素,如下代码:btn.onclick = function(){ console.log(event); console.log(window.event.srcElement);} 理解标准浏览器与IE下阻止事件传播的区别  在标准浏览器下我们可以使用stopPropagation()方法来停止事件在DOM层次中的传播,即取消事件中的冒泡或者捕获。从而避免触发注册在document.body上面的事件处理程序,如下所示:var btn = document.getElementById(“btn”);btn.onclick = function(e){ alert(1); e.stopPropagation();}document.body.onclick = function(){ alert(2);}  如上代码,如果我不使用stopPropagation()阻止冒泡事件的话,那么在页面中会先弹出1,然后弹出2,如果使用stopPropagation()方法的话,只会在页面上弹出1,就不会冒泡到body上面去;  IE下停止冒泡的话,我们可以使用cancelBubble属性,我们只要将此属性设置为true,即可阻止事件通过冒泡触发document.body中的注册事件。但是IE是不支持捕获事件的,但是stopPropagation()即支持捕获事件又支持冒泡事件的。如下代码:btn.onclick = function(e){ alert(1); window.event.cancelBubble = true;}document.body.onclick = function(){ alert(2);}  如果不设置window.event.cancelBubble 为true的话,就会先弹出1,然后弹出2,如果加上的话,就只会弹出1对话框。  理解了上面的区别后,我们现在可以往EventUtil对象里面添加跨浏览器的方法了; 跨浏览器的事件对象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; } }, 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; } }}; 事件类型:  DOM3级事件规定了以下几类事件;如下:  UI事件: 当用户与页面上的元素交互时触发;  load事件:当页面加载完后(包括所有图像,所有javascript文件,css文件等外部资源),就会触发window上面的load事件,如下代码是加载图片的:  HTML代码:  JS代码如下:var img = document.getElementById(“img”);EventUtil.addHandler(img,’load’,function(event){ var event = EventUtil.getEvent(event); alert(EventUtil.getTarget(event).src);});  当图片加载完后,就会弹出图片的url地址了;  如果在创建新的img元素时,可以为其指定一个事件处理程序,以便图像加载完成后给出提示,此时,最重要的是在指定src属性之前先指定事件;如下代码所示: EventUtil.addHandler(window,’load’,function(){ var img = document.createElement(“img”); EventUtil.addHandler(img,’load’,function(e){ e = EventUtil.getEvent(e); alert(EventUtil.getTarget(e).src); }); document.body.appendChild(img); img.src = “event.png”;});  在图像加载完成后,会弹出图片地址了;  同样的功能,我们可以使用DOM0级的Image对象来实现,在DOM出现之前,开发人员经常使用Image对象在客户端预加载图像,如下代码:EventUtil.addHandler(window,’load’,function(){ var img = new Image(); EventUtil.addHandler(img,’load’,function(e){ alert(1); }); img.src =”event.png”;});  Script元素也支持load事件,但是IE8及以下不支持,在IE9+,Firefox,Opera,chrome及Safari3+都支持,以便开发开发人员确定动态加载的javascript文件是否加载完毕;比如我们动态创建script标签后,通过load事件判断动态创建的script标签是否加载完毕,代码如下:EventUtil.addHandler(window,’load’,function(){ var script = document.createElement(“script”); EventUtil.addHandler(script,’load’,function(e){ alert(1); }); script.src = “a.js”; document.body.appendChild(script);});  焦点事件:当元素获得或失去焦点时触发;  有:blur:在元素失去焦点时触发,这个事件不会冒泡,所有浏览器都支持。  foucs:在元素获得焦点时触发,这个事件不会冒泡,所有浏览器都支持。  鼠标事件:当用户通过鼠标在页面操作时触发;1. click事件:在用户单击鼠标按钮或者按下回车键触发;2. dblclick事件:在用户双击鼠标按钮时被触发;3. mousedown事件:在用户按下了任意鼠标按钮时被触发,不能通过键盘触发这个事件。4. mouseenter事件:在鼠标光标从元素外部移动到元素范围之内被触发;这个事件不冒泡;5. mousemove事件:当鼠标指针在元素内部移动时重复地触发。6. mouseout事件:用户将其移入另一个元素内被触发。7. mouseover事件:鼠标指针在元素外部,用户将移入另一个元素的边界时触发,感觉和mouseenter事件类似;8. mouseup事件:用户释放鼠标按钮时触发;  页面上所有的元素都支持鼠标事件,除了mouseenter和mouseleave,所有鼠标事件都会冒泡,也可以被取消,而取消鼠标事件将会影响浏览器的默认行为。 理解客户区坐标位置  含义是:鼠标指针在可视区中的水平clientX和垂直clientY坐标;  如下图所示:  代码如下:EventUtil.addHandler(btn,’click’,function(e){ e = EventUtil.getEvent(e); console.log(“可视区X轴坐标为:”+e.clientX + ” “+ “可视区Y轴坐标为:”+e.clientY);});  注意:客户区坐标位置不包含滚动条滚动的位置,因此这个位置不代表鼠标在页面上的位置; 理解页面坐标位置pageX和pageY:  pageX与pageY是指页面坐标的位置,与clientX和clientY的区别是:它包含页面滚动条的位置,如下图所示:  代码如下:EventUtil.addHandler(btn,’click’,function(e){ e = EventUtil.getEvent(e); console.log(“页面X轴坐标为:”+e.pageX + ” “+ “页面Y轴坐标为:”+e.pageY);});  在页面没有滚动条的情况下,pageX与clientX相等,同理pageY与clientY相等。  但是IE8及更早的版本不支持pageX与pageY,不过我们可以使用客户区坐标(client,clientY)和滚动坐标计算出来;因此我们需要用到document.body(混杂模式下)或 document.documentElement(标准模式下)中的scrollLeft和scrollTop属性;  对此我们可以封装代码如下:EventUtil.addHandler(btn,’click’,function(e){ e = EventUtil.getEvent(e); var pageX = e.pageX, pageY = e.pageY; if(!pageX) { pageX = e.clientX + (document.body.scrollLeft || document.documentElement.scrollLeft); } if(!pageY) { pageY = e.clientY + (document.body.scrollTop || document.documentElement.scrollTop); } console.log(“页面X轴坐标为:”+pageX + ” “+ “页面Y轴坐标为:”+pageY);}); 理解屏幕坐标的位置  屏幕横坐标screenX和垂直坐标screenY属性是相对于整个屏幕的。如下图所示:  如下代码测试:EventUtil.addHandler(btn,’click’,function(e){ e = EventUtil.getEvent(e); console.log(“屏幕X轴的坐标为:”+e.screenX + ” “+”屏幕Y轴的坐标为:”+e.screenY);}); 理解鼠标滚轮事件:  IE6首先实现了mousewheel事件,此后opera,chrome和safari也都实现了这个事件,当用户通过鼠标滚轮与页面交互,在垂直方向上滚动页面时(无论向上还是向下),就会触发mousewheel事件,这个事件可以在任何元素上触发,最终会冒泡到document(IE8)或window(IE9,Opera,Chrome,Safari)对象,与mousewheel事件对应的event对象外,还有一个属性wheelDelta属性,当用户向前滚动鼠标滚轮时,wheelDelta是120的倍数,当用户向后滚动鼠标滚轮时,wheelDelta是-120的倍数。  将mousewheel事件给页面任何元素或document对象,即可处理鼠标滚轮操作;如下代码:EventUtil.addHandler(btn,’mousewheel’,function(e){ e = EventUtil.getEvent(e); alert(e.wheelDelta);});  如上代码,我不是在document对象或者window对象上,而是在页面btn元素上触发的;但是我们要注意,在Opera9.5之前的版本中,wheelDelta值的正负号是颠倒的,如果我们要支持Opera9.5版本之前的话,那么我们需要浏览器检测技术来检测下;如下代码EventUtil.addHandler(document, “mousewheel”, function(event){ event = EventUtil.getEvent(event); // var delta = (client.engine.opera && client.engine.opera
阅读全文
0 0