西西軟件下載最安全的下載網(wǎng)站、值得信賴(lài)的軟件下載站!

首頁(yè)編程開(kāi)發(fā)其它知識(shí) → CSS選擇符表達(dá)式引擎的入門(mén)知識(shí)分享

CSS選擇符表達(dá)式引擎的入門(mén)知識(shí)分享

相關(guān)軟件相關(guān)文章發(fā)表評(píng)論 來(lái)源:西西整理時(shí)間:2013/2/14 10:37:18字體大。A-A+

作者:西西點(diǎn)擊:0次評(píng)論:0次標(biāo)簽: CSS選擇符

  • 類(lèi)型:編程輔助大。134KB語(yǔ)言:中文 評(píng)分:3.3
  • 標(biāo)簽:
立即下載

本文的目標(biāo)讀者是入門(mén)級(jí)Web前端開(kāi)發(fā)人員。  

本文介紹了CSS選擇符表達(dá)式引擎的基本原理。CSS選擇符引擎幾乎是前端開(kāi)發(fā)人員每天在使用的工具。本文將逐一介紹實(shí)現(xiàn)該引擎的各種策略。首先,我們介紹基于W3C標(biāo)準(zhǔn)API的方法。

W3C標(biāo)準(zhǔn)的Slectors API

能夠支持的平臺(tái): Safari 3+, Firefox 3.1+, Internet Explorer 8+, Chrome and Opera 10+

兩個(gè)最常用的方法:

querySelector,該函數(shù)接受一個(gè)CSS選擇符字符串,返回找到的第一個(gè)元素,如果沒(méi)有找到則返回null。

querySelectorAll,該函數(shù)接受一個(gè)CSS選擇符字符串,返回找到的所有元素的集合(NodeList)。

這兩個(gè)方法存在于所有的DOM元素,DOM文檔對(duì)象,以及DOM文檔片段(fragment)對(duì)象上。

<div id="test">
<b>Hello</b>, I'm a ninja!
</div>
<div id="test2"></div>
<script>
window.onload = function () {
var divs = document.querySelectorAll("body > div");
assert(divs.length === 2, "Two divs found using a CSS selector.");
var b = document.getElementById("test")
.querySelector("b:only-child");
assert(b,
"The bold element was found relative to another element.");
};
</script>

上述例子的一個(gè)缺點(diǎn)是它依賴(lài)于瀏覽器對(duì)CSS選擇符的支持(老版本IE就歇菜了),因此可以考慮使用以某元素作為根節(jié)點(diǎn)對(duì)子節(jié)點(diǎn)的查詢(xún)。代碼如下。

<script>
window.onload = function () {
var b = document.getElementById("test").querySelector("div b");
assert(b, "Only the last part of the selector matters.");
};
</script>

上述代碼有個(gè)問(wèn)題,當(dāng)以某元素作為根節(jié)點(diǎn)對(duì)子節(jié)點(diǎn)的查詢(xún)時(shí),query函數(shù)只檢查最右邊的部分是不是包含在父節(jié)點(diǎn)里。注意到#test下面壓根就沒(méi)有div標(biāo)簽,可是query函數(shù)忽略了查詢(xún)字符串的前面部分。 

這個(gè)現(xiàn)象確實(shí)有悖于我們期望的CSS選擇符引擎的運(yùn)行效果,所以我們需要做一些修補(bǔ)工作。最常見(jiàn)的技巧是:臨時(shí)增加一個(gè)新id給那個(gè)根節(jié)點(diǎn)元素從而強(qiáng)行地包含它里面的內(nèi)容。代碼如下。

<script>
    (function () {
        var count = 1;
        this.rootedQuerySelectorAll = function (elem, query) {
            var oldID = elem.id;
            elem.id = "rooted" + (count++);
            try {
                return elem.querySelectorAll("#" + elem.id + " " + query);
            } catch (e) {
                throw e;
            } finally {
                elem.id = oldID;
            }
        };
    })();
    window.onload = function () {
        var b = rootedQuerySelectorAll( document.getElementById("test"), "div b");
        assert(b.length === 0, "The selector is now rooted properly.");
    };
