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

本篇博文解决的问题:

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

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

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

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

一 示例

       要求:根据以下代码,在<script>标签中添加代码,删除一条数据。

       下面代码中可以看出添加的代码必须使用js的原生方式,DOM获取元素的方法有getElementById、getElementsByName、getElementsByTagName和getElementByClassName。

       若根据id来获取元素并添加点击事件,由于只有父元素具有id,因此点击事件添加在父元素上,子元素“删除”进行响应,则需要用到事件委托:通过父元素给子元素注册点击事件。 

       提供的代码为:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <!--code here-->
  <title>demo</title>
  <style>
    * {
      padding: 0;
      margin: 0;
    }

    .head, li div {
      display: inline-block;
      width: 70px;
      text-align: center;
    }

    li .id, li .sex, .id, .sex {
      width: 15px;
    }

    li .name, .name {
      width: 40px;
    }

    li .tel, .tel {
      width: 90px;
    }

    li .del, .del {
      width: 15px;
    }

    ul {
      list-style: none;
    }

    .user-delete {
      cursor: pointer;
    }

  </style>
</head>

<body>
  <div id="J_container">
    <div class="record-head">
      <div class="head id">序号</div><div class="head name">姓名</div><div class="head sex">性别</div><div class="head tel">电话号码</div><div class="head province">省份</div><div class="head">操作</div>
    </div>
    <ul id="J_List">
      <li><div class="id">1</div><div class="name">张三</div><div class="sex">男</div><div class="tel">13788888888</div><div class="province">北京</div><div class="user-delete">删除</div></li>
      <li><div class="id">2</div><div class="name">李四</div><div class="sex">女</div><div class="tel">13788887777</div><div class="province">上海</div><div class="user-delete">删除</div></li>
      <li><div class="id">3</div><div class="name">王五</div><div class="sex">男</div><div class="tel">13788889999</div><div class="province">天津</div><div class="user-delete">删除</div></li>
    </ul>
  </div>

<script>
// your code here
</script>
</body>
</html>

       效果为:


       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

<li id = "1">code</li>
<li id = "2">code</li>
<li id = "3">code</li>

       然后给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,可以看到运行结果为:


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

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

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

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


       在上面的操作中,点击了两次,第一次点击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);

运行结果:


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

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

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

猜你喜欢

转载自blog.csdn.net/u013719339/article/details/80578869