Ruby's Louvre

司徒正美不姓司徒,RubyLouvre不主打Ruby……

javascript 选择器简明教程

本来准备开讲jQuery源码学习笔记六的,但心中有佛才能看到佛,有些人连选择器都不知什么东西,直接讲下去,估计有人会看得云里雾里,满头雾水了。加之,John Resig有一种把代码写得不知所云的魔力,他拥有强大的驭驾代码的实力,他自己看当然没问题,其他人则要命了。常常是一句代码调用几个方法,每个方法相隔几十行甚至上百行,而且这些方法还常常是幌子,真正做事是其他代码。这样盘根错节的代码与Base2有得一拼。最后,选择器本身也是非常复杂的东西,有必须独立提出来说一下。

选择器其实早已实现,不过都是一些不起眼但超常用的方法:getElementById,getElementsByName,getElementsByTagName。但外国人不满意这些API,于是搞出了getElementsByClassName,进而是getElementsBySelector,支持当时已公布的所有CSS2.1选择符!然后Prototype用一个$与一个$$把它们统括起来。之所以这样,其动机显然易见,如果返回一个就不用索引号去取了!像jQuery那样无论是返回多少个,反正都包裹在jQuery对象,都要用索引号或get方法把真正DOM元素取出来,因此用不用$$都差不多,于是$统一天下!

但这个包打天下的选择器其实包括多少东西呢?细算一下,ID选择器,标签选择器,联合选择器,属性选择器,关系选择器与伪类选择器。ID选择器,就是以#开头的,标签选择器就是tagName,联合选择器就是那个逗号,属性选择器就是用中括号括起来的字段,里面也非常复杂,可以单纯只有属性名,不用属性值,如果有属性值,又分好几种情况匹配(详见我的另一篇博文《getElementsByAttribute》)。像getElementsByName应该归并于属性选择器,因为name也是一个属性。关系选择符又细分为四个:兄长(~),亲子(>),相邻(+)与后代(空格)。伪类选择器的成员非常庞大,因为CSS3新添加的这些伪类选择符也是如此。除了伪元素选择符与页面伪类与链接伪类等少量无法转换为选择器外,其他基本都能。这些符合资格的伪类有目标伪类,结构伪类,语言伪类与UI状态伪类。语言伪类与UI状态伪类与属性选择符很相近,因此实现手段也一致。目标伪类我们可以通过截取地址栏上的参数,用IE选择器搞定。结构伪类则又是个大家族,分为两派:root与其他。:root就是根节点documentElement。其他都可以统称这子元素过滤器,我们可以根据顺序(nth-child)来筛选,可以根据类型来选(only-of-type),又可以组合类型与顺序来选,还可以根据元素里面有没有内容来选(empty),里面还有一些变种,如那些带first与last的。在jQuery,为了照顾那些美工MM,John Resig又定义许多快捷的选择符,这个,自己看文档吧。

兄长选择符

相邻选择符

亲子选择符

好了,我们说一下它的实现原理吧。通常都是选择一个字符串,最后返回一组元素。如果字符串是“#aaa”就好办,把前面的"#" 砍掉,用getElementsById去查找便是!如果是"p span"意思是取得所有p元素中的span元素,我们先用document.getElementsByTagName("p")得到所有p元素,然后遍历里面的p,用currentP.getElementsByTagName("span")就行了。但这都是最理想的情况下,我们要怎样才知道调用这些API呢?#提醒我们用IE选择器,但如果#号是包含在引号中呢,如p["gh#erewf"],这里也有#号。因此我们必须处理一下字符串,如把两边的空白去掉,把里面不必要的空白去掉,里面的每个空白只占一个字符空间就是,让我们知道那些后代选择符就是,多余的没有必要。我们看jQuery是如何处理的:

一个很强大的正则,不过这样后代选择符就看不到了,jQuery在后面一定做了什么补救措施。事实上,jQuery许多东西都不是一步到位,一个方法调用另一个方法,最后不是怎的就解决了其实一下子转换为数组意义不大,后面还是要其他正则进行深加工,它们都需要进一步的正则匹配,分析当前的数组元素是ID选择符还是属性选择符。

看来大家喜欢jQuery,那我就举jQuery吧。其他类库也是用许多正则来处理字符串,来得到它们想要的信息。jQuery外面看起来很漂亮很清致,里面的实现真是相当复杂与恶心。或许John Resig是个心理阴暗的人,他公开源码但不想别人看懂,故意写成这个样子。我更怀疑源码中那些注释是给jQuery另两个作者看的……嘛,都瞎猜的。我们接着看实现流程,上面这些ID,class,name,attr什么的,基本上是用那两个选择器document.getElementById与element.getElementsByTagName加getAttribute搞定。犹其是那个标签选择器,每个元素节点都有这方法,我们才可以层层遴选正确的元素。每次我们取得一堆元素,然后逐一检测其属性与在父元素的位置,得到结果放到一个数组中,然后下一次再重复这样的步骤。有时,两个元素都有相同的子元素,我们就需要在这些元素做一些标记,如果没有这标记的才放进数组,有就跳过。至于这些怎样实现,这是jQuery源码要讲的。最后附上一个表:

开头字符代表选择符判断方法
#ID选择符/#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/
.类选择符/\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/
后代选择符/^\s\s*/
[属性选择符/\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/
+相邻选择符/^[>\+\~]/
~兄长选择符/^[>\+\~]/
>亲子选择符/^[>\+\~]/
:伪类选择符/:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
,联合选择符/,/
字母标签选择符/^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/
Tag标签: javascript
4
0
(请您对文章做出评价)
« 上一篇:jQuery源码学习笔记五
» 下一篇:jQuery源码学习笔记六

posted on 2009-11-22 16:44 司徒正美 阅读(2059) 评论(19)  编辑 收藏 网摘

评论

#1楼 2009-11-22 16:51 Jeffrey Zhao      

大哥你太强了,拼命写啊。   回复  引用  查看    

#2楼 2009-11-22 17:17 柳絮随风      

大哥拼命写,我拼命看。   回复  引用  查看    

#3楼 2009-11-22 17:29 Ariex      

这些应该是CSS的选择器吧?js什么时候需要这些了?另外,一些选择其似乎不支持陈旧的浏览器,比如IE7-,是不是最好说明一下,不然某些人会看不到效果……
最后,lime的底色+yellow的前景是不是?……
  回复  引用  查看    

#4楼 2009-11-22 17:30 Ivony...      


的确是一高产牛人。。。。


说到选择器,我忽然想到W3C那个NC组织搞的最NC的东西XPath,按理说XPath应该是宇宙中最强大的选择器表达式,可惜难以被地球人理解。。。。

结果地球人纷纷选择这个虽然看起来很奇怪但还是比XPath简洁的所谓CSS Selector
  回复  引用  查看    

#5楼[楼主] 2009-11-22 17:41 司徒正美      

@Ariex
jQuery不是活生生的例子吗?
  回复  引用  查看    

#6楼 2009-11-22 17:57 励冰      

狠狠的些吧。   回复  引用  查看    

#7楼 2009-11-22 19:59 马来西亚程序员      

大哥你放心,我们都拼命的在学习   回复  引用  查看    

#8楼 2009-11-22 21:01 一叶舟      

$是什么
看不明白
  回复  引用  查看    

#9楼 2009-11-22 22:59 李子哥哥      

请教楼主及各位

JS里有没有可能将一个对像继承自 Function对象,或者弄得跟 Function类似

比如

类:
function MyFun()
{
}

声明一个 MyFun

var fn=new MyFun()

要能是fn 可以像一个 Function一样的使用

var obj=fn(args,....)

  回复  引用  查看    

#10楼 2009-11-23 00:04 scott.cgi[未注册用户]

我实现了一个css的引擎,可自由扩展
http://mojo-js.appspot.com
  回复  引用    

#11楼 2009-11-23 07:59 光锥之内就是命运      

引用李子哥哥:
请教楼主及各位

JS里有没有可能将一个对像继承自 Function对象,或者弄得跟 Function类似

比如

类:
function MyFun()
{
}

声明一个 MyFun

var fn=new MyFun()

要能是fn 可以像一个 Function一样的使用

var obj=fn(args,....)


这个我试过,结论就是不行。
但可以模拟
只要 MyFun 输出一个 Function对象就行了,然后对这个输出的对象添加相应的成员。   回复  引用  查看    

#12楼[楼主] 2009-11-23 08:33 司徒正美      

@李子哥哥
你可以看一下Prototype与mootools,做出的类符合你的要求。
  回复  引用  查看    

#13楼 2009-11-23 09:32 伊牛娃      

两天没看,都写到第六篇了


得努力看
  回复  引用  查看    

#14楼 2009-11-23 12:22 airy      

很好很强大   回复  引用  查看    

#15楼 2009-11-23 17:26 czjone      

你这不是CSS 中的吗?`
你怎么什么都说?
B/S 的要是这都不清楚就有点不好做了~
  回复  引用  查看    

#16楼 2009-11-23 17:28 czjone      

这和JS 有关系吗?

亲子选择符 只能在IE 6 以下才能用的(包括IE 6)
  回复  引用  查看    

#17楼 2009-11-23 22:20 xiaoxixi      

为了照顾那些美工MM,John Resig又定义许多快捷的选择符
=============================================

此处有可能让读者产生误导
这些个选择符号 不是jQuery作者发明创造的 而是属于css3的范畴
详细可以在http://www.w3.org/TR/css3-selectors/#pseudo-classes 了解
  回复  引用  查看    

#18楼[楼主] 2009-11-24 02:53 司徒正美      

@czjone
那三个选择符是给不认识亲子兄长相邻选择符的人看的

@xiaoxixi
许多快捷的选择符是指:
:file
:parent
:selected
:header
:text
:checkbox
:radio
:password
:submit
:input
:gt
:lt
:eq
:hidden等
W3C的那东西我熟得很……

  回复  引用  查看    

#19楼 2009-11-24 17:45 李子哥哥      

@光锥之内就是命运
@司徒正美

不好意思,那天提个问题就不得时间关注

如果按 @光锥之内就是命运 说的返回一个Function,是可以,不过这有问题,有要他像其它类一样有一些成员,或者方法,就要给这个Function,成员赋值,这样会影响性能,没有扩展Prototype性能好

Prototype与mootools 我也看了下,还是不晓得咋解决这问题
  回复  引用  查看