</script>

在上述代碼中我們需要注意到以下幾點(diǎn):

首先,要給父元素一個(gè)全局唯一的id,因此需要保存父元素原始的id。然后把這個(gè)全局唯一的id添加到查詢(xún)字符串中。 

接著的收尾部分就是去除新增加的那個(gè)id和返回查詢(xún)結(jié)果,這個(gè)過(guò)程中可能會(huì)有一個(gè)API異常拋出(多數(shù)情況是因?yàn)檫x擇符語(yǔ)法錯(cuò)誤或者是瀏覽器不支持的選擇符)。因此,我們要在外層用try/catch語(yǔ)句塊包住API調(diào)用語(yǔ)句,還要在finnally的子句中還原父元素的原始id。你可能會(huì)發(fā)現(xiàn),這里隱藏著JavaScript語(yǔ)言神奇的一個(gè)地方,就是雖然我們?cè)趖ry語(yǔ)句里已經(jīng)return了,可是finnally子句還是要被執(zhí)行的(在結(jié)果值被真正return給調(diào)用函數(shù)前)。

選擇器API絕對(duì)可以算作W3C標(biāo)準(zhǔn)里最具前途的新API了。一旦主流瀏覽器支持CSS3(或至少絕大部分CSS3選擇符)以后,它可以節(jié)省編程人員使用大量的JavaScript代碼。

使用 XPath 尋找元素

XPath是一種可以在DOM文檔中查詢(xún)節(jié)點(diǎn)的語(yǔ)言。它甚至比CSS選擇符更加強(qiáng)大。許多流行的瀏覽器 (Firefox,Safari 3+, Opera 9+, Chrome)都提供了對(duì)XPath的部分函數(shù)實(shí)現(xiàn),可以在HTML文檔中查找元素。 Internet Explorer 6及之前的版本只能使用XPath查找XML文檔(而不是HTML文檔)。

Xpath表達(dá)式比復(fù)雜的CSS選擇符執(zhí)行快。但是,當(dāng)我們實(shí)現(xiàn)一個(gè)純DOM操作方式的CSS選擇符引擎時(shí),我們要考慮瀏覽器支持性的風(fēng)險(xiǎn)。在對(duì)于簡(jiǎn)單的CSS選擇符,Xpath就失去優(yōu)越性了。

因此我們考慮使用一個(gè)閾值,當(dāng)使用Xpath更有利的情況下我們就是用Xpath。決定閾值依賴(lài)于開(kāi)發(fā)人員的經(jīng)驗(yàn),比如:當(dāng)查找id或者標(biāo)簽時(shí),使用純DOM操作代碼永遠(yuǎn)是更快的方式。

如果用戶(hù)瀏覽器支持Xpath表達(dá)式,我們可以使用下述代碼(依賴(lài)于prototype庫(kù))。

