本文共 7794 字,大约阅读时间需要 25 分钟。
Write By Monkeyfly
以下内容均为原创,如需转载请注明出处。
JavaScript
的事件委托(或称事件代理),用了两天的时间看完了这篇博客,并且将博主提供的案例手动敲了两边,可以说是基本掌握了事件委托的原理和用法。JavaScript的事件委托
的讲解还是比较通俗易懂,详细到位的,在这里向大家推荐一下,感兴趣的小伙伴们可以自行去里面学习。下面就来说一说,我在学习过程中碰到的问题。
querySelectorAll()
方法来获取元素,然后再利用JavaScript
的DOM
操作方法createElement()
动态添加一些元素的话。//通过id获取ul元素var oUl = document.getElementById("list");//通过css选择器获取ul中的所有li元素,返回一个类数组var aLi = document.querySelectorAll("#list li");//初次获取元素列表的长度var startLen = aLi.length;console.log(startLen);
//创建li元素节点var nLi = document.createElement("li");//创建文本节点var textNode = document.createTextNode("我是li元素");//将文本节点插入li元素节点中nLi.appendChild(textNode);//将组合后的li元素,再放入到ul元素内部子元素的末尾oUl.appendChild(nLi);
//动态添加元素后,再次获取元素列表的长度var endLen = aLi.length;console.log(endLen);
createElement()
方法确实成功的在页面添加了元素,但是实际上这个元素列表并没有进行实时的更新,刚才添加的新元素没有被放入最开始的元素列表中,还是独自一人在外面徘徊,孤苦伶仃。consloe.log(startLen === endLen); //true
但是呢,如果此时重新获取一次ul列表中的所有li元素,然后再次获取元素列表的长度
,你就会发现长度确实发生变化了。//动态添加元素后,再次通过css选择器获取ul中的所有li元素,返回一个类数组var aLi = document.querySelectorAll("#list li");//动态添加元素后,再次获取元素列表的长度var endLen = aLi.length;console.log(endLen);
//此时,你会发现开始和结束前后的列表长度不相等了。consloe.log(startLen === endLen); //false//这是为什么呢?//原因:就是因为增加元素后,又重新获取了一次ul中的所有li元素,这一操作就相当于更新了列表元素,故前后的列表长度不相等了。
(在 2018年7月3日 16时54分20秒 这一刻,群里一位成都的网友私聊我,并对该问题的出现提出了质疑,现特地在此对该问题作以补充说明)
质疑的内容:(如下图所示)
注意:他前后总共调用了两次querySelectorAll()方法,即获取了两次。
所以,前后两次获得的列表不一样。
并说道:
随后,我进行了论证与回复:(如下图所示)
注意:我前后总共只调用了1次querySelectorAll()方法,即获取了1次。
所以,前后两次获得的列表是一样的。
*我回复道:
随后,我又使用getElementsByTagName()
方法验证了一遍:(如下图所示)
注意:此处调用了getElementsByTagName()方法进行获取的,且只获取了1次。
为什么前后两次获得的列表长度不一致,原因在后面有讲到。
提示:使用 querySelectorAll()方法 与 getElementsByTagName()方法,获取到的列表内容有什么不一样的地方。(请细心观察,这是重点!!!)
最后的对话:(如下图所示)
主要有以下4点:
我在写代码的过程中,并没有参照作者的代码同步去写,而是用了前几天刚熟悉的,还没有完全掌握的新方法去写的。即 querySelectorAll()
。
对于这个方法,我只知其然,并不知其所以然。为什么呢?因为我连JavaScript
基础教程的书到现在还没看完,底层的实现根本没机会去接触或者了解。只知道有这样一个方法,而且使用简单快捷,需要用的时候拿来用就行了。
我自己写了好几组ul
列表并给了不同的id
,所以会存在很多个li
元素,而querySelectorAll()
方法可以利用CSS
选择器进行精准的定位到所要获取的ul
列表中的li
元素。
getElementsByTagName()
方法来说,它还要关心下标中i
的取值。因为你必须要知道当前要获取的ul
列表中的li
元素,是整个document
文档的第几个li
,数起来比较麻烦。而且我只想给当前的特定的一组li
元素注册事件,不想关心其他的li
元素,于是就放弃了这个方法。其实不用纠结那么多,哪个好用,哪个顺手就用哪个。
先来看看语法:
elementList = document.querySelectorAll(css selectors);
说明:
document.querySelectorAll(selectors)
这个方法获取元素时,我们都知道它是HTML5
新增的DOM
操作的方法。该方法会返回一个元素的集合,而且这个集合可以利用数组的形式来操作,使用起来非常的方便。“伪数组”(或称“类数组”)
,至于原因呢我会在下面告诉大家。getElementsByTagName()
方法很类似,返回的都是一个伪数组,大家可以对比着记忆。注:
其实呢,我还是对querySelectorAll( )
这个方法的本质没有搞明白,只知道它是获取元素的方法之一,并且知道它是通过CSS
选择器的方式来获取元素的。至于其他的,就不清楚了。 querySelectorAll( )
这个方法可以根据用户指定的选择器来返回文档中的所有匹配的元素,所以它返回的是一个元素的列表,相当于一个元素的集合,我们可以称之为“类数组”
,或者“伪数组”
。
问:为什么这么说呢?
答:就是因为它可以利用数组的某些特性,但是并不具备数组所拥有的方法。
类数组的2点
特性:
length
属性;下标形式
进行一些操作;语法:
document.getElementsByTagName(tagname)
说明:
它也是获取元素的一种方法,利用标签名来获取元素,返回的同样是一组元素,我们称之为“类数组”(即元素的集合)
。即它既可以使用数组的length
属性,也可以使用数组的下标形式进行操作。
该方法前面除了可以接document
外,还可以接其他DOM
元素,也可以操作用JavaScript
动态创建的DOM
元素。
getElementById()
方法前面只能接document
,就不能操作用JavaScript
动态创建的DOM元素。getElementsByTagName()
方法返回元素的顺序是它们在文档中的顺序。 "*"
传给 getElementsByTagName()
方法,它将返回文档中所有元素的列表。注:
静态页面
HTML代码
我是div5我是ul3
- 我是li1
- 我是li2
- 我是li3
CSS代码(可直接跳过,纯粹是为了更直观的看到各元素所占的区域,方便观察事件委托的相关事件操作)
JavaScript代码(这才是核心)
//通过id获取添加按钮元素var oBtnAdd = document.getElementById("btnAdd");//通过id获取删除按钮元素var oBtnRemove = document.getElementById("btnRemove");//通过id获取ul元素var oUl3 = document.getElementById("list3");//通过css选择器获取ul中的所有li元素,返回一个类数组var aLi2 = document.querySelectorAll("#list3 li");//等添加元素后,为了给li元素增加文本内容做准备var str = "我是li";var num = 3;//for循环给每一个li元素注册鼠标移入移出事件function mHover(){ for (var i = 0; i < aLi2.length; i++) { aLi2[i].onmouseover = function(){ this.style.backgroundColor = "yellow"; }; aLi2[i].onmouseout = function(){ this.style.backgroundColor = "skyblue"; } }}mHover();//给按钮添加点击事件,并实现添加li元素的功能oBtnAdd.onclick = function(){ num++; //创建li元素节点 var nLi = document.createElement("li"); //创建文本节点 var textNode = document.createTextNode("我是li" + num); //将文本节点插入li元素节点中 nLi.appendChild(textNode); //将组合后的li元素,再放入到ul元素内部子元素的末尾 oUl3.appendChild(nLi); mHover();//动态添加li元素后,希望给它们也添加上鼠标移入移出事件,此处执行此函数即可实现}//给按钮添加点击事件,并实现删除li元素的功能oBtnRemove.onclick = function(){ //删除新增加的那个li元素 oUl3.removeChild(oUl3.lastElementChild); num--; mHover();//如果没有这条语句,那么新增的li元素不会拥有鼠标移入移出事件}
作者的代码(只放不同的部分)
var aLi2 = document.getElementsByTagName('li'); //作者的var aLi2 = document.querySelectorAll("#list3 li"); //我的
其实只是获取 li
元素时选用的方法不同而已。那么,各自都会产生什么效果呢?
我实现的效果:使用querySelectorAll()方法
作者实现的效果:使用getElementsByTagName()方法
看到这里,大家大概应该已经知道是什么原因了吧。没错,就是使用querySelectorAll()
方法的缘故。
问:第一次执行mHover()
方法是什么时候?
问:那第二次呢?
答:在添加或者删除按钮的点击事件触发后执行,执行的时候,新增的li
元素已经动态创建完毕。此时,它还会去执行一次mHover()
方法,给新增的li元素同样注册鼠标移入移出事件,没错吧。 我们再回过头来看一下mHover()
这个方法,看看for循环中oLi2.length
的返回值到底是什么?
我们在mHover()
方法内部,for循环结束的下一行,添加如下两条语句:
console.log(aLi2);console.log(aLi2.length);
得到如下的返回值:
querySelectorAll()
是一个静态的获取元素的方法,在初始的DOM
元素被动态改变后,并不能实时的更新其元素列表中的值。NodeList
中的值。它始终如一的坚持着最初的那份想法,不被外界所动摇。不管外界怎么变化,就是不为之动心,这逼装的我给满分!为了搞清楚到底是怎么回事?就去问度娘了,因为我现在看的这本JavaScript
基础教程并不能帮我解决这么高深的问题。不出我所料,还真的有人遇到过同样的问题,并且也做了一番小研究,得出了结论。
于是,就找到了如下这几个对我解决问题有帮助的文章:
首先,我在第一篇文章,看到了这个操作:
1 var test=document.querySelectorAll('.x');2 3 test instanceof NodeList //true4 5 test instanceof HTMLCollection //false67 //这说明,querySelectorAll返回的是NodeList对象
于是,就产生了疑问?
instance of
?不记得是干嘛用的了?看样子是可以判断对象的类型。最初学JavaScript
的时候还学过,一年都没接触了,早都忘得一干二净了。不过,最近学的typeof
立马浮现在我的脑海,这就促使我又去找了它们两个的区别。
区别如下:
相同点:都可以用来判断一个变量是否为空,或者是什么类型的。
不同点: (1)typeof
用于变量的类型判断,它返回一个字符串,这个字符串代表了它所属的类型。 返回值包括五种基本数据类型和一种复杂数据类型。即number、boolean、string、undefined
和null
;以及object
。 (2)instance of
用于判断一个变量是否是某个对象的实例。
查证后得知:原来它们两个都是对象,且都是一个节点的集合。
相同点:它们两个都是集合对象,返回值都是一个类数组。
不同点:
(1)NodeList
是由 Node.childNodes
和 document.querySelectorAll
返回的。它有一个length
属性,
NodeList
对象中包含的节点个数。 注:
Node.childNodes
返回包含指定节点的子节点的集合,该集合为即时更新的集合。
大多数情况下,NodeList
对象都是一个实时变化(即时更新)的集合。
(DOM)
树发生变化,则已经存在的NodeList
对象也会发生相应的变化。它的底层实现相当于一组元素的快照,并不是对文档进行搜索的动态查询。
这样可以避免使用NodeList
对象通常会引起的性能问题。但是,在另外一些情况下,NodeList
是一个静态的集合。
DOM
元素的任何改动都不会影响NodeList
集合内的内容。document.querySelectorAll()
方法返回的就是一个静态的NodeList
。特别是当你选择如何遍历
NodeList
中的所有项,或缓存列表长度的时候,最好牢记这种区分。如需详细了解
NodeList
,请点击这里查看。
(2)HTMLCollection
接口表示一个包含了元素的通用集合。它也有一个length
属性值,返回集合当中子元素的数目。
HTML DOM
中的 HTMLCollection
是即时更新的;当其所包含的文档结构发生改变时,它会自动更新。如需详细了解
HTMLCollection
,请点击这里查看。如需详细了解
HTML DOM
,请点击这里查看
HTMLCollection
做到了随着DOM
元素的改变进行动态的变化,而 NodeList
并不会即时更新元素集合中的值,这就是差异。一个是静态的获取,一个是动态的获取,当然不一样了!感兴趣的同学可以去 查看相关的
Web
文档,这个网站提供了有关开放网络技术(Open Web)
的信息,包括HTML
、CSS
和万维网及HTML5
应用的API
。它还记录了Mozilla
产品的文档。该网站的宗旨是:源于开发者,服务开发者
。
虽然刚开始写博客确实很累,但是我又是处女座的,一向对自己比较严格,而且是极度的完美主义者,当然不允许自己的博客敷衍了事,草草结束,该认真还得认真写。今天就写这么多吧,该睡觉了。
等白天睡起来了,再去学点新的东西,反正不管每天能学多少知识,都得完全弄明白并且掌握了,不要只顾着数量而忽略了质量。基础没巩固好,到时候痛苦的是自己,而且还要花费额外的时间再来复习知识点。