添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

以下内容均为原创,如需转载请注明出处。

  • 最近这两天一直在学习 JavaScript 的事件委托(或称事件代理),用了两天的时间看完了这篇 《js中的事件委托或是事件代理详解》 博客,并且将博主提供的案例手动敲了两边,可以说是基本掌握了事件委托的原理和用法。
  • 这篇博客对于 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点:

  1. 我在写代码的过程中,并没有参照作者的代码同步去写,而是用了前几天刚熟悉的,还没有完全掌握的新方法去写的。即 querySelectorAll()

  2. 对于这个方法,我只知其然,并不知其所以然。为什么呢?因为我连 JavaScript 基础教程的书到现在还没看完,底层的实现根本没机会去接触或者了解。只知道有这样一个方法,而且使用简单快捷,需要用的时候拿来用就行了。

  3. 我自己写了好几组 ul 列表并给了不同的 id ,所以会存在很多个 li 元素,而 querySelectorAll() 方法可以利用 CSS 选择器进行精准的定位到所要获取的 ul 列表中的 li 元素。

  4. 但是呢,对 getElementsByTagName() 方法来说,它还要关心下标中 i 的取值。因为你必须要知道当前要获取的 ul 列表中的 li 元素,是整个 document 文档的第几个 li ,数起来比较麻烦。而且我只想给当前的特定的一组 li 元素注册事件,不想关心其他的 li 元素,于是就放弃了这个方法。

其实不用纠结那么多,哪个好用,哪个顺手就用哪个。

1、querySelectorAll()方法介绍

先来看看语法:

elementList = document.querySelectorAll(css selectors);
  • 在利用 document.querySelectorAll(selectors) 这个方法获取元素时,我们都知道它是 HTML5 新增的 DOM 操作的方法。该方法会返回一个元素的集合,而且这个集合可以利用数组的形式来操作,使用起来非常的方便。
  • 但实际上得到集合并不是一个真正意义上的数组,只能说它是一个 “伪数组”(或称“类数组”) ,至于原因呢我会在下面告诉大家。
  • 这个方法其实和 getElementsByTagName() 方法很类似,返回的都是一个伪数组,大家可以对比着记忆。

注:
其实呢,我还是对 querySelectorAll( ) 这个方法的本质没有搞明白,只知道它是获取元素的方法之一,并且知道它是通过 CSS 选择器的方式来获取元素的。至于其他的,就不清楚了。

疑问:何为类数组或者伪数组?

querySelectorAll( ) 这个方法可以根据用户指定的选择器来返回文档中的所有匹配的元素,所以它返回的是一个元素的列表,相当于一个元素的集合,我们可以称之为 “类数组” ,或者 “伪数组”

问:为什么这么说呢?
答:就是因为 它可以利用数组的某些特性,但是并不具备数组所拥有的方法。

类数组的 2点 特性:

  • 可以使用数组的 length 属性;
  • 可以利用数组的 下标形式 进行一些操作;

2、getElementsByTagName()方法介绍

document.getElementsByTagName(tagname)
  • 它也是获取元素的一种方法,利用标签名来获取元素,返回的同样是一组元素,我们称之为 “类数组”(即元素的集合) 。即它既可以使用数组的 length 属性,也可以使用数组的下标形式进行操作。

  • 该方法前面除了可以接 document 外,还可以接其他 DOM 元素,也可以操作用 JavaScript 动态创建的 DOM 元素。
    但是, getElementById() 方法前面只能接 document ,就不能操作用 JavaScript 动态创建的DOM元素。

  • getElementsByTagName() 方法返回元素的顺序是它们在文档中的顺序。
  • 如果把 "*" 传给 getElementsByTagName() 方法,它将返回文档中所有元素的列表。
  • 这个方法平时在代码中很常见,遇见的几率比较大,也比较大众化,基本人人都会使用这个方法。
  • 在学DOM操作的时候,这个方法是重点内容必须得掌握。

二、情景还原

我觉得图文并茂才是解决问题的最好办法,那么多文字谁愿意去看呀,要是我,我肯定也不乐意。下面就让我们开始吧。

HTML代码

<div id="box5">我是div5
    <button id="btnAdd">添加元素</button>
    <button id="btnRemove">删除元素</button>
    <ul id="list3">我是ul3
        <li>我是li1</li>
        <li>我是li2</li>
        <li>我是li3</li>
</div>

CSS代码(可直接跳过,纯粹是为了更直观的看到各元素所占的区域,方便观察事件委托的相关事件操作)

