HTML中动态生成内容的事件绑定问题

由于实际的需要,有时需要往网页中动态的插入HTML内容,并在插入的节点中绑定事件处理函数。我们知道,用Javascript向HTML文档中插入内容,有两种方法,一种是在写HTML代码写入JS,然后插入到文档中;另外一种是通过ajax的方式,从服务器获取数据,然后用js把获取的数据经过处理后插入文档中;两种方法各有特点,本文将分析新插入到文档中的元素的事件绑定问题,并假定新插入的对象不做内联的事件绑定(即不用类似 <a onclick=””>的形式)。所有的示例将会用到jQuery和原生Javascript。

情况一: HTML代码存放在JS中,请看以下代码:

<body>
<p>第1行内容</p>	
<p>第2行内容</p>	
<p>第3行内容</p>	
<script>
var appendhtml=document.createElement("p");
appendhtml.innerHTML="这是插入的内容";
document.body.appendChild(appendhtml);

var nodep=document.getElementsByTagName("p");
for (var i=0;i<nodep.length;i++){
	nodep[i].onclick=function(){
		console.log("Click Event!");
	}
}
</script>
</body>

上面的代码是用原生的Javascript生成的,当此代码执行时,js会在页面中生成第四个P标签,且点击这四个标签的时候,都会触发相应的动作。那么是不是就是说,用js生成的HTML内容,都可以被绑定相应的事件呢?答案是否定的,在上面代码的script标签中,有两个代码段,代码段一是用来向HTML中插入内容的,代码段二是用来绑定事件的,如果把代码段一和代码段二互换位置,发现JS生成的第四个P标签没有绑定上click事件。用以下代码的jquery测试:

$(function(){
	$("p").click(function(){
		console.log("Click Event");
	})	

	$("<p>这是生成的内容</p>").appendTo("body");
})

发现结果也一样,事件执行成功与否也代码段的顺序有直接的关系。其实原来很好分析,无论是利用getElementsByTagName还是jquery的选择器,当需要的内容还没有被插入时,选择器只会选择页面中已经存在的元素,所以事先没有存在元素是绑定不了事件的。

但是,实际情况是,工作中有可能需要把后来生成的元素绑定上事件,并注册上事件处理函数。例如本网站的留言系统,第一次加载只会显示固定条数的评论,如果评论超过一定数量,剩下的会用ajax的方式进行加载。所有的留言最后都有个回复功能,点击可以恢复相应的留言,也就是说,动态加载上来的留言,也许要绑定click事件,并注册上回复留言的函数。当然偷懒的方法是,为ajax加载上来的内容再注册一个click并再绑定一次相应的函数,但这加大了代码的冗余度,增加系统开销,还会使代码变得难以理解。那么更好的解决办法是什么呢?

留言系统的回复功能

留言系统的回复功能

可以这样理解,无论HTML内容是不是JS生成的,只要没有跨域,所有页面内的元素都属于这个页面,都能够绑定事件,JS中有个非常重要的概念叫事件冒泡,简单来讲,就是子元素产生的事件,会一直冒泡到最顶级父元素,并能够被父元素监测到。请看下图:

事件冒泡

事件冒泡

那么,我能不能在被插入元素的父元素上监测冒泡产生的事件,并回调相应的函数呢?答案是当然是肯定的。来看下面的例子,注意网页面里插入内容是在JS代码的最后。

<script>
$(function(){
	$("body").delegate("p","click",function(){
		console.log("Click Event");
	})	
	$("<p>这是生成的内容</p>").appendTo("body");
})
</script>

这是,发现所有的P元素点击都产生了输出,说明代码运行成功。这里我们用到了jQuery的delegate函数,来看一下官方解释:

Attach a handler to one or more events for all elements that match the selector, now or in the future, based on a specific set of root elements.
根据特定的根元素,把一个或者多个事件注册到指定的元素上,不论这个元素现在是否存在。

在jQuery1.7.3以上的版本中,on方法也可以做这件事,官方有示例说明,这里不再赘述。
jQuery提供一个强大的API来解决这个问题,原生的JS怎么去做呢?下面给出一个简单方案,希望能对大家理解冒泡的原理有所帮助。

<script>
function gelegate(action,selector,callback){
	document.addEventListener(action,function(e){
		if(selector==e.target.tagName.toLowerCase()||selector==e.target.className){
			console.log("Click Event");
			callback();
		}
	})
}
gelegate('click','p',function(){});

var appendhtml=document.createElement("p");
appendhtml.innerHTML="这是插入的内容";
document.body.appendChild(appendhtml);
</script>

注意,以上代码仅针对chrome,实际项目可能会产生兼容性问题。
本人粗见,欢迎拍砖吐槽。

发表回复

评论列表:

roak
roak
非常nice,收藏了
回复此留言
loveloliii
loveloliii
感谢,帮了大忙
回复此留言
Noeltouch
Noeltouch
十分感谢作者!!正好遇到了这个难题,使用简单的一行父元素绑定的代码,马上就成功了!!
回复此留言
Hito
Hito
不清楚你描述的问题
爱好者
爱好者
你们绑定的事件,只能再循环内部,我想问的是怎么在外面绑定 可以重复执行的事件 (可以一直被触发!!!!!!!!!!!!!!!!!!)
回复此留言
爱好者
爱好者
你们绑定的事件,只能再循环内部,我想问的是怎么在外面绑定 可以重复执行的事件 (可以一直被触发!!!!!!!!!!!!!!!!!!)
回复此留言
flyne
flyne
我来测试下
yuxue
yuxue
解决了我的一个大问题,非常感谢
回复此留言
yuxue
yuxue
解决了我的一个大问题,非常感谢
回复此留言