if (typeof document.evaluate === "function") {
    function getElementsByXPath(expression, parentElement) {
        var results = [];
        var query = document.evaluate(expression,
        parentElement || document,
        null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
        for (var i = 0, length = query.snapshotLength; i < length; i++)
        results.push(query.snapshotItem(i));
        return results;
    }
}

雖然使用Xpath可以解決任何選擇符問(wèn)題,但它不是一個(gè)可行的方案。對(duì)于一個(gè)的CSS選擇符表達(dá)式,對(duì)應(yīng)的Xpath的表達(dá)式卻是令人生畏的復(fù)雜。 下面這個(gè)表格展示了如何把CSS選擇符轉(zhuǎn)換到Xpath表達(dá)式。

當(dāng)建造一個(gè)基于正則表達(dá)式的CSS選擇符引擎時(shí),我們可以包含Xpath的方式作為一個(gè)子模塊,它把用戶(hù)查詢(xún)的CSS選擇符表達(dá)式部分轉(zhuǎn)換成Xpath的表達(dá)式,然后使用Xpath的辦法查找DOM。

實(shí)現(xiàn)XPath的部分的代碼可能與正則表達(dá)式方式的代碼一樣多。很多開(kāi)發(fā)人員選擇拋棄XPath的部分來(lái)減少CSS選擇符引擎的復(fù)雜程度。所以,你需要衡量Xpath帶來(lái)的的性能提升以及它的代碼實(shí)現(xiàn)復(fù)雜程度。

純DOM實(shí)現(xiàn)方式

CSS選擇符引擎的核心是以純DOM操作方式實(shí)現(xiàn)的。它解析用戶(hù)給出的CSS選擇符,然后使用已有的DOM方法(例如getElementById, getElementsByTagName)來(lái)查找對(duì)應(yīng)的DOM元素。使用純DOM方式實(shí)現(xiàn)有以下理由:

      第一,Internet Explorer 6 and 7。盡管IE8以上的版本支持querySelectorAll()方法,但是在IE6、7中對(duì)Xpath和選擇符API的支持使得使用純DOM實(shí)現(xiàn)很有必要。

      第二,向下兼容,如果你希望你的代碼能夠“降級(jí)”支持老版本的瀏覽器(比如Safari 2),那么你應(yīng)該使用純DOM實(shí)現(xiàn)。

      第三,為了速度。對(duì)于某些CSS選擇符表達(dá)式,使用純DOM核心能夠處理得更快(比如根據(jù)id找元素)。

知道了使用純DOM核心的重要性,接下來(lái)我們要看以?xún)煞N方式實(shí)現(xiàn)選擇符引擎:從上往下解析,和從下往上解析。

一個(gè)從上往下的引擎是這樣解析CSS選擇符表達(dá)式的:從左往右的匹配元素,在前部分的基礎(chǔ)上再接著找下部分匹配的元素。 這種方式是目前主流JavaScript庫(kù)的實(shí)現(xiàn)方式,更通用地,也是尋找頁(yè)面元素的最佳方式。 讓我們來(lái)看一段標(biāo)記

<body>
    <div></div>
    <div class="ninja">
<span>Please </span>
        <a href="/ninja"><span>Click me!</span>
        </a>
    </div>
</body>

如果我們想選"Click me!"那個(gè)元素,我們可以這樣寫(xiě) 選擇符表達(dá)式 : div.ninja a span 。

使用從上往下的方法是這樣解析這個(gè)選擇符表達(dá)式的:

表達(dá)式中的第一項(xiàng),div.ninja 指明了文檔中的一顆子樹(shù)。在那顆子樹(shù)中,接著找表達(dá)式中下一項(xiàng)對(duì)應(yīng)的子樹(shù)。最后,span的目標(biāo)節(jié)點(diǎn)被找到。

注意,這只是最簡(jiǎn)單的情況。在任何層的推進(jìn)過(guò)程中,完全有可能有多顆子樹(shù)同時(shí)匹配表達(dá)式。在實(shí)現(xiàn)選擇符引擎的時(shí)候,有兩個(gè)原則需要考慮:

     返回的結(jié)果中元素的順序應(yīng)該按照在文檔中原本的順序出現(xiàn)

     返回的結(jié)果中的元素不應(yīng)該有重復(fù)的(比如不能一個(gè)元素在結(jié)果中出現(xiàn)兩次)

為了避免這些陷阱,具體的代碼實(shí)現(xiàn)可能會(huì)有一點(diǎn)小技巧。下面是一個(gè)簡(jiǎn)化的top-down方式引擎,它只能夠支持按照tag標(biāo)簽名字查找元素。

<div>
    <div>    <span>Span</span>
    </div>
</div>
<script>
    window.onload = function () {
        function find(selector, root) {
            root = root || document;
            var parts = selector.split(" "),
                query = parts[0],
                rest = parts.slice(1).join(" "),
                elems = root.getElementsByTagName(query),
                results = [];
            for (var i = 0; i < elems.length; i++) {
                if (rest) {
                    results = results.concat(find(rest, elems[i]));
                } else {
                    results.push(elems[i]);
                }
            }
            return results;
        }
        var divs = find("div");
        assert(divs.length === 2, "Correct number of divs found.");
        var divs = find("div", document.body);
        assert(divs.length === 2,
            "Correct number of divs found in body.");
        var divs = find("body div");
        assert(divs.length === 2,
            "Correct number of divs found in body.");
        var spans = find("div span");
        assert(spans.length === 2, "A duplicate span was found.");
    };
