`

浏览器事件机制与自定义事件的实现

阅读更多

一、 0 DOM 上的事件和 2 DOM 事件机制

0 DOM 上的事件又称原始事件模型,所有的浏览器都支持他,而且是通用的。 2 DOM 事件机制又为标准事件模型,除了 ie 其他浏览器都支持( ie9 也支持), ie 虽然大部分与标准事件模型一样,但有自己专有的事件模型,因此开发人员要实现标准事件模型必须为 IE 写特定的代码,这给程序员增加了负担。

原始事件模型

 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>浏览器0级DOM上的事件</title>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  </head>
  
  <body>
    <input type="button" value = "点击我" id = "btn">
  </body>
</html>

<script type="text/javascript">
<!--
var method1 = function(){alert(1)};
var method2 = function(){alert(2)};
var method3 = function(){alert(3)};
document.getElementById("btn").onclick = method1; 
document.getElementById("btn").onclick = method2; 
document.getElementById("btn").onclick = method3;
//-->
</script>

 

      以上书写在各浏览器中都是兼容的,但只有 medhot3 被执行,即同一个对象同一类型的事件只能注册一个处理函数,要想实现注册多个处理函数,需要利用 2 DOM 事件机制。

 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>浏览器2级DOM事件机制</title>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  </head>
  
  <body>
    <input type="button" value = "点击我" id = "btn">
  </body>
</html>

<script type="text/javascript">
<!--
var method1 = function(){alert(1)};
var method2 = function(){alert(2)};
var method3 = function(){alert(3)};

//执行顺序为method1->method2->method3 
//标准事件模型
var btn1Obj = document.getElementById("btn"); 
btn1Obj.addEventListener("click",method1,false); 
btn1Obj.addEventListener("click",method2,false); 
btn1Obj.addEventListener("click",method3,false);

//执行顺序为method3->method2->method1
//IE事件模型
var btn1Obj = document.getElementById("btn");
btn1Obj.attachEvent("onclick",method1); 
btn1Obj.attachEvent("onclick",method2); 
btn1Obj.attachEvent("onclick",method3);

//-->
</script>

 

     从运行结果来看, ie firefox 下执行的顺序是不一样的

 

二、2级DOM事件模型事件的注册与删除

element.addEventListener(eventType,fn,useCapture); // 注册事件

element.removeEventListener(eventType,fn, useCapture);// 删除事件

 

      可以用 addEventListener() 给同一个对象同一类型的事件注册多个处理函数,但是如果在同一元素上多次注册了一个处理函数,那么第一次注册后的所有注册都将被忽略,但删除该注册函数(调用 removeEventListener() )后可以再重新注册该函数。需要注意的是删除事件, useCapture 的值必须要跟注册时保持一致

 

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="zh-CN">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"    />
<title>浏览器事件机制——注册和删除事件</title>
<style>
     div {border:1px  solid blue;}
     div#div1 {padding:40px;background-color:#aaaaaa;}
     div#div2 {padding:40px;background-color:#bbbbbb;}
     div#div3 {padding:40px;background-color:#cccccc;}
</style>
</head>
 <body>
<div id="div1" style="width:100px;height:100px;" >
      我是老大, 点击我添加老三的click事件
</div>
<br/>
<div id="div2" style="width:100px;height:110px;" >
    我是老二, 点击我删除老三的click事件        
</div>
<br/>
<div id="div3" style="width:100px;height:100px;" >
    我是老三,是否有click事件,老大老二说了算,呵呵
</div>
<script>
    function click1() {
        alert("I am div1,add div3 event");
        if(window.addEventListener){
	        div3.addEventListener("click", click3, false);
	    }else if (window.attachEvent){
	        div3.attachEvent("onclick", click3);
	    }
    }
    function click2() {
        alert("I am div2,remove div3 event");
        if(window.addEventListener){
	        div3.removeEventListener("click", click3, false);
	    }else if (window.attachEvent){
	        div3.detachEvent("onclick", click3);
	    }
    }
    function click3() {
        alert("I am div3");
    }
    
    var div1 = document.getElementById("div1");
    var div2 = document.getElementById("div2");
    var div3 = document.getElementById("div3");
    
    if(window.addEventListener){
        div1.addEventListener("click", click1, false);
        div2.addEventListener("click", click2, false);
    }else if (window.attachEvent){
        div1.attachEvent("onclick", click1);
        div2.attachEvent("onclick", click2);
    }
</script>    
</body>
</html>

 

 

三、2级DOM事件冒泡模型(Bubble Model)

 

      在2级DOM事件模型中,事件传播分三个阶段进行,即捕获阶段(capturing)、目标阶段和冒泡阶段(bubbling)。在捕获阶段,事件从Document对象沿着文档树向下传播给目标节点,如果目标的任何一个祖先(不是目标本身)专门注册了捕获事件句柄,那么在事件传播过程中,就会运行这些句柄,在冒泡阶段,事件将从目标元素向上传播回或气泡回Document对象的文档层次。虽然所有事件都受事件传播的捕获阶段的支配,但并非所有类型的事件都起泡。

      在注册事件时,useCapture参数确定侦听器是运行于捕获阶段、目标阶段还是冒泡阶段。 如果将 useCapture 设置为 true,则侦听器只在捕获阶段处理事件,而不在目标或冒泡阶段 处理事件。 如果useCapture 为 false,则侦听器只在目标或冒泡阶段处理事件。 要在所有三个阶段都侦听事件,需调用两次 addEventListener,一次将 useCapture 设置为 true,第二次再将useCapture 设置为 false。

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="zh-CN">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"    />
<title>浏览器事件机制——冒泡处理</title>
<style>
     div {border:1px  solid blue;}
     div#divGrandpa {padding:40px;background-color:#aaaaaa;}
     div#divFather {padding:40px;background-color:#bbbbbb;}
     div#divSon {padding:40px;background-color:#cccccc;}
</style>
</head>
 <body>
<div id="divGrandpa" style="width:300px;height:200px;" >
       <div id="divFather" style="width:200px;height:120px;" >
            <div id="divSon" style="width:100px;height:40px;" >
				点击我
            </div>
       </div>
</div>
<script>
    function showSon() {
        alert("I am son");
    }
    function showFather() {
        alert("I am father");
    }
    function showGrandpa() {
        alert("I am Grandpa");
    }
    var grandpa = document.getElementById("divGrandpa");
    var father = document.getElementById("divFather");
    var son = document.getElementById("divSon");
    if(window.addEventListener){
        grandpa.addEventListener("click", showGrandpa, false);
        father.addEventListener("click", showFather, false);
        son.addEventListener("click", showSon, false);
    }else if (window.attachEvent){
        grandpa.attachEvent("onclick", showGrandpa);
        father.attachEvent("onclick", showFather);
        son.attachEvent("onclick", showSon);
    }
</script>    
</body>
</html>

 

     从运行结果来看,对于ie,在ie(ie8之前的版本,包括ie8)中当点击son节点时,会分别弹出I am son、I am father和I am Grandpa,即事件最先被底层的结点触发,再逐渐上传,直到最外层的结点,冒泡方式为儿子——>父亲的模式;在Firefox等支持标准事件模型的浏览器中,跟addEventListener的Capture参数有关,当设置为true时,为捕获模式,事件会从最顶层的结点往下传输,即 父亲——>儿子的传播模式。当设为false(默认值)时,则会按冒泡模式传递事件。另外由于ie9即支持window.attachEvent,又支持window.addEventListener,所以会根据代码的书写来运行其效果的。


四、如何停止事件的传递


在IE浏览器中可以调用以下代码
event.cancelBubble = true;
在Firefox等遵循W3C规范的浏览器中,可以调用以下代码
e.stopPropagation();

      调用以上代码后可以终止事件在传播过程的捕获、目标处理或起泡阶段进一步传播。调用该方法后,该节点上处理该事件的处理程序将被调用,事件不再被分派到其他节点(即不再进一步传播)。
      该方法(属性)将停止事件的传播,阻止它被分派到其他 Document 节点。在事件传播的任何阶段都可以调用它。注意,虽然该方法不能阻止同一个 Document 节点上的其他事件句柄被调用,但是它可以阻止把事件分派到其他节点。

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="zh-CN">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"    />
<title>浏览器事件机制——停止事件的进一步传递</title>
<style>
     div {border:1px  solid blue;}
     div#divGrandpa {padding:40px;background-color:#aaaaaa;}
     div#divFather {padding:40px;background-color:#bbbbbb;}
     div#divSon {padding:40px;background-color:#cccccc;}
</style>
</head>
 <body>
<div id="divGrandpa" style="width:300px;height:200px;" >
       <div id="divFather" style="width:200px;height:120px;" >
            <div id="divSon" style="width:100px;height:40px;" >
				点击我
            </div>
       </div>
</div>

<script>
    function showSon(e) {
        alert("I am son");
    }
    function showFather(e) {
        //IE把event对象作为window对象的一个属性,而W3C把event对象作为处理程序的一个参数
       e = e || event;
        if(e.stopPropagation){
        	e.stopPropagation();
        }else{
        	e.cancelBubble = true;
        }
        alert("I am father");
    }
    function showGrandpa(e) {
        alert("I am Grandpa");
    }
    var grandpa = document.getElementById("divGrandpa");
    var father = document.getElementById("divFather");
    var son = document.getElementById("divSon");
    if(window.addEventListener){
        grandpa.addEventListener("click", showGrandpa, false);
        father.addEventListener("click", showFather, false);
        son.addEventListener("click", showSon, false);
    }else if (window.attachEvent){
        grandpa.attachEvent("onclick", showGrandpa);
        father.attachEvent("onclick", showFather);
        son.attachEvent("onclick", showSon);
    }
</script>    
</body>
</html>

 

以上代码,把useCapture 设为false后,是在冒泡的时候传播事件。当点击son节点时,先执行son注册的事件,再执行father注册的事件,此时该事件中阻止了事件的向上传播,故grandpa注册的事件被阻止。如果把useCapture 设为true后,是在捕获阶段传播事件。当点击son节点时,是先执行grandpa注册的事件,再执行father注册的事件,此时该事件函数中阻止了事件的传播,故son节点注册的事件不会被执行。

五、自定义事件

 

1、不带参数事件处理,也是最简单的事件设计模式


      最简单的一种模式是将一个类的方法成员定义为事件,通常是一个空函数,当程序需要处理该事件时,再进行扩充该事件接口。比如:

 

function Class1(){
		//构造函数
	}
	Class1.prototype = {
		show : function(){
			this.onShow();//触发onShow事件
		},
		onShow : function(){}//定义事件接口
	}
	//创建class1实例
	var obj = new Class1();
	//创建obj的onShow事件处理程序
	obj.onShow = function(){
		alert('onshow event');
	}
	//调用obj的show方法
	obj.show();

 

    以上实现,每个事件接口仅能绑定1个事件处理程序


2、给事件处理程序传递参数

//将有参数的函数封装为无参数的函数
	function createFunction(obj, strFn){
		obj = obj || window;
		var args = [];
		for(var i = 2; i < arguments.length; i++){
			args.push(arguments[i]);
		}
		return function(){
		    //该语句相当于obj[strFn](args[0],args[1],...);
			obj[strFn].apply(obj,args);
		}
	}
	//定义类 Class1
	function Class1(){
		//构造函数
	}
	Class1.prototype = {
		show : function(){
			this.onShow();//触发onShow事件
		},
		onShow : function(){}//定义事件接口
	}
	//创建class1实例
	var obj = new Class1();
	//创建obj的onShow事件处理程序
	function objOnShow(userName){
		alert('hello, ' + userName);
	}
	var userName = 'xiaowang';
	//绑定obj的onShow事件
	obj.onShow = createFunction(null,'objOnShow',userName);
	//调用obj的show方法
	obj.show();

 

      在以上代码中,将变量userName作为参数传递给了objOnShow事件处理程序。事实上,obj.onShow 得到的事件处理程序并不是objOnShow,而是由createFunction返回的一个无参函数


3、自定义事件支持多绑定

//定义类 Class1
	function Class1(){
		//构造函数
	}
	Class1.prototype = {
		show : function(){
		    //如果有事件绑定则循环onshow数组,触发该事件
		    if(this.onshow){
		    	for(var i = 0, len = this.onshow.length; i < len; i++){
		    		this.onshow[i]();//调用事件处理程序
		    	} 
		    }
		},
		addEventOnShow : function (_eHandler){
			this.onshow = this.onshow || [];//用数组存储绑定的事件处理程序引用
			this.onshow.push(_eHandler);
		}
	}
	//创建class1实例
	var obj = new Class1();
	//事件一
	function onShow1(){
		alert('event1');
	}
	//事件二
	function onShow2(){
		alert('event2');
	}
	//绑定事件
	obj.addEventOnShow(onShow1);
	obj.addEventOnShow(onShow2);
	//调用obj的show方法
	obj.show();

 

4、自定义事件支持带参数的多绑定

//将有参数的函数封装为无参数的函数
	function createFunction(obj, strFn){
		obj = obj || window;
		var args = [];
		for(var i = 2; i < arguments.length; i++){
			args.push(arguments[i]);
		}
		return function(){
		    //该语句相当于obj[strFn](args[0],args[1],...);
			obj[strFn].apply(obj,args);
		}
	}
	//定义类 Class1
	function Class1(){
		//构造函数
	}
	Class1.prototype = {
		show : function(){
		    //如果有事件绑定则循环onshow数组,触发该事件
		    if(this.onshow){
		    	for(var i = 0, len = this.onshow.length; i < len; i++){
		    		this.onshow[i]();//调用事件处理程序
		    	} 
		    }
		},
		addEventOnShow : function (_eHandler){
			this.onshow = this.onshow || [];//用数组存储绑定的事件处理程序引用
			this.onshow.push(_eHandler);
		}
	}
	//创建class1实例
	var obj = new Class1();
	//创建obj的onShow事件处理程序
	function objOnShow(userName){
		alert('hello, ' + userName);
	}
	//事件一
	var  userName1 = 'xiaowang';
	var  onShow1 = createFunction(null,'objOnShow',userName1);
	//事件一
	var  userName2 = 'xiaoli';
	var  onShow2 = createFunction(null,'objOnShow',userName2);
	//绑定事件
	obj.addEventOnShow(onShow1);
	obj.addEventOnShow(onShow2);
	//调用obj的show方法
	obj.show();

    以上实现把带参数和多绑定结合在一起,还可以增加一个removeEventOnShow来删除已注册的事件。

 

六、把对象注册为事件句柄

 

      在编写面向对象的JavaScript程序时,如果想用对象作为事件句柄,那么可以使用如下的函数来注册它们:

function registerObjectEventHandler(element,eventtype,listener,captures){
    element.addEventListener(eventtype,
        function(event) {listener.handleEvent(event);},captures);
}

      用这个函数可以把任何对象注册为事件句柄,只要它定义了handleEvent()方法。Firefox(以及其他基于Mozilla代码的浏览器)允许直接把定义了handleEvent()方法的事件监听器对象传递给addEventListener()方法而不是函数引用。对于这些浏览器来说,不需要我们刚才给出的特殊注册函数。

 

请看下面的例子

 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>把对象注册为事件句柄</title>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  </head>
  
  <body>
    <input type="button" value = "点击我" id = "btn">
  </body>
</html>

<script type="text/javascript">
<!--
	var EventHandler = function(){}
	EventHandler.prototype.handleEvent = function(event){
		alert('用对象作为事件句柄,只要实现该对象的方法handleEvent即可');
		alert(event.type);
	}
	var objectHandler = new EventHandler();
	var btn1Obj = document.getElementById("btn"); 
	if(window.addEventListener){
		btn1Obj.addEventListener("click",objectHandler,false); 
	}else if (window.attachEvent){
	    //btn1Obj.attachEvent("onclick",objectHandler);//调用失败,说明不支持把对象注册为事件句柄
		//btn1Obj.attachEvent("onclick",objectHandler.handleEvent);
		registerObjectEventHandler(btn1Obj,"onclick",objectHandler);
	}

    /**
     * 对于不支持把对象注册为事件句柄的浏览器,可以调用以下方法来实现
     */
	function registerObjectEventHandler(element,eventtype,listener,captures){
		if(window.addEventListener){
			element.addEventListener(eventtype,
				function(event) {listener.handleEvent(event);},captures);
		}else if (window.attachEvent){
			element.attachEvent(eventtype,
				function(event) {listener.handleEvent(event);});
		}
	}

//-->
</script>

 

 

 

分享到:
评论

相关推荐

    115极速浏览器 v6.0.5.29.zip

    软件基于Chromium内核, 它采用多线程框架设计,极大的优化了浏览器的内存占用,采用的多重加速机制,更大幅提高了网页的加载速度,并高度集成了115.com的多种重量级网络服务:聚合搜索、115网络U盘、在线收藏夹、...

    傲游云浏览器(Maxthon) v4.1.2.1000 Mac版.zip

    云同步 - 收藏、选项设置、智能填表等账户数据瞬间同步,实现与Windows, Android, iOS平台设备上的无缝连接 超级拖拽 页面中拖拽链接可以在新标签内打开网页 拖拽图片可在新标签内查看图片或直接保存到桌面 拖拽...

    快快浏览器KChrome v3.2.5.20417.zip

    Google搜索数据库的安全积累与沙盒机制的应用,保证了您在浏览时能杜绝99%的恶意软件侵入的机会!  安全、精简、快速、绿色,随时拷贝带走  Google服务器同步数据保存,一键恢复  快速与兼容模式随时切换,...

    Javascript处理DOM元素事件实现代码

    但是浏览器有addEventListener和attachEvent方法可供调用,从而模拟出类似于C#中的事件委托的事件触发机制! 代码如下: /* * 功能:事件处理 * Author:LQB * 时间:2009-1-4 * #include JCore.js */ var JEvents = ...

    JavaScript王者归来part.1 总数2

     13.1.1 消息与事件响应   13.1.2 浏览器的事件驱动机制   13.2 基本事件处理   13.2.1 事件和事件类型   13.2.2 事件的绑定   13.2.3 直接调用事件处理函数   13.2.4 事件处理函数的返回值   13.2.5...

    pushlet.jar(2.0.4)

    用采用RMI、CORBA或者自定义TCP/IP信息的applet来实现。这些技术往往由于复杂而产生诸多不利之处:技术难以实现、存在防火墙限制(因为需要打开非HTTP的通讯端口)、需要额外的server开发和维护。并且除了刷新整个...

    [原创]基于FLASH机制实现的不同域窗口相互调用js的插件-虫洞

    2)本插件是基于同一台电脑上的FLASH之间能进行LocalConnection以及FLASH与JS能相互调用的原理而设计,将客户端不同域页面间Javascript相互调用转化为了FLASH之间的"本地命令发送". 3)本插件在不同域页面间Javascript...

    Web组件框架NovaJS.zip

    浏览器支持度移动端主流浏览器(Android2.3 , iOS4.0 )PC端主流浏览器(Chrome, Safari, Firefox, IE8 )功能列表自定义元素Scoped CSS模板双向绑定监听属性事件模型方法切片Behaviors复用机制 标签:NovaJS

    Delphi7 编程 100 实例

    ToolBar工具栏控件的使用 动态建立主菜单选项 窗口界面的动态分隔条...自定义系统的About项 修改系统级菜单 实现透明窗体效果 爆破特技窗体 只允许建立一次子窗体的MDI程序 从外部DLL中调用子窗口...

    精通JS脚本之ExtJS框架.part2.rar

    第2章 JavaScript浏览器对象模型与事件处理 2.1 浏览器对象模型 2.1.1 文档对象 2.1.2 表单及其元素对象 2.1.3 浏览器信息对象 2.1.4 窗口对象 2.1.5 网址对象 2.1.6 历史记录对象 2.2 基本事件处理 2.3 2...

    精通JS脚本之ExtJS框架.part1.rar

    第2章 JavaScript浏览器对象模型与事件处理 2.1 浏览器对象模型 2.1.1 文档对象 2.1.2 表单及其元素对象 2.1.3 浏览器信息对象 2.1.4 窗口对象 2.1.5 网址对象 2.1.6 历史记录对象 2.2 基本事件处理 2.3 2...

    庖丁解牛:纵向切入ASP.NET 3.5控件和组件开发技术

    5.2 事件和数据回发机制的实现 148 5.2.1 客户端回传事件接口ipostbackeventhandler 148 5.2.2 客户端回发/回调揭密 150 5.2.3 回传数据处理接口ipostbackdatahandler 153 5.2.4 正确处理继承基类中控件的事件 ...

    通达OA协同办公 (Office Anywhere) 2011增强版 v5.1.120718

    灵活定义角色权限,全面面向集团应用5、内置工作流系统,通过傲视同群的可视化“智能表单设计”、灵活的图形化自定义流程,实现各项业务流程的管理,文件流转及审批6、完善灵活的菜单自定义机制,方便挂接外部的B/S...

    jQuery权威指南366页完整版pdf和源码打包

    3.10.3 功能实现 3.10.4 代码分析 3.11 本章小结 第4章 jquery中的事件与应用 4.1 事件机制 4.2 页面载入事件 4.2.1 ready()方法的工作原理 4.2.2 ready()方法的几种相同写法 4.3 绑定事件 ...

    庖丁解牛 纵向切入ASP.NET 3.5控件和组件开发 part1

    5.2 事件和数据回发机制的实现 148 5.2.1 客户端回传事件接口ipostbackeventhandler 148 5.2.2 客户端回发/回调揭密 150 5.2.3 回传数据处理接口ipostbackdatahandler 153 5.2.4 正确处理继承基类中控件的事件 ...

    庖丁解牛 纵向切入ASP.NET 3.5控件和组件开发 part2

    5.2 事件和数据回发机制的实现 148 5.2.1 客户端回传事件接口ipostbackeventhandler 148 5.2.2 客户端回发/回调揭密 150 5.2.3 回传数据处理接口ipostbackdatahandler 153 5.2.4 正确处理继承基类中控件的事件 ...

    Java典型模块

    27.2.1 实现与浏览器通信的类 27.2.2 实现Web服务器的类 27.2.3 浏览器所请求的页面 27.3 知识点扩展——HTTP协议知识 27.3.1 HTTP协议原理 27.3.2 实现HTTP协议服务器的原理 27.4 小结 28.1 QQ聊天工具原理 28.1.1 ...

    jQuery教程学习

    第二十一节:jQuery速成- 页面的载入事件与事件处理 第二十二节:jQuery速成- 鼠标事件与交互 第二十三节:jQuery速成- 焦点事件 第二十四节:jQuery速成- 键盘事件 第二十五节:jQuery速成- 其他事件 第六章:效果...

Global site tag (gtag.js) - Google Analytics