原生的强大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 相关文章推荐
可以把编码转换成 gb2312编码lib.UTF8toGB2312.js
Aug 21 Javascript
关于jQuery中的end()使用方法
Jul 10 Javascript
Jquery下EasyUI组件中的DataGrid结果集清空方法
Jan 06 Javascript
iframe父页面获取子页面参数的方法
Feb 21 Javascript
js实现获取两个日期之间所有日期的方法
Jun 17 Javascript
文本框只能输入数字的实现方法(兼容IE火狐)
Jun 25 Javascript
AngularJS使用ng-repeat指令实现下拉框
Aug 23 Javascript
JavaScript两个变量交换值的实现方法
Mar 01 Javascript
Vue中img的src是动态渲染时不显示的解决
Nov 14 Javascript
vue学习笔记之slot插槽基本用法实例分析
Feb 01 Javascript
JavaScript canvas绘制渐变颜色的矩形
Feb 18 Javascript
JS call()及apply()方法使用实例汇总
Jul 11 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使用pack处理二进制文件的方法
2014/07/03 PHP
Laravel5权限管理方法详解
2016/07/26 PHP
php通过pecl方式安装扩展的实例讲解
2018/02/02 PHP
用Jquery实现可编辑表格并用AJAX提交到服务器修改数据
2009/12/27 Javascript
用jquery实现等比例缩放图片效果插件
2010/07/24 Javascript
在网站上应该用的30个jQuery插件整理
2011/11/03 Javascript
jquery连缀语法如何实现
2012/11/29 Javascript
node.js中的fs.fchown方法使用说明
2014/12/16 Javascript
Vue.js每天必学之过滤器与自定义过滤器
2016/09/07 Javascript
jQuery中show与hide方法用法示例
2016/09/16 Javascript
浅谈Angular.js中使用$watch监听模型变化
2017/01/10 Javascript
JavaScript中object和Object的区别(详解)
2017/02/27 Javascript
深入浅析AngularJS中的一次性数据绑定 (bindonce)
2017/05/11 Javascript
js脚本编写简单刷票投票系统
2017/06/27 Javascript
不使用 JS 匿名函数理由
2017/11/17 Javascript
JavaScript设计模式之单例模式简单实例教程
2018/07/02 Javascript
使用VueCli3+TypeScript+Vuex一步步构建todoList的方法
2019/07/25 Javascript
一篇文章带你浅入webpack的DLL优化打包
2020/02/20 Javascript
vue-router 路由传参用法实例分析
2020/03/06 Javascript
Python 自动安装 Rising 杀毒软件
2009/04/24 Python
Pygame框架实现飞机大战
2020/08/07 Python
python 基于selectors库实现文件上传与下载
2020/12/31 Python
详解移动端html5页面长按实现高亮全选文本内容的兼容解决方案
2016/12/03 HTML / CSS
ivx平台开发之不用代码实现一个九宫格抽奖功能
2021/01/27 HTML / CSS
Canon佳能美国官方商店:购买数码相机、数码单反相机、镜头和打印机
2016/11/15 全球购物
GNC健安喜官方海外旗舰店:美国著名保健品牌
2017/01/04 全球购物
美国益智玩具购物网站:Fat Brain Toys
2017/11/03 全球购物
在什么时候需要使用"常引用"
2015/12/31 面试题
介绍一下结构化程序设计方法和面向对象程序设计方法的区别
2012/06/27 面试题
偷看我的初中毕业鉴定
2014/01/29 职场文书
数控专业大学毕业生职业规划范文
2014/02/06 职场文书
战友聚会策划方案
2014/06/13 职场文书
对外汉语专业大学生职业生涯规划范文
2014/09/13 职场文书
法定代表人授权委托书
2014/09/19 职场文书
2015年国庆晚会主持词
2015/07/01 职场文书
Python 文本滚动播放器的实现代码
2021/04/25 Python