dom selector 分析--1

Ext.DomQueryJquery Selector的分析

1、概述

JS的主要作用就是对Dom元素的操作。操作Dom元素的显示,怎么显示,如何显示等形式。在操作Dom 元素,首先就是取得Dom元素的引用。一般我们都会采用document.getElementById(id)通过指定的id来引用,还有一个常用的方法就是通过document.getElementsByTagName(tagName)来获得指定tagNameDom元素的集合。对于间接引用,我们可以通过Dom元素的以下的几个方法parentNode,firstChild,lastChild,childNodes,nextSibling,previousSibling得到与本元素相关的Dom元素。

Js开发工具很简陋,没有提示,这些方法名太长,很容易写错,调试又很麻烦。Prototype$(id)的出现,一下子似乎成为取得元素的标准。这是在知道元素的id的情况下,很多时候我们是不知道id的,比如我想找一个 name’username’input类型的元素?怎么办?

在写css时候,我们发现Css的对元素的选择是一个很好的解决方法,于是Js中对元素的查询现在都支持CSS1~CSS3的标准。那么如何去实现CSS选择器呢?http://www.w3.org/TR/2005/WD-css3-selectors-20051215/,是我们可以粗略分成几类,ID选择器(#id),Class选择器(.class),类型(type)选择器(p)Combinators,属性(Attribute)选择器,Pseudo Classes选择器等。这些都是单一的选择器,可以在应用中把它们组合起来,如:div#id, div:last-child

 

我们仔细分析一下,这些选择器。既然叫做selector,肯定是有选择的功能。先分析一个例子:span[class=’test’],这是一个属性选择器,我们一般这样理解,找到class=’test’的所有标签为span的元素集。这就是从Dom树中select到所需要的元素。其实我们把这个拆分成两步,第一步是根据document.getElementsByTagName(tagName)取得span的元素集,第二步是再根据其class是否等于test来进行判断,把不等于的元素从结果集中去掉。事实上,扩展javascript只能是这样进行处理。

这样处理的话,我们就能把selector分成两类,一类是从根据给定的selectorDom树找到相关的元素节点放到结果集中来,第二类就是在结果集中进行判断该元素还满足下面的表达式呢?第一类是是选择(selector),第二类是筛选(filter)

这样一下,就可以把一个复合的selector表达式分解成以多个单selector的操作。如div#id就可以变成先找到div的元素,之后就判断其元素的id是否等于id,如果不等于就从结果集中删除掉。而对于div[id=’bar’]也是一样,可以看出div#iddiv[id=’id’]是一样的操作。对于div.class也可以转换到属性中去操作。

我们可细化一下:把ID选择器(#id),Class选择器(.class),类型(type)选择器(p)Combinators,属性(Attribute)选择器,Pseudo Classes选择器等分成选择和筛选。Id Seletor在起始的位置,如#id,是选择器,不在起始的位置上如div#id是筛选器。Class选择器(.class)ID选择器一样。属性(Attribute)选择器是筛选的功能。它只能在一个元素或多个元素后面。

对于Pseudo Classes选择器,其作用也是筛选的功能。(不过有的时候可能会是选择功能)。对于Type选择器,只能是选择器了,它要么是开始。要么和+>,~组成复合的Combinators选择器。对于像E FE > F or E/FE + FE ~ F这样的复合选择器,我们其三部分可以分成这样:E 是元素的集合,符号是>+~。而F只能是标签的形式了。a > p#id。这样分析的话,就是先取a > p所有元素的集合,再在集合找到id相同的。在分析的时候,对于p来说,他根本不要关心a,它只要关心于 >。我们可以把 > p看成和p相同的一部分,看成.class,#id这样的一部分。

看一个:div.foo:nth-child(odd)[@foo=bar]#aId > a

à第一步:找到div type选择器的所有元素。

à第二步:在这些元素中找到classfoo的元素。如果没有就根本不要去分析下面的字符。

à第三步:根据:nth-child(odd)找到元素的子孩子元素为偶数的的元素。进一步筛选了。

à第四步:找到[],就是属性筛选器了,根据foo属性值bar来判断集合中还有那些满足条件。

à第五步:在这些元素集合,找到id=aId的元素,进一步筛选。

à第六步:在这些元素集合中找到所有元素对应的子元素类型为a的所有子元素。

à结果:我们可以看到,现在元素是子元素的集合。

 

那么应该怎么分析这样的表达式呢?对div,可以采用getElementsByTagName,对于.foo,那我们得自己去写函数判断元素的是不是有这个class了,对于:nth-child(odd),我们也得采用Pseudo Classes的方法,对于[@foo=bar]我们也要写属性分析函数,对于#aId,采用getElementById。对于> a 我们也得自己来处理。如果.foo在开头怎么办?我们可能分成两步来做,先根据.doucument中找到所有的元素,之后根据.foo进行筛选。

 

分隔这些字符串也不难,只要.,#,:,[, blackspace,+,/,>,~是分隔符。,+,/,>,~只要不在[]中出现就是元素选择器,在[]中出现是属性的比较器。

待续

看一下domQuery,Jquery是怎么实现的吧。

 

2domQuery分析

select : function(path, root, type) { // 默认把document当做root if (!root || root == document) { root = document; } // 支持root采用string id的形式 if (typeof root == "string") { root = document.getElementById(root); } // 支持多个path var paths = path.split(","); var results = []; for (var i = 0, len = paths.length;i < len; i++) { // 去string前后的空格 var p = paths[i].replace(trimRe, ""); // cache 已经编译的select if (!cache[p]) { // 编译 cache[p] = Ext.DomQuery.compile(p); if (!cache[p]) { throw p + " is not a valid selector"; } } // 运行编译的函数 var result = cache[p](root); if (result && result != document) { results = results.concat(result); } } // 去掉重复的元素 if (paths.length > 1) { return nodup(results); } return results; },

 

 

在上面的代码中,我们可以看出该函数支持多个path, compile每个path之后,运行编译的函数。最后去掉重复的元素。上面的代码Ext.DomQuery.compile(p)是把每个path编译成函数,便于重用。这种方式对于较复杂的path是能提高效率的,但是如#id,.class的之类的,就得不偿失了。这应该是大量使用的,很多是用过之后又不用了。本来只要一些getElementById就可以搞定的,现在又要生成函数,又要缓存。肯定慢的。所以使用domQuery的select最好的是那些经常重用的且path比较复杂的。

Ext.DomQuery.compile(p)就是主要处理根据path而动态编译查询的函数。上一节,我们讲到getElementById可以得到根据id得到元素在,那么.class,> p这样的呢?在EXT.domQuery中,每种方法都有一种像getElementById的方法。采用get开头是选择,采用by开头是筛选。

首先我们要选择元素,根据tagName,>这样的符合来选择:

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值