jsDOM获取元素方法、事件委托、事件捕获和事件冒泡

本篇博文解决的问题:

1、给父元素注册点击事件,特定子元素响应该点击事件;——2.2 事件委托

2、给父元素注册点击事件,点击父元素,所有子元素响应/不响应该点击事件;——2.3 事件冒泡

3、给子元素和父元素注册点击事件,点击子元素,自己有点击事件的父元素响应/不响应自己的点击事件。——2.3 事件冒泡

4、给多个子元素和父元素注册点击事件,点击子元素,只响应子元素的点击事件。——2.2 事件委托 2.3 事件冒泡

 

一 示例

       要求:根据以下代码,在

       效果为:

jsDOM获取元素方法、事件委托、事件捕获和事件冒泡_第1张图片

       script中的代码为:

//方法1,直接获取class,为删除的div添加点击事件
var a = document.getElementsByClassName("user-delete");
for(var i = 0, len = a.length; i < len; i++) {
	a[i].addEventListener("click", function(e) {
		this.parentNode.remove();
	})
}

//方法2:点击事件添加在ul上,使用事件委托
var deletef = function() {
	this.del = function(li) {
		li.remove();
	}
}

var delli = new deletef();
var parent = document.getElementById("J_List");
parent.addEventListener("click", function(e) {
	if (e.target.className === "user-delete") {
		delli.del(e.target.parentNode);
	}
},false);

 

 

二 相关方法解释

       在上面script中涉及到的方法有:

1、DOM获取元素的方法

1)getElementById

       返回一个匹配特定ID的元素,不存在则返回null。

       一般情况下认为这是一个唯一值,因此如果页面上有多个相同id的元素,DOM只会解析第一个元素,页面只会显示第一个元素,则也只能获取第一个元素。

2)getElementsByName

       根据给定的“name”返回一个在HTML document的节点列表集合。

       name是元素的name属性的值。包含添加了name自定义属性的元素。

       在IE和Opera中,该方法还会返回id为指定值的元素,所以最好不要为元素的name和id赋同样的值。

3)getElementsByTagName

       返回一个包括所有给定标签名称的元素的HTML集合,这个文件结构都会被搜索,包括根结点。

       返回的HTML集合是动态的,即它可以自动更新自己来保持和DOM树的同步而不用再次调用。

       使用方法:

var elements = document.getElementsByTagName(name);

       name是一个代表元素的名称的字符串。

4)getElementByClassName

       返回一个包含了所有指定类名的子元素的类数组对象。当在document对象上调用时,会搜索真个DOM文档,包括根结点。

       在任意元素上调用此方法将返回以当前元素为根结点,所有指定类名的子元素。

 

2、DOM事件委托

       事件委托,即把一个元素的响应事件的函数委托给另一个元素。

       一般情况下,把一个或一组元素的事件委托给它的父元素或者更外层元素上,因此真正绑定事件的是外层元素,当事件响应到需要绑定的元素上时,会通过事件冒泡机制触发到它的外层元素的绑定事件上,然后在外层元素上进行执行。

       重新来看上面的代码:

var parent = document.getElementById("J_List");
parent.addEventListener("click", function(e) {
	if (e.target.className === "user-delete") {
		//真正要执行的代码
	}
},false);

       实际上要进行click事件响应的是li的子元素“删除”这个div,但是在代码中可以看到事件绑定给了ul元素,e为点击的元素,即所绑定的外层元素,target元素是在外层元素之下具体被点击的元素,通过判断target的属性来进行匹配,此处是通过class来判断找到“删除”这个div。

       注意,代码中少了兼容性处理:

var parent = document.getElementById("J_List");
parent.addEventListener("click", function(e) {
	//兼容性处理
	var event = e || window.event;
	var target = event.target || event.srcElement;
	if (target.className === "user-delete") {
		//真正要执行的代码
	}
},false);

       更具体和详细的解释等可以看JavaScript事件委托详解。
 

3、DOM事件捕获和事件冒泡

       上面提到了事件冒泡,它具体是什么呢?

事件冒泡:当鼠标点击或者触发DOM事件时,浏览器会从内向外进行事件传播,直到根节点。即点击了父元素,如果子元素通过事件冒泡方式注册了对应的事件,会先触发子元素绑定的事件。

事件捕获:当鼠标点击或者触发DOM事件时,浏览器会从根节点开始由外到内进行事件传播。即点击了子元素,如果父元素通过事件捕获方式注册了对应的事件,会先触发父元素绑定的事件。

       可以看到这两个的事件的响应方式正好相反,它们的行为都是事件传播。

       DOM事件流存在三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。

       DOM标准事件流的触发先后顺序为:先捕获再冒泡。

