奥门威尼斯网址javascript DOM扩张querySelector()和和querySelectorAll()

DOM元素querySelectorAll可能让你意外的特性表现

2015/11/07 · HTML5 ·
DOM,
querySelectorAll

原文出处:
张鑫旭   

抛砖引玉

在传统的 JavaScript 开发中,查找 DOM
往往是开发人员遇到的第一个头疼的问题,原生的 JavaScript 所提供的 DOM
选择方法并不多,仅仅局限于通过 tag, name, id
等方式来查找,这显然是远远不够的,如果想要进行更为精确的选择不得不使用看起来非常繁琐的正则表达式,或者使用某个库。事实上,现在所有的浏览器厂商都提供了
querySelector 和 querySelectorAll 这两个方法的支持,甚至就连微软也派出了
IE 8 作为支持这一特性的代表,querySelector 和 querySelectorAll 作为查找
DOM 的又一途径,极大地方便了开发者,使用它们,你可以像使用 CSS
选择器一样快速地查找到你需要的节点。

选在符的API的核心有两个方法:querySelector()和querySelectorAll()
querySelector(a):a是一个css选择符,返回与该模式匹配的第一个元素,如果没有匹配的元素,返回null.

一、时间紧急,废话少说

本文所在的页面藏匿了下面这些代码:

<img id=”outside”> <div id=”my-id”> <img id=”inside”>
<div class=”lonely”></div> <div class=”outer”> <div
class=”inner”></div> </div> </div>

1
2
3
4
5
6
7
8
<img id="outside">
<div id="my-id">
    <img id="inside">
    <div class="lonely"></div>
    <div class="outer">
        <div class="inner"></div>
    </div>
</div>

就是下面这样的表现(为了便于观察,我加了边框背景色和文字):

奥门威尼斯网址 1

首先说点大家都知道的热热身。

  • querySelectorquerySelectorAll IE8+浏览器支持。
  • querySelector返回的是单个DOM元素;querySelectorAll返回的是NodeList.
  • 我们一般用的多的是document.querySelectorAll,
    实际上,也支持dom.querySelectorAll.例如:
JavaScript

