sizzle分析記錄:關於querySelectorAll兼容問題


querySelector和querySelectorAll是W3C提供的 新的查詢接口

目前幾乎主流瀏覽器均支持了他們。包括 IE8(含) 以上版本、 Firefox、 Chrome、Safari、Opera。

萬能的sizzle在高版本的瀏覽器中復雜的選擇器盡量走querySelectorAll,前提是這個匹配的節點沒有兼容問題

從IE8開始雖然支持querySelectorAll的API,但是會有各式各樣的BUG,所以sizzle拿rbuggyQSA用來記錄這個BUG問題

if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {    
    //newContext.querySelectorAll( newSelector )
}

zepto的選擇器則更直接

zepto.qsa = function(element, selector) {
    var found
    return (isDocument(element) && idSelectorRE.test(selector)) ?
        ((found = element.getElementById(RegExp.$1)) ? [found] : []) :
        (element.nodeType !== 1 && element.nodeType !== 9) ? [] :
        slice.call(
            classSelectorRE.test(selector) ? element.getElementsByClassName(RegExp.$1) :
            tagSelectorRE.test(selector) ? element.getElementsByTagName(selector) :
            element.querySelectorAll(selector)
    )
}

sizzle 2000行,zepto直接querySelectorAll接口,利弊各自評估了

 


重點就看querySelectorAll的坑到底有哪些?

接口定義:

partial interface Document {
  Element?  querySelector(DOMString selectors);
  NodeList  querySelectorAll(DOMString selectors);
};

partial interface DocumentFragment {
  Element?  querySelector(DOMString selectors);
  NodeList  querySelectorAll(DOMString selectors);
};

partial interface Element {
  Element?  querySelector(DOMString selectors);
  NodeList  querySelectorAll(DOMString selectors);
};

從接口定義可以看到Document、DocumentFragment、Element都實現了NodeSelector接口。即這三種類型的元素都擁有者兩個方法。

querySelector和querySelectorAll的參數須是符合 css selector 的字符串。

不同的是querySelector返回的是一個對象,querySelectorAll返回的一個集合(NodeList)。

所以選擇querySelectorAll更符合jQuery這個合集對象的習慣

 


document.querySelectorAll 與 element.querySelectorAll區別?
 
當調用上下為document的時候,沒有什么問題,各瀏覽器的實現基本一致
 
如果調用的上下文是element,dom Node的時候,瀏覽器的實現有點不同
 
具體就是表現在:element.querySelectorAll 在文檔內找全部符合選擇器描述的節點包括Element本身
<div class= "aaron"   id= "aaronId" >
     <p><span>內容</span></p>
     <div class="text">452</div>
</div>

js

<script type="text/javascript">

var aaElement = document.getElementById('aaronId');
var element     = aaElement.querySelector('.aaron span');
var elementList = document.querySelectorAll('.aaron span');
console.log(element);  // <span>Test</span>
console.log(elementList);  //

問題出在testElement.querySelector盡然還有返回值!選擇上下文是在aaElement里面,選擇器是.aaarn就父節點,理論是找不到對應的節點的

所以邏輯上是不合理的,因為根本找不到,但是結果跟document調用如出一轍,所以此時node ele類似document 了

可能的查找機制是這樣的:首先在document的范圍內進行查找所有滿足選擇器條件的元素,

在上面這段代碼中,我們的選擇器是.aaron span,就是所有的直接父元素類名為aaron的元素。

然后,再看哪些元素是調用querySelector/querySelectorAll的元素的子元素,這些元素將會被返回

這也就說明了為什么aaElement會一同返回


那么針對這種情況如何兼容?

程序員的智慧總是無窮的, Andrew Dupont提出了一個解決方案,來自jQuery2.1.1

先看看jQuery最終的實現newContext.querySelectorAll用的上下文調用

if ( newSelector ) {
    try {
        push.apply( results,
            newContext.querySelectorAll( newSelector )
        );
        return results;
    } catch(qsaError) {
    } finally {
        if ( !old ) {
            context.removeAttribute("id");
        }
    }
}

代碼可見newContext可能是document || 提供的一個上下文

如:$("#aaronId").find("div[class='text']) 此時的上下文即是$("#aaronId")節點

jQuery(element).find(selector) 在文檔內找全部符合選擇器描述的節點不包括Element本身

注意finally總是執行context.removeAttribute("id"),意味着我們在之前的處理強制加了一個id

 

反推hack的手法,selectors前面指定上下文的的id,限制匹配的范圍

版本各有實現的不同,但是我們目前最終版為標准2.1.1

IE 8 :不支持上下文為object;

if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
    groups = tokenize( selector );

    if ( (old = context.getAttribute("id")) ) {
        nid = old.replace( rescape, "\\$&" );
    } else {
        context.setAttribute( "id", nid );
    }
    nid = "[id='" + nid + "'] ";

    i = groups.length;
    while ( i-- ) {
        groups[i] = nid + toSelector( groups[i] );
    }
    newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context;
    newSelector = groups.join(",");
}

1. 關鍵是給context設置一個id

所以上下文content,就會存在這個id限制范圍

image

2. 拼接出查詢的選擇器,附上這個ID前綴

newSelector: "[id='sizzle-1405486760710'] div[class='text']"

3. 查詢

newContext.querySelectorAll( newSelector )

4. 因為強制加了ID,所以需要刪除

context.removeAttribute("id");

 

這樣就達到目的范圍限制:context.querySelectorAll了

 


querySelectorAll在選擇器上存在的問題,具體我是看jQuery的源碼相關處理,基本都是IE8上的問題

jQuery對兼容的判斷,都是采用的功能判斷直接特性檢測,偽造一個真實的環境測試支持度

針對querySelectorAll選取存在的問題之后分析


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM