JQuery選擇器中含有冒號的ID處理差異的分析


問題提出

對於一個輸入框, 如果其id中含有冒號(:),選擇器使用需要有特殊寫法, 

例如 id為下

<input type="text" value="ddd" id="a:b">

使用$(selector)直接使用#id值, 找不到DOM

console.log("#a:b")
console.log($("#a:b").length) // output 0

 

經過探索可以使用以下兩個方法選擇到DOM:

$("#a\\:b");

$("[id^='a:b']");

其中第一種, 需要在冒號(:)前添加一個反斜杠(\), 某些同學要問了,命名是兩個反斜杠, 忽悠我?

請看下節介紹。

JS轉義

js中的字符串語法需要占用一些元字符, 對於這些元字符如果需要想要在字符串中出現就需要使用轉移:

轉義表: FROM http://www.w3school.com.cn/js/js_special_characters.asp

代碼 輸出
\' 單引號
\" 雙引號
\& 和號
\\ 反斜杠
\n 換行符
\r 回車符
\t 制表符
\b 退格符
\f 換頁符

事實上,從上表中看以看出,不僅僅是元字符需要顯示才轉義(" -> \"), 還有使用轉移后可以將特定字符轉義為另外含義的情況, 例如 n -> \n 變為換行符。

從學習上可以按照如下邏輯整體理解js轉義:

1、 js字符串使用 單引號 或者 雙引號 作為字符串的”括號”, 表示一個整體字符串, 引號內部為字符串內容,

2、 那么問題來了, 字符串內容中要想包括 引號, 怎么辦?

3、 js設計者(Brendan Eich)考慮, 類似c語言中轉義方法, 使用\x 方式來表示x, 即轉義規則 \x == x, 這里x包括引號(‘ “)。

4、 既然使用了\x格式執行轉義, 那字符串內容中要想包含\怎么辦? 如果直接使用\, 則很可能跟其后面一個正常字符意外的轉義。

5、 既然轉義規則已定  \x == x, 那么\在字符串內容中存在的場景,正符合此規則, 規則應用效果為 \\ == \ 、

6、 最后還有一種場景, 存在很多控制字符(包括格式控制字符,例如\n、 和不可打印字符), 這些字符本身沒有可打印字符的表現形式,即沒有在ascii中[32, 126]出現。

      但是字符只能書寫[32, 126]之間的字符, 所以這種情況下需要定義一種新的轉義, 為不增加轉義元字符, 轉義字符還為\,

      轉義規則為 \x == y,  x in [32, 126], y in [0, 31] or 127

說明: 如果對[32, 126]之間的非3 和 6中使用字符進行轉義,即應用規則(\x), 則得到還是x, 符合轉義規則。

總結如下圖:

demo代碼:

            console.log('---- \\x present x (x is printable character) ----');
            console.log('slash + slash='+"\\"); // \ is part of transfer encoding syntax
            console.log('slash + "='+"\""); // " is collision with string syntax double quotation, eg "xx" wrapped by "
            
            console.log('---- \\x present control character ----');
            console.log('slash + n='+"\n"); // \n present line break

            console.log('---- \\x equal x when x is not needed transfer concoding ----');
            console.log('slash + 8='+"\8"); // \8 == 8
            console.log('slash + o='+"\o"); // \o == o
            console.log('slash + :='+"\:"); // \: == :

 

JQuery執行順序跟蹤

經過分析JQuery源代碼, $("")的調用次序

1、 $ === jQuery.fn.init

2、 走到 jQuery.fn.init 中  return ( context || rootjQuery ).find( selector );

3、 find == jQuery.find == Sizzle

4、Sizzle 調用 newContext.querySelectorAll( newSelector )

而 querySelectorAll 為 最新瀏覽器都實現的 選擇器查詢接口,

各個DOM上都實現了 此接口, 詳情見 http://www.cnblogs.com/snandy/archive/2011/03/30/1999388.html

所以查看到這里, 說明jquery 和 sizzle 未對選擇器做處理。完全是 瀏覽器實現 的 querySelectorAll接口行為。

 

CSS Selector規則

W3C對選擇器有一套規范:

http://www.w3.org/TR/css3-selectors/#selectors

其中有對 id選擇器的說明, 可見選擇器的中 : 為選擇器的元字符, 即保留字符, 做偽類使用,其他情況不能與之沖突。

所以讀到此處,看官明白 $("#a\\:b"); 為什么要對冒號添加轉義了吧!

對於此選擇器 即 "#a\\:b"  由於其本身是js字符串, \\表示\, 那么這個字符串在內存中的存儲為

可以猜測 querySelectorAll 的實現, 首先會同js對selector字符串 做解析, 首先分析 : 前面不帶\ 的冒號, 將其前后內容拆分, 前面為 選擇器主體部分, 后面為偽類部分,

然后對 前面主題部分, 執行js字符串轉義解析, 類似eval接口執行行為 :

eval("var evalselector = '#a\\:b';")
console.log(evalselector); // output  #a:b

將 轉義表寫 還原為 

 

運行如下js:

console.log("#a:b")
console.log("#a\:b")
console.log(document.querySelectorAll("#a\\:b").length)
console.log("[id^='a:b']")
console.log(document.querySelectorAll("[id^='a:b']").length)

打印如下:

結論 :

1、冒號(:)屬於js中不用轉義的普通可見字符, "#a:b" === "#a\:b"

2、 "#a\\:b" 為避免css selector偽類標識符號沖突的解法, 經過 js字符串轉義,需要兩個反斜杠\, 最后在 querySelectorAll接口中,將\:轉換為:

3、 對於id中含有冒號的情況, 可以使用"[id^='a:b']"的表寫方式, 這種不需要對冒號轉義, 因為這種格式不存在沖突的可能。

建議 id可變的情況,都使用"[id^='a:b']"方式。

demo code

<html>
<head> 
        <script type="text/javascript" src="./jquery.js"></script>
        <style>

        </style>
</head> 
<body>
        <input type="text" value="ddd" id="a:b">
        <script>
            console.log('---- \\x present x (x is printable character) ----');
            console.log('slash + slash='+"\\"); // \ is part of transfer encoding syntax
            console.log('slash + "='+"\""); // " is collision with string syntax double quotation, eg "xx" wrapped by "
            
            console.log('---- \\x present control character ----');
            console.log('slash + n='+"\n"); // \n present line break

            console.log('---- \\x equal x when x is not needed transfer concoding ----');
            console.log('slash + 8='+"\8"); // \8 == 8
            console.log('slash + o='+"\o"); // \o == o
            console.log('slash + :='+"\:"); // \: == :


            console.log('#a+slash+slash+:b='+"#a\\:b"); // #a\\:b == #a\:b
            console.log('jquery length='+$("#a\\:b").length); // length == 1 with selector #a\\:b
            console.log('jquery length='+$("[id^='a:b']").length); // length == 1 with selector [id^='a:b']


            console.log("#a:b") //output #a:b
            console.log("#a\:b") //output #a:b
            console.log(document.querySelectorAll("#a\\:b").length)//output 1
            console.log("[id^='a:b']") //: do not need transfer coding
            console.log(document.querySelectorAll("[id^='a:b']").length) //output 1
        </script>
</body>
</html>

 

 


免責聲明!

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



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