<style>
    html,body{
        margin: 0;
        padding: 0;
    div:not(:nth-of-type(2)){
        margin:0;
        width:500px;
        height:100%;
        background-color: #f40;
        margin-top: 50px;
    /*ul默认有外边距*/
        width: 300px;
        height: 100%;
        background-color: #BDBDBD;
        list-style: none;
        /*清除默认边距*/
        margin:0;
        padding:0;
        margin:20px auto;
        margin-bottom:30px; 
        width: 200px;
        height: 30px;
        line-height: 30px;
        margin: 20px auto;
        background-color: pink;
</style>

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元素注册完鼠标移入移出事件之后。

问:那第二次呢?
答:在添加或者删除按钮的点击事件触发后执行,执行的时候,新增的li元素已经动态创建完毕。此时,它还会去执行一次mHover()方法,给新增的li元素同样注册鼠标移入移出事件,没错吧。

我们再回过头来看一下mHover()这个方法,看看for循环中oLi2.length的返回值到底是什么?

我们在mHover()方法内部,for循环结束的下一行,添加如下两条语句:

console.log(aLi2);
console.log(aLi2.length);

得到如下的返回值:

  • 显而易见,querySelectorAll()是一个静态的获取元素的方法,在初始的DOM元素被动态改变后,并不能实时的更新其元素列表中的值。
  • 这说明,文档结构的改变,并没有影响到NodeList中的值。它始终如一的坚持着最初的那份想法,不被外界所动摇。不管外界怎么变化,就是不为之动心,这逼装的我给满分!

四、寻根问底

为了搞清楚到底是怎么回事?就去问度娘了,因为我现在看的这本JavaScript基础教程并不能帮我解决这么高深的问题。不出我所料,还真的有人遇到过同样的问题,并且也做了一番小研究,得出了结论。

于是,就找到了如下这几个对我解决问题有帮助的文章:

  1. 关于querySelectorAll的一个坑
  2. DOM元素querySelectorAll可能让你意外的特性表现
  3. 说说HTMLCollection、NodeList以及NamedNodeMap
  4. JS中typeof与instanceof的区别

首先,我在第一篇文章,看到了这个操作:

1 var test=document.querySelectorAll('.x');
3 test instanceof NodeList //true
5 test instanceof HTMLCollection //false
7 //这说明,querySelectorAll返回的是NodeList对象

于是,就产生了疑问?

疑问一:instanceof 是谁?干嘛用的?

instance of?不记得是干嘛用的了?看样子是可以判断对象的类型。最初学JavaScript的时候还学过,一年都没接触了,早都忘得一干二净了。不过,最近学的typeof立马浮现在我的脑海,这就促使我又去找了它们两个的区别。

区别如下:

相同点:都可以用来判断一个变量是否为空,或者是什么类型的。
不同点:
(1)typeof 用于变量的类型判断,它返回一个字符串,这个字符串代表了它所属的类型。
返回值包括五种基本数据类型和一种复杂数据类型。即number、boolean、string、undefinednull;以及object
(2)instance of 用于判断一个变量是否是某个对象的实例。

疑问二:NodeList 是谁?HTMLCollection 又是谁?

查证后得知:原来它们两个都是对象,且都是一个节点的集合。

相同点:它们两个都是集合对象,返回值都是一个类数组。

(1)NodeList 是由 Node.childNodesdocument.querySelectorAll 返回的。它有一个length属性,
返回 NodeList对象中包含的节点个数。

注:Node.childNodes 返回包含指定节点的子节点的集合,该集合为即时更新的集合。

  • 大多数情况下,NodeList对象都是一个实时变化(即时更新)的集合。
    什么意思呢?就是说,每当文档中的节点(DOM)树发生变化,则已经存在的NodeList对象也会发生相应的变化。

  • 它的底层实现相当于一组元素的快照,并不是对文档进行搜索的动态查询。
    这样可以避免使用NodeList对象通常会引起的性能问题。

  • 但是,在另外一些情况下,NodeList是一个静态的集合。
    这也就意味着随后对DOM元素的任何改动都不会影响NodeList 集合内的内容。

  • document.querySelectorAll()方法返回的就是一个静态的NodeList

特别是当你选择如何遍历NodeList中的所有项,或缓存列表长度的时候,最好牢记这种区分。

如需详细了解NodeList,请点击这里查看Web/API/NodeList

(2)HTMLCollection接口表示一个包含了元素的通用集合。它也有一个length属性值,返回集合当中子元素的数目。

  • HTML DOM中的 HTMLCollection 是即时更新的;当其所包含的文档结构发生改变时,它会自动更新。

如需详细了解HTMLCollection,请点击这里查看Web/API/HTMLCollection

如需详细了解HTML DOM,请点击这里查看《getElementById返回的是什么?串讲HTML DOM》

五、总结:

HTMLCollection 做到了随着DOM元素的改变进行动态的变化,而 NodeList 并不会即时更新元素集合中的值,这就是差异。一个是静态的获取,一个是动态的获取,当然不一样了!

最后我自己也在控制台做了判断,如下图所示:

使用querySelectorAll()方法:

使用getElementsByTagName()方法:

感兴趣的同学可以去 Mozilla 开发者网络 查看相关的Web文档,这个网站提供了有关开放网络技术(Open Web)的信息,包括HTMLCSS 和万维网及 HTML5 应用的 API。它还记录了 Mozilla 产品的文档。该网站的宗旨是:源于开发者,服务开发者

六、结束语

  • 今天晚上有点事耽误了,没能早早把博客写完,写到这么晚也实属无奈。突然发现现在已经是凌晨05:36了,从开始到结束,不知不觉已经过去了3个小时。毕竟今天遗留的问题,最好当天就解决了,千万别拖到明天去执行,后果你懂得。人是越拖延越不想做,越不想做就会变得越懒,每天利用写博客的方式还能时刻提醒自己,激励自己,让自己坚持下来。
  • 虽然刚开始写博客确实很累,但是我又是处女座的,一向对自己比较严格,而且是极度的完美主义者,当然不允许自己的博客敷衍了事,草草结束,该认真还得认真写。今天就写这么多吧,该睡觉了。

  • 等白天睡起来了,再去学点新的东西,反正不管每天能学多少知识,都得完全弄明白并且掌握了,不要只顾着数量而忽略了质量。基础没巩固好,到时候痛苦的是自己,而且还要花费额外的时间再来复习知识点。

  • 所以说,应该严格要求自己,合理分配时间,不要眼高手低,初学阶段就应该多观察多思考多实践,而非走马观花式的,左顾右盼式的学习。尽量做到快速高效的学习,形成自己的一套科学合理的学习方法,建立一套彰显自己风格的有特色的知识体系,这何尝不是一件好事呢?
Write By Monkeyfly以下内容均为原创,如需转载请注明出处。一、前提 最近这两天一直在学习JavaScript的事件委托(或称事件代理),用了两天的时间看完了这篇《js中的事件委托或是事件代理详解》博客,并且将博主提供的案例手动敲了两边,可以说是基本掌握了事件委托的原理和用法。这篇博客对于JavaScript的事件委托的讲解还是比较通俗易懂,详细到位的,在这里向大
一、getElementsByClassName()方法不足之处 在之前的学习中,我们了解到,可以根据元素的 class 属性值查询一组元素节点对象,即:getElementsByClassName(),但是这个方法不支持 IE8 及以下的浏览器。 在我们的实际开发中,会特别的受限,为了兼容 IE8 及以下的浏览器,我们可以采用一种新的方法,这种方法在我们的实际开发中也很常用,因此我们要熟练掌握...
对于在浏览器上运行JS程序,最核心的库就是DOM API 库DOM可以看作文档对象模型,HTML中会把每个html标签看作成一个JS中可操作的对象。操作这个对象,就可以影响界面的显示。其中获取网页元素是DOM的基础。当一个网页加载好后,会自动生成一个全局变量,叫做document,这其中有些方法和属性,让我们来操作网页内容。 在JS中的很多代码都是通过事件来触发。eg:鼠标点击页面,会产生鼠标点击事件等等。事件的三要素: 获取操作元素的内容 通过对象内的属性 innerHTML来实现。 需要注意,input
它们俩是有区别的: 1.querySelector返回满足条件的第一个元素(node),而querySelectorAll返回满足条件的所有元素(nodelist); 2.在都没有满足条件的元素情况下:querySelector返回null,而querySelectorAll返回空的数组[]。 1.querySelector 返回指定元素节点的子树中匹配selector的集合中的第一个,如果没有匹...
querySelectorAll与querySelector的区别: querySelectorAll : 找出所有匹配的节点并返回数组. querySelector : 找到一个后就返回节点对象。 1、querySelector只返回匹配的第一个元素,如果没有匹配项,返回null。 2、querySelectorAll返回匹配的元素集合,如果没有匹配项,返回空的nodelist(节点数组)。 3、返回的结果是静态的,之后对document结构的改变不会影响到之前取到的结果 找出body标签下的第一..
document.querySelectorAll与常规获取的区别一、document.querySelectorAll获取节点方法二、常规获取DOM节点方法2.分析区别总结 <!--body里面的代码--> <li>洪山区</li> <li id="wuchang">武昌区</li> <li>汉阳区</li>
querySelectorAllJavaScript 中的一个方法,它用于获取文档中符合指定 CSS 选择器的所有元素,并返回一个 NodeList 对象。 querySelectorAll 方法的语法如下: elementList = parentNode.querySelectorAll(selectors); 其中,parentNode 表示要查询的父元素节点,selectors 表示一个或多个 CSS 选择器,多个选择器之间用逗号分隔。 querySelectorAll 方法返回的是一个 NodeList 对象,它类似于数组,可以使用索引值访问其中的元素,也可以使用 forEach 等方法遍历其中的元素。 例如,要获取文档中所有 class 为 "myClass" 的元素,可以使用以下代码: var elements = document.querySelectorAll(".myClass"); 这样就可以获取到所有符合条件的元素,存储在 elements 变量中。
nginx启动失败之nginx: [emerg] invalid number of arguments in "root" directive in nginx/nginx.conf:41 106491 nginx启动失败之nginx: [emerg] invalid number of arguments in "root" directive in nginx/nginx.conf:41 Zhang_9368: 我在c盘用户名有空格 md 百度地图JavaScript API 学习之浏览器定位 wwtx2048: 我也遇到了是不是谷歌根据谷歌地图定位的,谷内无法访问谷歌地图导致定位不准确