动态绑定事件的方法

<!doctype html>
<html>
<head>
	<meta charset = "utf-8" />
</head>

	<div id = "target"></div>
	<script>
		var div = document.getElementById("target");
		for(var i = 0; i < 8; i++){
			var p = document.createElement("p");
			var b = document.createElement("button");
			b.innerText = "button" + i;
			div.appendChild(b);
			div.appendChild(p);
			
			b.onclick = function(){
				p.innerText = i;
			}
			
		}	
	</script>
</body>

</html>

  上面代码原意是希望将生成的button元素,绑定相对应的p元素,但是运行后发现所有的button元素都绑定了最后一个p元素。

       为什么?

       这是因为onclick事件绑定的函数的词法环境(作用域)都是for循环花括号区域,区域中有p,b,i三个变量。

       而b.onclick = function(){}和这个作用域形成了闭包,函数中使用该作用域的p,i变量,造成所有绑定事件共享这两个变量。

       在页面加载后,所有button 未被点击前,for循环已经结束。作用域变量变为i = 8, p => 最后一个p。

       一触发onclick事件,调用function(){p.innerText = i;},就变成了最后一个p元素显示文本8。

       这十分像java的继承,触发一次onclick就是一次“继承”,然后共用父类的成员变量, p,b是“protected类型”。 

      闭包的参考:   https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures

      所以,在公共的作用域for循环没有结束前立刻保存每一次循环的p和i是十分必要的。

      考虑用参数传递的方式将每次的p和q传递到function(){p.innerText = i;}中,则有:

  function test(p,i){p.innerText = i;}

 

       立刻触发事件传递参数: test(p,i);     

       又onclick需要绑定一个函数,但是b.onclick = function(){}的写法在触发时不能同时绑定。故让test函数内部返回一个操作函数,test函数作为直接的绑定。

  b.onclick = function test (p,i){ 

            return function(){

                     p.innerText = i;

          }

       }     

      test (p,i);


      这样就间接绑定了操作,也传递了参数。

 使用匿名函数可以将代码合并:

 

b.onclick = (function(p,i){
	return function(){
		p.innerText = i;
	}
})(p,i);

  以上代码可以实现效果。

  除了闭包外,还可以使用let来处理。

  let定义函数变量有各自的块作用域,可以保证onclick事件的词法环境中let定义的变量的不同的。

  代码可改为:

  

var div = document.getElementById("target");
for(let i = 0; i < 8; i++){
	let p = document.createElement("p");
	var b = document.createElement("button");
	b.innerText = "button" + i;
	div.appendChild(b);
	div.appendChild(p);
			
	b.onclick = function(){
		p.innerText = i;
	}
}

  

 

   

        

      

posted @ 2018-03-23 13:50  web_小隆  阅读(437)  评论(0编辑  收藏  举报