</script>

在上面的例子中,我們實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的支持按照tag標(biāo)簽名字查找元素的從上往下解析方式的選擇符引擎。 這個(gè)引擎可以分解為幾個(gè)子部分:解析選擇符表達(dá)式,在文檔中查找元素,過(guò)濾元素,遞歸/合并每一層里的結(jié)果。

解析選擇符表達(dá)式

在上面的例子中,解析過(guò)程就是把CSS選擇符(例如"div span")分解成字符串?dāng)?shù)組(["div", "span"])。實(shí)際上,在CSS2和CSS3的標(biāo)準(zhǔn)中,使用屬性值查找元素是被支持的。因此在選擇符中完全有可能有額外的空格,使得上面簡(jiǎn)單的方法不可行了。但是,這種簡(jiǎn)單的方法對(duì)于處理大部分的情況已經(jīng)足夠了。

要完全實(shí)現(xiàn)解析,我們需要一系列的解析規(guī)則來(lái)處理用戶(hù)給出的任何表達(dá)式。 下面的代碼就是使用正則表達(dá)式把表達(dá)式分解成各個(gè)小塊(如果需要,則分開(kāi)逗號(hào))

<script type="text/javascript">
    var selector = "div.class > span:not(:first-child) a[href]"
    var chunker = /((?:\([^\)]+\)|\[[^\]]+\]|[^ ,\(\[]+)+)(\s*,\s*)?/g;
    var parts = [];
    // Reset the position of the chunker regexp (start from beginning)
    chunker.lastIndex = 0;
    // Collect the pieces
    while ((m = chunker.exec(selector)) !== null) {
        parts.push(m[1]);
        // Stop if we've countered a comma
        if (m[2]) {
            extra = RegExp.rightContext;
            break;
        }
    }
    assert(parts.length == 4,
        "Our selector is broken into 4 unique parts.");
    assert(parts[0] === "div.class", "div selector");
    assert(parts[1] === ">", "child selector");
    assert(parts[2] === "span:not(:first-child)", "span selector");
    assert(parts[3] === "a[href]", "a selector");
</script>

顯然,這段代碼支持的選擇符只是一張大拼圖中的一小部分。我們需要定義更多的解析規(guī)則來(lái)支持用戶(hù)輸入的各種表達(dá)式組合。絕大多數(shù)CSS選擇符引擎使用了map結(jié)構(gòu),來(lái)將正則表達(dá)式對(duì)應(yīng)到目標(biāo)處理函數(shù)。這樣當(dāng)一個(gè)正則表達(dá)式匹配用戶(hù)表達(dá)式的一部分時(shí),對(duì)應(yīng)的函數(shù)就去處理那一部分表達(dá)式的選擇符。

尋找元素

在頁(yè)面中尋找正確的DOM元素有許多種解決方案。使用哪種方案取決于瀏覽器支持什么樣的選擇符。

首先是 getElementById() 方法。它只在HTML文檔的根節(jié)點(diǎn)上存在。它的作用是找到第一個(gè)匹配指定id值的元素,因此他可以用來(lái)解決 "#id" 這樣的表達(dá)式。注意,在Internet Explorer 和 Opera,它同樣也會(huì)查找第一個(gè)具有同名 name值的元素。 因此,如果需要值按照 id 值查找,我們需要額外的一步驗(yàn)證工作來(lái)排除掉 name值同名的元素。

如果需要支持尋找所有具有給定 id值的元素(這在CSS選擇符表達(dá)式中是習(xí)慣性用法,盡管HTML文法規(guī)定一個(gè)id只能對(duì)應(yīng)一個(gè)元素),有兩種方法可以采用:第一種方法,遍歷所有的元素,找出所有匹配給定 id值的元素;第二種方法,使用 document.all["id"] ,它將返回一個(gè)包含匹配id值元素的數(shù)組。

