原生的强大DOM选择器querySelector介绍


Posted in Javascript onDecember 21, 2016

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

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

document.getElementById("test");
现在我们来试试使用新方法来获取这个 DIV:

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

下面是个小演示:

我是 id 为 test 的 div

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

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

下面是个小演示:

我是层里的 p 标签

现在应该对于 querySelector、querySelectorAll 方法中的参数已经非常明白了,是的,它接收的参数和 CSS 选择器完全一致。querySelector 和 querySelectorAll 的区别在于 querySelector 用来获取一个元素,而querySelectorAll 可以获取多个元素。querySelector 将返回匹配到的第一个元素,如果没有匹配的元素则返回 Null。querySelectorAll 返回一个包含匹配到的元素的数组,如果没有匹配的元素则返回的数组为空。在本文最后一个例子中,我们使用 querySelectorAll 给所有 class 为 emphasis 的元素加粗显示。

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

这是原生方法,比起jquery速度快,缺点是IE6、7不支持。

W3C的规范与库中的实现

querySelector:return the first matching Element node within the node's subtrees. If there is no such node, the method must return null .(返回指定元素节点的子树中匹配selector的集合中的第一个,如果没有匹配,返回null)

querySelectorAll:return a NodeList containing all of the matching Element nodes within the node's subtrees, in document order. If there are no such nodes, the method must return an empty NodeList. (返回指定元素节点的子树中匹配selector的节点集合,采用的是深度优先预查找;如果没有匹配的,这个方法返回空集合)

这在BaseElement 为document的时候,没有什么问题,各浏览器的实现基本一致;但是,当BaseElement 为一个普通的dom Node的时候(支持这两个方法的dom Node ),浏览器的实现就有点奇怪了,举个例子:

<div class= "test"  id= "testId" > 
   <p><span>Test</span></p> 
</div> 
<script type= "text/javascript" >   
   var  testElement= document.getElementById( 'testId' ); 
   var  element = testElement.querySelector( '.test span' ); 
   var  elementList = document.querySelectorAll( '.test span' ); 
   console.log(element); // <span>Test</span>
   console.log(elementList); // 1  
</script>

按照W3C的来理解,这个例子应该返回:element:null;elementList:[];因为作为baseElement的 testElement里面根本没有符合selectors的匹配子节点;但浏览器却好像无视了baseElement,只在乎selectors,也就是说此时baseElement近乎document;这和我们的预期结果不合,也许随着浏览器的不断升级,这个问题会得到统一口径!

人的智慧总是无穷的,Andrew Dupont发明了一种方法暂时修正了这个怪问题,就是在selectors前面指定baseElement的id,限制匹配的范围;这个方法被广泛的应用在各大流行框架中;

Jquery的实现:

var  oldContext = context,
old = context.getAttribute( "id"  ),
nid = old || id,
try  {
	if  ( !relativeHierarchySelector || hasParent ) {
   	return  makeArray( context.querySelectorAll( "[id='"  + nid + "'] "  + query ), extra );
 	}  
} 
catch (pseudoError) {} 
finally {
	if  ( !old ) {
	 	oldContext.removeAttribute( "id"  );
	}
}

先不看这点代码中其他的地方,只看他如何实现这个方法的;这点代码是JQuery1.6的片段;当baseElement没有ID的时候,给他设置一个id = "__sizzle__”,然后再使用的时候加在selectors的前面,做到范围限制;context.querySelectorAll( "[id='" + nid + "'] " + query ;最后,因为这个ID本身不是baseElement应该有的,所以,还需要移除:oldContext.removeAttribute( "id" ); ,Mootools的实现:

var  currentId = _context.getAttribute( 'id' ), slickid = 'slickid__' ;
_context.setAttribute( 'id' , slickid);
_expression = '#'  + slickid + ' '  + _expression;
context = _context.parentNode;

Mootools和Jquery类似:只不过slickid = 'slickid__';其实意义是一样的;方法兼容性:FF3.5+/IE8+/Chrome 1+/opera 10+/Safari 3.2+;IE 8 :不支持baseElement为object;