事件冒泡

先来看一个例子:

       首先,给上面的代码中的每个li添加一个id

  • code
  • code
  • code
  •        然后给body、ul和id为1的li添加点击事件:

    var parent = document.getElementById("J_List");
    var child = document.getElementById("1");
    
    document.body.addEventListener("click", function(e){
    	alert("body");
    }, false);
    parent.addEventListener("click", function(e){
    	alert("parent");
    }, false);
    child.addEventListener("click", function(e){
    	alert("child");
    }, false);

           点击第一个li,可以看到运行结果为:

    jsDOM获取元素方法、事件委托、事件捕获和事件冒泡_第2张图片

           点击子元素后,事件触发顺序是从内到外的:child-parent-body。虽然只点击了子元素,但它的所有有点击事件的父元素全都响应了,这就是事件冒泡。

           可以这样理解:虽然只点击了子元素li1,但li1在parent里面,parent又在body里面,把它们都当成有压感的东西,压了最上面的,那下面的也应该有感觉,有了感觉就要有相应的反应,这个反应就是响应自己的点击事件。

           如果我不想要父元素和body有反应怎么办呢?这就需要阻止事件传播。修改child的代码:

    child.addEventListener("click", function(e){
    	alert("child");
    	//停止事件传播
    	e.stopPropagation();
    }, false);

           运行结果:

    jsDOM获取元素方法、事件委托、事件捕获和事件冒泡_第3张图片

           在上面的操作中,点击了两次,第一次点击li1,第二次点击li3,可以看到弹出的结果就不同:

           点击li1:li1有点击事件,且阻止了事件的传播,因此只会弹出child;

           点击li3:li3没有点击事件,进行事件传播,传播到ul,检测到有点击事件,弹出parent,再进行传播,传播到body,检测到有点击事件,弹出body。

           由于冒泡是从内到外,因此阻止冒泡只能阻止该元素的事件向外传递,所以点击li1会阻止事件传递。

           而点击元素的子元素,由于子元素没有阻止事件的传播,因此它都会冒泡到最外层元素,所以点击li3后外层元素都会响应。

           所以如果不想点击子元素后父元素的点击事件进行响应,就需要使该父元素的子元素阻止冒泡。则如果给li3增加一个监听事件,点击li3就不会有任何反应。

    var li3 = document.getElementById("3");
    li3.addEventListener("click", function(e){
        e.stopPropagation();
    }, false);

           那么,如果父元素注册了点击事件,其中的多个子元素也注册了点击事件,我希望不论点击哪一个子元素,父元素的点击事件都不响应,应该怎么做呢?

    有两个方法:

           方法1:子元素的点击事件写在自己的元素上,在每个点击事件里添加阻止冒泡事件的语句;

           方法2:使用事件委托,将所有的点击事件写在父元素上,使用条件语句进行判断,在最后写上阻止冒泡事件的语句。

    因此,如果部分子元素需要父元素响应自己的点击事件、部分元素不需要父元素响应自己的点击事件,也对应以下方法:

           方法1:子元素的点击事件写在自己的元素上,根据需要决定是否添加添加阻止冒泡事件的语句;

           方法2:使用事件委托,将不需要父元素响应的点击事件写在父元素上,使用条件语句进行判断,在最后写上阻止冒泡事件的语句。

           方法3:与方法2相反,不推荐。

    事件捕获

    修改上面的代码:

    //在parent后添加事件传播,表示是事件传播
    parent.addEventListener("click", function(e){
    	alert("parent事件传播");
    }, false);
    
    //添加parent的事件捕获,注意false变为了true
    parent.addEventListener("click", function(e){
    	alert("parent事件捕获");
    }, true);

    运行结果:

    jsDOM获取元素方法、事件委托、事件捕获和事件冒泡_第4张图片

    可以看到执行顺序为:parent事件捕获-child-parent事件传播-body。

    父元素通过事件捕获的方式注册了click事件,根据DOM标准事件流的触发顺序,这个click事件在事件捕获阶段就会触发,然后到了目标阶段,即事件源,然后再进行事件传播,而parent也用冒泡方式注册了click事件,所以又会触发冒泡事件,最后冒泡到根节点。

    根据上面的代码可以看到,冒泡和捕获在代码上的区别是监听事件的第三个参数,第三个参数是可选参数,默认为false,即事件冒泡,当设置为true时为事件捕获。具体使用方式看EventTarget.addEventListener()。

    你可能感兴趣的:(JavaScript)