欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

动态绑定事件的方法

程序员文章站 2024-03-18 18:38:10
...
<!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;
	}
}