Javascript 相关文章推荐
cssQuery()的下载与使用方法
Jan 12 Javascript
ExtJS 2.0实用简明教程 之Border区域布局
Apr 29 Javascript
IE6/7 and IE8/9/10(IE7模式)依次隐藏具有absolute或relative的父元素和子元素后再显示父元素
Jul 31 Javascript
jQuery实现复选框成对选择及对应取消的方法
Mar 03 Javascript
jQuery实现转动随机数抽奖效果的方法
May 21 Javascript
详解vue项目构建与实战
Jun 27 Javascript
jQuery实现的鼠标滚轮控制图片缩放功能实例
Oct 14 jQuery
Bootstrap Fileinput 4.4.7文件上传实例详解
Jul 25 Javascript
浅谈VUE单页应用首屏加载速度优化方案
Aug 28 Javascript
vue 解决循环引用组件报错的问题
Sep 06 Javascript
基于mpvue的简单弹窗组件mptoast使用详解
Aug 02 Javascript
Vue CLI4 Vue.config.js标准配置(最全注释)
Jun 05 Javascript
懒加载实现的分页&amp;&amp;网站footer自适应
Dec 21 #Javascript
JS树形菜单组件Bootstrap TreeView使用方法详解
Dec 21 #Javascript
Vue.js 递归组件实现树形菜单(实例分享)
Dec 21 #Javascript
详解jQuery选择器
Dec 21 #Javascript
如何清除IE10+ input X 文本框的叉叉和密码输入框的眼睛图标
Dec 21 #Javascript
js实现可输入可选择的select下拉框
Dec 21 #Javascript
详解handlebars+require基本使用方法
Dec 21 #Javascript
You might like
php出现Cannot modify header information问题的解决方法大全
2008/04/09 PHP
PHP使用数组实现队列
2012/02/05 PHP
PHP file_exists问题杂谈
2012/05/07 PHP
克隆一个新项目的快捷方式
2013/04/10 PHP
浅谈PHP的反射机制
2016/12/15 PHP
thinkPHP中钩子的使用方法实例分析
2017/11/16 PHP
PHP中in_array的隐式转换的解决方法
2018/03/06 PHP
laravel withCount 统计关联数量的方法
2019/10/10 PHP
jsTree树控件(基于jQuery, 超强悍)[推荐]
2009/09/01 Javascript
cookie在javascript中的使用技巧以及隐私在服务器端的设置
2012/12/03 Javascript
使用ngView配合AngularJS应用实现动画效果的方法
2015/06/19 Javascript
Jquery数字上下滚动动态切换插件
2015/08/08 Javascript
XML、HTML、CSS与JS的区别整理
2016/02/18 Javascript
JQuery 进入页面默认给已赋值的复选框打钩
2017/03/23 jQuery
在vue中通过axios异步使用echarts的方法
2018/01/13 Javascript
使用webpack打包koa2 框架app
2018/02/02 Javascript
微信小程序中的列表切换功能实例代码详解
2020/06/09 Javascript
[03:19]2016国际邀请赛中国区预选赛第四日TOP10镜头集锦
2016/07/01 DOTA
[01:08:48]LGD vs OG 2018国际邀请赛淘汰赛BO3 第三场 8.25
2018/08/29 DOTA
[38:23]完美世界DOTA2联赛循环赛 FTD vs PXG BO2第二场 11.01
2020/11/02 DOTA
用Python遍历C盘dll文件的方法
2015/05/06 Python
Python键盘输入转换为列表的实例
2018/06/23 Python
PyQt5 多窗口连接实例
2019/06/19 Python
Python如何实现强制数据类型转换
2019/11/22 Python
Python多进程编程multiprocessing代码实例
2020/03/12 Python
Django choices下拉列表绑定实例
2020/03/13 Python
PYcharm 激活方法(推荐)
2020/03/23 Python
canvas绘制视频封面的方法
2018/02/05 HTML / CSS
html5+css如何实现中间大两头小的轮播效果
2018/12/06 HTML / CSS
科颜氏法国官网:Kiehl’s法国
2019/08/20 全球购物
Club Monaco加拿大官网:设计师男女服装
2019/09/29 全球购物
法国在线药房:Shop Pharmacie
2019/11/26 全球购物
数据库笔试题
2013/05/09 面试题
有针对性的求职自荐信
2013/11/14 职场文书
大学生实习鉴定评语
2014/04/25 职场文书
vue中 this.$set的使用详解
2021/11/17 Vue.js