zoukankan      html  css  js  c++  java
  • JS--传统事件模型的问题


    事件绑定分为两种情况:传统的事件绑定(内联模型、脚本模型),一种是现代事件绑定模型(DOM2级事件绑定)。

        内联模型的事件绑定是将事件写在元素标签中,将事件绑定函数当做元素的一种属性来实现的,这种绑定模型违反了HTML中分层的原则,故不讨论

    <span style="font-size:18px;"><div id="box" onclick="alert('abc')">测试DIV</div> </span>


    脚本模型,在单独的JS文件中,通过DOM来获取元素,然后为元素绑定事件等,下面讨论的问题都是用的脚本模型

    <span style="font-size:18px;">window.onload = function(){
    	var box = document.getElementById("box");
    	box.onclick = function(){
    		alert("box");
    	}
    }</span>
    </pre><pre code_snippet_id="314036" snippet_file_name="blog_20140426_3_5927837" class="javascript" name="code"><span style="font-size:18px;"></span><h2><span style="font-size:18px;"><strong>问题一</strong>、一个事件绑定函数被触发多次,导致前面的会被后面的覆盖,以window.onload 为例</span></h2>
    
    <span style="font-size:18px;">	//下面程序端只是输出 "第二次"
    	window.onload = function(){
    		alert("第一次");
    	}
    
    	window.onload=function(){
    		alert("第二次");
    	}</span>


        分别在第一个onload函数之前输出这个函数以及它的类型,发现,在没有定义之前,旧版的火狐输出的是undefined,新版的火狐、IE、谷歌都是null,类型都是object,在定义这儿函数之后,输出的类型都是function,代码段如下:

    <span style="font-size:18px;">alert(window.onload);	//null	旧版火狐(3.8.6)输出为undefined
    	alert(typeof window.onload);	//object
    
    	window.onload = function(){
    		alert("第一次");
    	}
    
    	alert(window.onload);	//当已经定义了这个函数的时候输出是这个函数的函数体
    	alert(typeof window.onload);	//旧版和新版都是输出的undefined
    
    	window.onload=function(){
    		alert("第二次");
    	}</span>


    解决覆盖问题:可以创建一个保存器将它保存下来,然后在下一次注册函数的函数中先执行一次,然后再注册新的函数,利用了JS中没有块级作用域的概念

    <span style="font-size:18px;">	window.onload = function(){
    		alert("第一次");
    	}
    
    	//创建一个保存器
    	if(typeof window.onload == "function"){
    		var saved = null;
    		saved = window.onload;	//保存上一个事件
    	}
    
    	window.onload=function(){
    		if(typeof saved != "null"){
    			saved();	//执行上一个事件
    		}
    		alert("第二次");	//执行本事件中的内容
    	}</span>
    <span style="font-size:18px;"> 	//上面的保存器中saved就相当于window.onload;saved()就相当于window.onload();但是window.onload后面
     	//是不能够直接加括号的,saved相当于注册的匿名函数,匿名函数的自我执行是直接在后面添加一个括号
     	//所以 saved() 相当于 windo.onload=function(){};
    </span>


    问题二、事件切换器(this自动传参以及可读性问题)

        页面上有两个样式表

    <span style="font-size:18px;">  <style>
    	.red{
    		200px;
    		height:200px;
    		background-color:red;
    	}
    	.blue{
    		200px;
    		height:200px;
    		background-color:blue;
    	}
      </style></span>
    <span style="font-size:18px;"><div id="box">测试DIV</div></span>

        切换器:在单击这个div的时候,切换样式

    <span style="font-size:18px;">//点击页面上的某个元素,改变他们的背景颜色
    	window.onload=function(){
    		var box = document.getElementById("box");
    		box.onclick = toRed;
    	}
    
    	//下面两个函数中 this 如果绑定到了box.onclick 的事件中,this是代表的box对象,在全局代表的是window
    
    	function toRed(){
    		this.className = "red";
    		this.onclick = toBlue;
    	}
    
    	function toBlue(){
    		this.className = "blue";
    		this.onclick = toRed;
    	}</span>

        上面这个切换器中存在一个问题就是,当在添加一个box.onclick=function(){}; 的时候,被新添加的函数又会被覆盖

    <span style="font-size:18px;">window.onload=function(){
    		var box = document.getElementById("box");
    		box.onclick = function(){	//被覆盖了,不会被执行
    			alert("new");
    		}
    		box.onclick = toRed;
    	}</span>

        为了解决这个覆盖的问题,可以在上面的新添加的匿名函数中执行后面的事件绑定函数

    <span style="font-size:18px;">window.onload=function(){
    		var box = document.getElementById("box");
    		box.onclick=function(){
    			alert("new");
    			//toBlue();			//通过匿名函数来执行函数的话,里面的this代表的就是window
    			toBlue.call(this);	//通过冒充调用将this传递过去
    		}
    	}</span>


    为了解决上面的三个问题,我们自己写一个事件添加函数

        在JS中,数组是集数组、集合与字典一生的类型,对象操作可以使用数组操作来完成,window.onload 相当于window['onload'];

    <span style="font-size:18px;">	window['onload'] = function(){
    		alert("onload");
    	}</span>


    自己手动的写一个事件添加函数:

    <span style="font-size:18px;">	function addEvent(obj,type,fun){//obj:要添加事件绑定函数的对象,type:事件名,fun:事件执行函数
    		var saved = null;	//保存每次触发的事件处理函数
    		if(typeof obj[type] == "function"){
    			saved = obj[type];	//保存上一个事件
    		}
    		
    		obj[type] = function(){
    			if(saved){		//如果存在上一个事件,就先执行上一个事件
    				alert(saved);
    				saved();
    			}
    			fun();
    		}
    	}</span>

        通过这个事件函数,可以给元素添加多个事件绑定函数。并且都会执行

    <span style="font-size:18px;">window.onload=function(){
    		var box = document.getElementById("box");
    			addEvent(box,"onclick",function(){alert("第一个");});
    			addEvent(box,"onclick",function(){alert("第二个");});
    			addEvent(box,"onclick",function(){alert("第三个");});
    	}</span>


        将这个自定义的事件添加函数用在上面的事件切换器上面去,能够解决被覆盖的问题,但同样要注意this的传值需要我们手动传递。

    <span style="font-size:18px;">	function addEvent(obj,type,fun){//obj:要添加事件绑定函数的对象,type:事件名,fun:事件执行函数
    		var saved = null;	//保存每次触发的事件处理函数
    		if(typeof obj[type] == "function"){
    			saved = obj[type];	//保存上一个事件
    		}
    		
    		obj[type] = function(){
    			if(saved){		//如果存在上一个事件,就先执行上一个事件
    				saved();
    			}
    			fun.call(this);		//注意this的作用范围,故要传递进去
    			
    		}
    	}
    
    	window.onload=function(){
    		var box = document.getElementById("box");
    			//addEvent(box,"onclick",function(){alert("OK");});
    			addEvent(box,"onclick",toRed);
    	}
    
    	//这和上面通过window.onload的注册时一样的
    	//addEvent(window,"onload",function(){
    	///	var box = document.getElementById("box");
    	///	addEvent(box,"onclick",function(){alert("OK");});
    	//	addEvent(box,"onclick",toRed);
    	//});
    
    	function toRed(){
    		this.className = "red";
    		//this.onclick = toBlue;
    		addEvent(this,"onclick",toBlue);
    	}
    
    	function toBlue(){
    		this.className = "blue";
    		//this.onclick = toRed;
    		addEvent(this,"onclick",toRed);
    	}</span>

    问题三、浏览器假死

        上面通过我们自定义的函数,解决了传统的事件绑定的缺点,但是上面这个事件切换器,如果在火狐中运行,会发现当我们点击到一定的次数后,浏览器就会卡死。这是因为在我们自己写的这个事件添加函数,其本质是通过递归来实现的,JS中有一种概念叫闭包,它会把变量驻留在内存中,这种概念上和递归是一样的,当递归多次后,内存资源占用过多,导致浏览器假死的现象,而IE中有垃圾回收机制,它会帮我们回收一些资源然后释放,故不存在这种问题。解决这种假死办法就是每次定义之前,删除之前的东西。

    <span style="font-size:18px;">	//移除事件处理函数,采用的是一刀切的方法
    	function removeEvent(obj,type){
    		if(typeof obj[type] == "function"){
    			obj[type] = null;
    		}
    	}</span>
    <span style="font-size:18px;">//调用函数修改为
    function toRed() {
    	this.className = 'red';
    	removeEvent(this, 'click');
    	addEvent(this, 'click', toBlue);	
    }</span>


    问题四、不能屏蔽重复注册问题

    <span style="font-size:18px;">	addEvent("window","onload",init);	//init是一个函数
    	addEvent("window","onload",init);
    	addEvent("window","onload",init);</span>


       这种问题同样需要我们手动的去解决判断,比较麻烦,W3C在它的DOM2事件中解决了此问题,解决办法见下一章



  • 相关阅读:
    浅析HSTS
    浅析Diffie–Hellman
    SSIM(结构相似度算法)不同实现版本的差异
    某直播App问题分析
    相机与摄影基础
    Macaca-iOS入门那些事2
    Macaca-iOS入门那些事
    iOS instruments trace文件解析方案
    关于QCon2015感想与反思
    深入浅出Android App耗电量统计
  • 原文地址:https://www.cnblogs.com/qigang/p/3841951.html
Copyright © 2011-2022 走看看