接下來(lái)是 getElementsByTagName() 方法,它的作用就如同它的名字所述:找出所有匹配給定標(biāo)簽名的元素。 注意,它還有另一種用法:如果使用 星號(hào)* 作為參數(shù)標(biāo)簽名,那么它會(huì)返回文檔中或者一個(gè)節(jié)點(diǎn)下所有的元素。 這一招對(duì)于 處理基于屬性值的選擇符 很有用,比如 ".class" 或者 "[attr]"。 因?yàn)?nbsp;".class" 并沒(méi)有指定標(biāo)簽名,所以我們需要列出某節(jié)點(diǎn)下的所有子元素,然后依次判斷class名稱(chēng)。

另外,在Internet Explorer中使用 星號(hào)* 查找有一個(gè)缺點(diǎn),它同樣會(huì)返回 注釋語(yǔ)句 節(jié)點(diǎn)(因?yàn)樵贗E中,注釋語(yǔ)句節(jié)點(diǎn)有一個(gè) "!" 的標(biāo)簽名,所以它也會(huì)被返回)。 這樣,我們需要額外的一步過(guò)濾工作來(lái)排除注釋語(yǔ)句節(jié)點(diǎn)。

接下來(lái)是 getElementsByName() 方法,它的作用只有一個(gè): 找出所有匹配給定 name值的節(jié)點(diǎn)(例如,<input> 元素都具有name值 )。因此這個(gè)方法可以用來(lái)解決 "[name=name]" 這樣的表達(dá)式。

最后是 getElementsByClassName() 方法。這個(gè)方法相對(duì)比較新,正在被各主流瀏覽器實(shí)現(xiàn)(Firefox 3+, Safari 3+ 和 Chrome)。它的作用是基于 元素的class名稱(chēng) 進(jìn)行查找。 這種瀏覽器原生的方法極大地加快了 按class名稱(chēng)查找 的代碼實(shí)現(xiàn)。

盡管還有一些其他技巧用來(lái)解決元素查找,以上那些方法依然是我們主要使用的工具。一旦找出了所有匹配的備選元素后,接下來(lái)就進(jìn)行元素過(guò)濾了。

過(guò)濾元素

一個(gè)CSS表達(dá)式通常是由幾個(gè)獨(dú)立的小部分組成的。例如,這樣一個(gè)表達(dá)式 "div.class[id]" 就由三個(gè)部分組成: 1. div元素 2. 具有給定的class名稱(chēng) 3. 具有一個(gè)名叫id的屬性值。

首先我們需要找出選擇符的第一部分。例如,在上述表達(dá)式中,我們看到第一部分是找div元素,所以我們立刻想到用 getElementsByTagName() 方法找出頁(yè)面上所有的 <div> 元素。 接下來(lái),我們必須過(guò)濾元素,使得剩下的元素具有給定的class名稱(chēng)和id屬性值。

過(guò)濾元素是選擇符引擎的實(shí)現(xiàn)中普遍存在的一部分。過(guò)濾原則主要依靠元素屬性值或者元素在DOM樹(shù)中與其他節(jié)點(diǎn)的關(guān)系。

按照屬性過(guò)濾:訪問(wèn)元素的DOM屬性(通常使用 getAttribute() 方法),并且驗(yàn)證它的值是否等于給定值。按照class類(lèi)名過(guò)濾是本類(lèi)別中的一個(gè)子集(訪問(wèn) className 屬性并且驗(yàn)證它的值)。

按照位置關(guān)系過(guò)濾: 這種情況出現(xiàn)在對(duì)于在某父元素上使用 ":nth-child(even)" 或者 ":last-child" 組合的表達(dá)式。 如果瀏覽器支持這樣的CSS選擇符,那么會(huì)返回一個(gè)子元素的集合。另外,所有的瀏覽器都支持 childNodes,它返回一個(gè)子元素的集合,其中也包含所有的純文本節(jié)點(diǎn)和注釋語(yǔ)句節(jié)點(diǎn)。 使用以上兩種方法,可以按照元素在DOM樹(shù)中的位置關(guān)系進(jìn)行過(guò)濾。

