XPath和CSS選擇器

[譯]XPath和CSS選擇器

原文:http://ejohn.org/blog/xpath-css-selectorscss


最近,我作了不少工做來實現一個同時支持XPath和CSS 3的解析器,令我驚訝的是:它們倆在某些方面上很是類似,而在另外一些方面上又徹底不一樣.不一樣的地方有,CSS是用來配合HTML工做的,可使用#id來根據ID獲取元素,以及使用.class來根據class獲取元素.這些用XPath實現的話都不會那麼簡潔,反過來呢,XPath可使用..來返回到DOM樹的上層節點中,還可使用foo[bar]來獲取到一個擁有bar子元素的foo元素.CSS選擇器徹底作不到這些,總結一下就是,和XPath比起來,CSS選擇器一般都比較短小,但惋惜的是不夠強大.html

我認爲將這兩種選擇器的寫法作一個比較是頗有價值的.node

目標 CSS 3 XPath
全部元素 * //*
全部的P元素 p //p
全部的p元素的子元素 p > * //p/*
根據ID獲取元素 #foo //*[@id='foo']
根據Class獲取元素 .foo                                //*[contains(@class ,'foo')] 1
擁有某個屬性的元素 *[title] //*[@title]
全部P元素的第一個子元素 p > *:first-child //p/*[0]

全部擁有子元素a的P元素git

沒法實現 //p[a]
下一個兄弟元素 p + * //p/following-sibling::*[0]

從語法上看,我很是驚訝這兩種選擇器在某些狀況下的類似性,尤爲是'>'和'/'二者之間.雖然他們並不老是有着相同的功能(XPath中要取決於正在使用的軸),但一般狀況下他們指的都是某個父元素的子元素.還有,空白符' '和'//'都意味着當前元素的全部後代元素.最後是星號'*',相似於通配符,表示全部元素,而不論是哪一種標籤名.github


1 這個寫法其實不正確,由於它不光會匹配到咱們想要的'foo bar',還會意外的匹配到'foobar'.正確的寫法可能會很是複雜,可能會須要用到多個表達式才能完成.chrome

下面是譯者注:

上表中錯誤的XPath:
瀏覽器

//*[contains(@class,'foo')]

我實現的寫法是:函數

//*[@class='foo' or contains(@class,' foo ') or starts-with(@class,'foo ') or substring(@class,string-length(@class)-3)=' foo']

比起CSS的.foo,真的是好複雜,我來解釋一下,一個元素的class屬性中若是包含'foo',可能有四種狀況,列出表來是這樣的:google

class="foo" //*[@class='foo'] class屬性只有一個值foo
class="foobar foo bar" //*[@class=' foo '] class屬性值中,foo在其餘兩邊的值的中間

class="foo bar"lua

//*[starts-with(@class,'foo ')] class屬性值中,foo在最左邊
class="bar foo" //*[substring(@class,string-length(@class)-3)=' foo'] class屬性值中,foo在最右邊,XPath1.0中沒有ends-with函數,2.0有,如今瀏覽器實現的都是1.0

那麼咱們能在網頁開發中用上XPath嗎?最初,jQuery是支持XPath選擇器的,但後來,因爲效率問題,jQuery放棄了對XPath的支持.恰好,谷歌在上個月發佈了Wicked Good XPath,這是一個DOM Level 3 XPath規範的純JavaScript實現,也是目前同類實現中最快的,咱們能夠把這個腳本和jQuery結合起來使用.

jQuery.getScript("http://wicked-good-xpath.googlecode.com/files/wgxpath.install.js").success( () {        wgxpath.install();        jQuery.xpath =  elements = [];                        xpathResult = document.evaluate(xpath, document, , 6,  ( i = 0; i < xpathResult.snapshotLength; i++ jQuery(elements);

這樣就能經過$.xpath()靜態方法來選擇元素了,該方法返回的也是一個jQuery對象,和使用$()沒什麼差異.本頁面已經加載了這個腳本,你能夠如今打開控制檯試驗一下$.xpath方法.

那咱們有了CSS選擇器,爲何還要用XPath呢,答案是:有些時候,XPath更強大一點.好比:

在上面John Resig總結的表中,有一個CSS沒法實現的功能,就是查找包含某個子元素的父元素.的確,目前的CSS還沒法實現,不過在將來CSS4的選擇器中,將會有一個父選擇器

E! > F    //注意,2011年的時候,父選擇器的語法是$E > F,今年草案又改了.網上有些介紹CSS4選擇器的博文仍是舊的,這裏有一個能在CSS文件中使用父選擇器的polyfill https://github.com/Idered/cssParentSelector

該選擇器能夠選取到那些包含子元素F的E元素.但即使之後實現了CSS4,稍微改變一下需求,查找那些包含後代元素F的E元素,CSS選擇器又怎麼寫呢?應該是沒什麼辦法實現.熟悉jQuery的朋友可能會說,jQuery裏有:has僞類,能夠這麼寫E:has(F),的確,若是使用jQuery自定義的過濾器,幾乎任何需求均可以用遍歷DOM的方法實現,但效率絕對會很低.而XPath就不同了,畢竟Firefox和chrome都已經實現了XPath的接口document.evaluate方法(Wicked Good XPath應該主要是努力在IE上實現統一的接口),速度確定比手動遍歷DOM來的快.XPath的寫法是這樣的//E[.//F],怎麼樣,也挺簡單明瞭的.

另外很重要的一點是,CSS原本是用於給HTML添加樣式的,12種節點類型中,只有元素節點(nodetype等於1)纔有樣式這一說,所以,CSS選擇器只能選取到頁面中的元素節點,而XPath就不是了,它不光能夠用在HTML中,還能夠用在XML中,除了元素節點,而能夠選擇屬性節點(//@*)或者文本節點(//text())等,若是將來XPath2.0實現了,它會變的更增強大.

相關文章
相關標籤/搜索