document.querySelector("\#my-id").querySelectorAll("img")

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f2fbc48034065158916-1">
1
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f2fbc48034065158916-1" class="crayon-line">
document.querySelector(&quot;#my-id&quot;).querySelectorAll(&quot;img&quot;)
</div>
</div></td>
</tr>
</tbody>
</table>

选择的就是里面这个妹子。例如,我在控制台输出该选择NodeList的长度和id,如下截图:
奥门威尼斯网址 2

好了,上面都是众所周知的,好,下面开始展示点有意思的。

大家看下下面2行简单的查询语句:

JavaScript

document.querySelectorAll(“#my-id div div”);

1
document.querySelectorAll("#my-id div div");

JavaScript

document.querySelector(“#my-id”).querySelectorAll(“div div”);

1
document.querySelector("#my-id").querySelectorAll("div div");

奥门威尼斯网址 3

提问:上面两个语句返回的NodeList的内容是否是一样的?

给大家1分钟的时间思考下。

//zxx: 假设1分钟已经过去了

好了,答案是:不一样的。估计不少人跟我一样,会认为是一样的。

实际上:

JavaScript

document.querySelectorAll(“#my-id div div”).length === 1;

1
document.querySelectorAll("#my-id div div").length === 1;

JavaScript

document.querySelector(“#my-id”).querySelectorAll(“div div”).length ===
3;

1
document.querySelector("#my-id").querySelectorAll("div div").length === 3;

大家如果有疑问,可以在控制台测试下,下图就是我自己测试的结果:

奥门威尼斯网址 4

为啥会这样?

第一个符合我们的理解,不解释。那下一个语句,为何返回的NodeList长度是3呢?

首先,遍历该NodeList会发现,查询的三个dom元素为:div.lonelydiv.outerdiv.inner.

奇怪,奇怪,怎么会是3个呢?

jQuery中有个find()方法,大家很可能受到这个方法影响,导致出现了一些认知的问题:

JavaScript

$(“#my-id”).find(“div div”).length === 1;

1
$("#my-id").find("div div").length === 1;

如果使用find方法,则是1个匹配;由于结构和作用类似,我们很自然疑问原生的querySelectorAll也是这个套路。真是太错特错!!

要解释,为何NodeList长度是3,只要一句话就可以了,我特意加粗标红:

CSS选择器是独立于整个页面的!

什么意思呢?比如说你在页面很深的一个DOM里面写上:

<style> div div { } </style>

1
2
3
<style>
div div { }
</style>

 

整个网页,包括父级,只要是满足div div父子关系的元素,全部会被选中,对吧,这个大家应该都清楚的。

这里的querySelectorAll里面的选择器也同样是这也全局特性。document.querySelector("#my-id").querySelectorAll("div div")翻译成白话文就是:查询#my-id的子元素,同时满足整个页面下div div选择器条件的DOM元素们。

我们页面往上滚动看看原始的HTML结构,会发现,在全局视野下,div.lonelydiv.outerdiv.inner全部都满足div div这个选择器条件,于是,最终返回的长度为3.

  很多前端类库(比如dojo与JQuery)在涉及dom操作时都会见到两个模块:attr、prop。某天代码复查时,见到一段为某节点设置文本的代码:

  querySelector
1  var result = {};
2             result = document.querySelector("body"); //[object HTMLBodyElement]
3             result = document.querySelector("#guoDiv").innerHTML; //div里面的内容

二、:scope与区域选择限制

其实,要想querySelectorAll后面选择器不受全局影响,也是有办法的,就是使用目前还处于实验阶段的:scope伪类,其作用就是让CSS是在某一范围内使用。此伪类在CSS中使用是大头,但是也可以在querySelectorAll语句中使用:

JavaScript

document.querySelector(“#my-id”).querySelectorAll(“:scope div div”);

1
document.querySelector("#my-id").querySelectorAll(":scope div div");

兼容性如下:

奥门威尼斯网址 5

我写此文时候是15年11月初,目前基本上就FireFox浏览器支持,我估计,以后,会支持越来越多的。为什么呢?

因为Web
Components需要它,可以实现真正独立封装,不会受外界影响的HTML组件。

关于:scope目前支持尚浅,时机未到,我就没必要乱展开了,点到为止。

attr.set(node, 'innerText', 'Hello World!')

querySelector 和 querySelectorAll
的使用非常的简单,就像标题说到的一样,它和 CSS
的写法完全一样,对于前端开发人员来说,这是难度几乎为零的一次学习。假如我们有一个
id 为 test 的 DIV,为了获取到这个元素,你也许会像下面这样:

css选择符可以简单也可以复杂.如果传入不被支持的选择符,querySelector();会抛出错误.
querySelectorAll(a);
a也是一个css选择符,但是返回的是所有匹配元素而不仅仅是一个元素.返回的是一个NodeList的实例

三、结语还是要的

参考文章:querySelectorAll from an element probably doesn’t do what you
think it
does

感谢阅读,欢迎纠错,欢迎交流!

1 赞 1 收藏
评论

奥门威尼斯网址 6

  这段代码执行后并未生效,虽说innerText不是标准属性,尚未被ff支持,可用的是chrome,这个属性是被支持的。既然显示的文本没变,那就查看一下元素吧。

  document.getElementById("test");
1 result =  document.querySelectorAll(".guoDiv");
2             alert(result.length);  //4 

奥门威尼斯网址 7

现在我们来试试使用新方法来获取这个 DIV:

 

  innerText被添加到了html标签上,而换成prop模块后,成功的为节点替换文本。

  document.querySelector("#test");
  document.querySelectorAll("#test")[0];

  以上的这个小案例就涉及到了DOM操作时常常被忽略的一个问题:特性与属性的区别

获取文档中 class=”example” 的第一个

 

元素:

 

  document.querySelector("p.example");

返本求源

获取文档中有 “target” 属性的第一个 元素:

    在DOM中,特性指的是html标签上的属性,比如:

  document.querySelector("a[target]");

  奥门威尼斯网址 8

使用这两个方法无法查找带伪类状态的元素,比如querySelector(‘:hover’)不会得到预期结果。

  Property是对于某一类型特征的描述。可以这样理解,在DOM元素中可以通过点语法访问,又不是标准特性的都可以成为属性。

  querySelectorAll

  DOM中所有的节点都实现了Node接口。Node接口是在DOM1级中定义的,其中定义了一些用来描述DOM节点的属性和操作方法。

该方法返回所有满足条件的元素,结果是个nodeList集合。查找规则与前面所述一样。

  奥门威尼斯网址 9

elements =
document.querySelectorAll(‘div.foo’);//返回所有带foo类样式的div

  常见的nodeType、nodeValue、节点关系(parentNode、childNodes、firstChild、lastChild、previousSibling、nextSibling等)都属于Node接口定义的属性。对于Node接口的具体实现者,HTMLElement不仅继承了这些属性,还拥有五个wac规范中的五个标准特性:id、title、lang、dir、class和一个属性:attributes。

但需要注意的是返回的nodeList集合中的元素是非实时(no-live)的,想要区别什么是实时非实时的返回结果,请看下例:

   每一个元素都有一个或多个特性,这些特性的用途是给出相应元素或其内容的附加信息。通过DOM元素直接操作特性的的方法有三个:

  <div id="container">
      <div></div>
      <div></div>
  </div>
  • getAttribute(attrName)
  • setAttribute(attrName, value)
  • removeAttribute(name)

//首先选取页面中id为container的元素

  这三个方法都可以操作自定义特性。但是只有公认的(非自定义)特性才会以属性的形式添加到DOM对象中,以属性方式操作这些特性会被同步到html标签中。HTMLElement的五个特性都有相应属性与其对待:id、title、lang、dir、className。在DOM中以属性方式操作这几个特性会同步到html标签中。

container=document.getElementById(‘#container’);
console.log(container.childNodes.length)//结果为2

  不过,HTML5规范对自定义特性做了增强,只要自定义特性以”data-attrName”的形式写入到html标签中,在DOM属性中就可以通过element.dataset.attrName的形式来访问自定义特性,如:

//然后通过代码为其添加一个子元素

<input type=​"text" name=​"as_q" class=​"box" id=​"searched_content" title=​"在此输入搜索内容。" disabled=​"false" data-ff=​"fsdf">​
seh.dataset.ff
  container.appendChild(document.createElement('div'));

  

//这个元素不但添加到页面了,这里的变量container也自动更新了

  元素的特性在DOM中以Attr类型来表示,Attr类型也实现了Node接口。Attr对象有三个属性:name、value、specified。其中,name是特性的名称,value是特性值,specified是一个布尔值,用来指示该特性是否被明确设置。

console.log(container.childNodes.length)//结果为3

  document.createAttribute方法可以用来创建特性节点。例如,要为元素添加align特性可以使用如下方法:

通过上面的例子就很好地理解了什么是会实时更新的元素。document.getElementById返回的便是实时结果,上面对其添加一个子元素后,再次获取所有子元素个数,已经由原来的2个更新为3个(这里不考虑有些浏览器比如Chrome会把空白也解析为一个子节点)。

ar attr = document.createAttribute('align')
attr.value = 'left'
seh.setAttributeNode(attr)

感觉区别不大是吧,但如果是稍微复杂点的情况,原始的方法将变得非常麻烦,这时候
querySelector 和 querySelectorAll
的优势就发挥出来了。比如接下来这个例子,我们将在 document 中选取 class
为 test 的 div 的子元素 p
的第一个子元素,当然这很拗口,但是用本文的新方法来选择这个元素,比用言语来描述它还要简单。

  要将新创建的特性添加到元素上,必须使用元素的setAttributeNode方法。添加特性后,特性会反映在html标签上:

  document.querySelector("div.test>p:first-child");
  document.querySelectorAll("div.test>p:first-child")[0];

奥门威尼斯网址 10

我们使用 querySelectorAll 给所有 class 为 emphasis 的元素加粗显示。

  注意,尽管特性节点也实现了Node接口,但特性却不被认为是DOM文档树的一部分。

  var emphasisText = document.querySelectorAll(".emphasis");
  for( var i = 0 , j = emphasisText.length ; i < j ; i++ ){
      emphasisText[i].style.fontWeight = "bold";
  }

 

这是原生方法,比起jquery速度快

  在所有的DOM节点中attributes属性是Element类型所独有的的属性。从技术角度来说,特性就是存在于元素的attributes属性中的节点。attributes属性属于NamedNodeMap类型的实例。元素的每一个特性节点都保存在NamedNodeMap对象中。NamedNodeMap类型拥有如下方法:

注意: querySelector()
方法仅仅返回匹配指定选择器的第一个元素。如果你需要返回所有的元素,请使用
querySelectorAll() 方法替代。

  • getNamedItem(name):返回特性名为name的特性节点
  • removeNamedItem(name):删除特性名为name的特性节点
  • setNamedItem(attr):像元素中添加一个特性节点
  • item(pos):返回位于数组pos处的节点

  获取、设置、删除元素节点可以如下方式:

element.attributes.getNamedItem('align') //获取

var attr = document.createAttribute('align');
attr.value = 'right';
element.attributes.setNamedItem(attr); //添加

element.attributes.removeNamedItem('align'); //删除

  实际应用中并不建议使用特性节点的方式,而getAttribute、setAttribute、removeAttribute方法远比操作特性节点更方便。

  DOM、attributes、Attr三者关系应该这么画:

奥门威尼斯网址 11

 

 

应用总结

  基于以上DOM基础知识和实际工作经验,我将特性和属性的区别联系总结如下:

  1. 属性以及公认特性可以通过点语法访问;html5规范中,data-*形式的自定义特性可以通过element.dataset.*的形式来访问,否则用getAttribute
  2. 特性值只能是字符串,而属性值可以是任意JavaScript支持的类型
  3. 几个特殊特性:

    1. style,通过getAttrbute和setAttribute来操作这个特性只能得到或设置字符串;而已属性方式来操作就是在操作CSSStyleDeclaration对象
    2. 事件处理程序,通过特性方式得到和传递的都只是函数字符串;而已属性方式操作的是函数对象
    3. value,对于支持value的元素,最好通过属性方式操作,而且操作不会反映在html标签上

      seh.value = 10
      <input type="text" name="as_q" class="box" id="searched_content" title="在此输入搜索内容。" disabled="false" data-ff="fsdf" align="left">
      
    4. href,通过属性方式设置可以反映到html标签上,但用过点语法和getAttribute能够取到的值并不一定相同

      <a href="/jsref/prop_checkbox_tabindex.asp" id="tabI">tabIndex</a>
      
      link.getAttribute('href') // "/jsref/prop_checkbox_tabindex.asp"
      
      link.href // "http://www.w3school.com.cn/jsref/prop_checkbox_tabindex.asp"
      
    5. disabled和checked,对于支持这两个特性的元素来说,他们在html标签中都是无状态的,只要有独立的标签属性在以点语法访问时就返回true,如果html标签属性不存在,则以点语法访问时就是false

    6. seh.disabled // true
      
        seh.disabled = false
        <input type=​"text" name=​"as_q" class=​"box" id=​"searched_content" title=​"在此输入搜索内容。" data-ff=​"fsdf" align=​"left">​


        如果您觉得这篇文章对您有帮助,请不吝点击右下方“推荐”,谢谢~

发表评论

电子邮件地址不会被公开。 必填项已用*标注