實(shí)現(xiàn)元素過(guò)濾功能具有兩個(gè)目的:第一,可以把這個(gè)功能提供給用戶(hù)讓他們測(cè)試任意元素是否符合某值;第二,在內(nèi)部計(jì)算時(shí),可以檢查元素是否符合用戶(hù)給出的選擇符表達(dá)式。

合并元素

在本文的第一段代碼中,我們可以看見(jiàn)選擇符引擎需要能夠遞歸的找元素(找出后代元素)以及合并所有符合要求的元素,最終返回結(jié)果集。

但是,在本小節(jié)中,我們初步的代碼實(shí)現(xiàn)太簡(jiǎn)單了。注意到,我們最終在文檔中找到了兩個(gè) <span> 元素。因此,我們需要做額外的一步檢查,來(lái)確保最終結(jié)果的數(shù)組中不能包含重復(fù)的元素。 大多數(shù)top-down方式的選擇符引擎中都使用了若干確保元素唯一性的方法。

<div id="test">
<b>Hello</b>, I'm a ninja!</div>
<div id="test2"></div>
<script>
    (function () {
        var run = 0;
        this.unique = function (array) {
            var ret = [];
            run++;
            for (var i = 0, length = array.length; i < length; i++) {
                var elem = array[i];
                if (elem.uniqueID !== run) {
                    elem.uniqueID = run;
                    ret.push(array[i]);
                }
            }
            return ret;
        };
    })();
    window.onload = function () {
        var divs = unique(document.getElementsByTagName("div"));
        assert(divs.length === 2, "No duplicates removed.");
        var body = unique([document.body, document.body]);
        assert(body.length === 1, "body duplicate removed.");
    };
</script>

其中的 unique() 方法給數(shù)組中的所有元素增加了一個(gè)額外的屬性,標(biāo)記它們是否被訪問(wèn)過(guò)。因此,當(dāng)所有元素都處理后,最終只剩下了不重復(fù)的元素。類(lèi)似本方法的其他算法可以在大多數(shù)CSS選擇符引擎中見(jiàn)到。

到目前為止,我們大致就構(gòu)造了一個(gè) 從上到下方式(top-down)的 CSS選擇符引擎。 現(xiàn)在,我們來(lái)看另外的一種方案。

從下到上的方式實(shí)現(xiàn)

如果你不用考慮唯一地確定元素,那么你可以以從下到上的方式(bottom-up)實(shí)現(xiàn)選擇符解析過(guò)程。它的流程跟從上到下的方式相反(復(fù)習(xí)那張解析過(guò)程的圖示)。例如,對(duì)于這樣的表達(dá)式 "div span",你需要首先找出所有 <span> 元素,然后對(duì)于每個(gè)候選元素,看看它們是否有一個(gè) <div> 的祖先元素。 

這樣的方式并沒(méi)有 從上到下的方式 流行。盡管它能夠良好地處理簡(jiǎn)單的CSS選擇符表達(dá)式,但是在每個(gè)候選元素上對(duì)于祖先的遍歷就顯得太耗費(fèi)時(shí)間和資源了。

構(gòu)造從下到上方式的引擎很簡(jiǎn)單。首先找到CSS選擇符表達(dá)式中的最后一個(gè)部分,然后找出匹配的元素,接著按照一系列的過(guò)濾規(guī)則過(guò)濾掉不符合的元素。下面的代碼闡述了這一過(guò)程。

<div>
    <div>
<span>Span</span>

    </div>
</div>
<script>
    window.onload = function () {
        function find(selector, root) {
            root = root || document;
            var parts = selector.split(" "),
                query = parts[parts.length - 1],
                rest = parts.slice(0, -1).join("").toUpperCase(),
                elems = root.getElementsByTagName(query),
                results = [];
            for (var i = 0; i < elems.length; i++) {
                if (rest) {
                    var parent = elems[i].parentNode;
                    while (parent && parent.nodeName != rest) {
                        parent = parent.parentNode;
                    }
                    if (parent) {
                        results.push(elems[i]);
                    }
                } else {
                    results.push(elems[i]);
                }
            }
            return results;
        }
        var divs = find("div");
        assert(divs.length === 2, "Correct number of divs found.");
        var divs = find("div", document.body);
        assert(divs.length === 2,
            "Correct number of divs found in body.");
        var divs = find("body div");
        assert(divs.length === 2,
            "Correct number of divs found in body.");
        var spans = find("div span");
        assert(spans.length === 1, "No duplicate span was found.");
    };
</script>

注意,上述代碼只能處理一層祖先關(guān)系。如果需要處理多層祖先關(guān)系,那么當(dāng)前層的狀態(tài)則需要被記錄?紤]使用兩個(gè)數(shù)組:第一個(gè)數(shù)組記錄將要被返回的元素(其中的某些元素被設(shè)置成undefined,如果它們不能匹配表達(dá)式);第二個(gè)數(shù)組記錄當(dāng)前需要被測(cè)試的祖先節(jié)點(diǎn)。

就如之前所述,這一步額外的祖先關(guān)系驗(yàn)證會(huì)帶來(lái)更多的性能開(kāi)銷(xiāo)。但是按照從下到上的方式實(shí)現(xiàn)就不需要在結(jié)果集中取出重復(fù)元素的一步,因此它也算有一些優(yōu)勢(shì)。(因?yàn)榘凑諒南碌缴系姆绞皆谧铋_(kāi)始的時(shí)候每個(gè)元素就已經(jīng)是各自獨(dú)立不重復(fù)的了;而如果按照從上到下的方式,由于在遞歸時(shí)子樹(shù)可能相互重疊,所以那會(huì)包含重復(fù)的元素)

小結(jié)

JavaScript實(shí)現(xiàn)的CSS選擇符引擎是一個(gè)強(qiáng)大的工具。它能讓我們輕松地使用若干選擇符語(yǔ)法在頁(yè)面上尋找DOM元素。盡管在完全實(shí)現(xiàn)一個(gè)選擇符引擎時(shí)有非常多的細(xì)節(jié)需要考慮,這種情況正在大大的改善(得益于瀏覽器原生的方法)。

回顧一下本文討論的幾點(diǎn):

現(xiàn)代瀏覽器已經(jīng)開(kāi)始實(shí)現(xiàn)對(duì)于 W3C標(biāo)準(zhǔn)選擇符API的支持,但是依然有很長(zhǎng)一段路要走.

考慮到性能問(wèn)題,我們?nèi)匀挥斜匾獙?shí)現(xiàn)自己的選擇符引擎。

要?jiǎng)?chuàng)建一個(gè)選擇符引擎,我們可以:

      利用 W3C標(biāo)準(zhǔn)的選擇符API

      利用 XPath

      為了最好的性能,使用純DOM操作方式

從上到下的方式非常流行,但是它需要一些清理工作:比如確保返回元素不重復(fù)。

從下到上的方式避免了那個(gè)清理工作,但是它會(huì)帶來(lái)更多的性能開(kāi)銷(xiāo)。

隨著瀏覽器逐漸支持W3C標(biāo)準(zhǔn)選擇符,顧慮引擎實(shí)現(xiàn)的細(xì)節(jié)或許會(huì)成為過(guò)去式了。但是對(duì)于許多開(kāi)發(fā)人員來(lái)說(shuō),那一天或許不能很快的到來(lái)。

    相關(guān)評(píng)論

    閱讀本文后您有什么感想? 已有人給出評(píng)價(jià)!

    • 8 喜歡喜歡
    • 3 頂
    • 1 難過(guò)難過(guò)
    • 5 囧
    • 3 圍觀圍觀
    • 2 無(wú)聊無(wú)聊

    熱門(mén)評(píng)論

    最新評(píng)論

    發(fā)表評(píng)論 查看所有評(píng)論(0)

    昵稱(chēng):
    表情: 高興 可 汗 我不要 害羞 好 下下下 送花 屎 親親
    字?jǐn)?shù): 0/500 (您的評(píng)論需要經(jīng)過(guò)審核才能顯示)
    推薦文章

    沒(méi)有數(shù)據(jù)