小心: E4X 已廢棄不用。 It's been disabled by default for chrome in Firefox 17, and completly removed in Firefox 21. 請使用 DOMParser/DOMSerializer 或 a non-native JXON algorithm 替代。
使用 E4X 處理 XML
JavaScript 在 1.6 版本中首次引入,E4X 引入原生的 XML 物件以供 JavaScript 語言使用,並新增用來在 JavaScript 的代碼中嵌入照字面表達的 XML 文件的語法。
E4X 的完整定義可以在 Ecma-357 規範 中找到。本章提供實踐中的語言概要;但這並不是全面的參考資料。
相容性的問題
在主要的瀏覽器支援 <script>
元素以前,對於嵌入到頁面裡的 JavaScript 存有一種常見的做法,就是使用 HTML 的註解標籤將其包夾,避免瀏覽器直接把 <script>
裡的 JavaScript 代碼顯示在使用者眼前。如今已無須這麼做,不過仍殘存在部分遺留下來的代碼中。為了向後相容的需要,E4X 預設會忽略註解和 CDATA 區段。你可以給你的 <script>
標籤加上 e4x=1
參數以解除這項限制︰
<script type="text/javascript;e4x=1"> ... </script>
XML 物件的建立
E4X 提供兩種建立 XML 物件的方式。第一種是傳入字串給 XML
建構子︰
var languages = new XML('<languages type="dynamic"><lang>JavaScript</lang><lang>Python</lang></languages>');
第二種是直接在你的 Script 中嵌入 XML︰
var languages = <languages type="dynamic"> <lang>JavaScript</lang> <lang>Python</lang> </languages>;
在這兩種情況裡,產生都會是 E4X XML
物件,他提供了方便的語法用以存取並更新其內夾藏著的資料。
XML 物件在外觀上和行為上都和普通的 JavaScript 物件很類似,但有兩件事並不相同。E4X 引入的新語法只能用在 E4X XML 物件上。雖然新語法被設計成對 JavaScript 設計者而言較熟悉的形式,但 E4X 並不提供從 XML 到原生 JavaScript 物件的完整對應;只是看起來很像而已。
可以把變數加入到 XML 的字面中用來建立元素的名稱(或是內容)。
var h = 'html'; var text = "Here's some text"; var doc = <{h}><body>{text}</body></{h}>; alert(doc.toXMLString()); // 產生的是 <html> <body>Here's some text</body> </html>
屬性的運用
當你需要動態的建立標記的時候,XML 字面語法明顯更勝 XML
建構子。使用 E4X 可輕易的在標記中嵌入動態值。只需使用花括弧 ({}) 包夾要建立的屬性值,並省略一般會加上的引號標記,變數和表達式即可用於建立屬性值,如同下例所示︰
var a = 2; var b = <foo bar={a}>"hi"</foo>;
開始執行時,變數的值會被求出,並自動在適當的位置加上引號。前面的例子產生的 XML 物件看起來就像這樣︰<foo bar="2">"hi"</foo>
。
在屬性的替換方面,雙引號會被跳脫成 ",單引號則按平常方式處理。
var b = 'He said "Don\'t go there."'; var el = <foo a={b}/>; alert(el.toXMLString()); // 產生︰<foo a="He said "Don't go there.""/>
小於和 & 符號也會被跳脫成各自的等價實體。由於大於符號不會被跳脫,因此如果包含了 CDATA 結束序列 (]]>),就有可能產生 XML 錯誤。
通常不會直接在字面(或變數)的屬性內容之中作修改(例如,bar="a{var1}{var2}"
)。如果有必須使用 JavaScript 表達式計算變數來替代的內容的話(例如,bar={'a'+var1+var2}
),在元素字面之前先定義新的變數,在變數中包含完整的修改內容,然後在字面中含入這個變數,或在字面之後取回屬性並將其修改(見下)。
可如同屬性值一般的修改屬性的名稱︰
var a = 'att'; var b = <b {a}='value'/>; alert(b); // Gives: <b att="value"/>
…但之後就不能修改表達式(例如,<b {a}>)。
執行上面的例子之後,參考 XML 物件的語言變數與 XML 文件中的 <languages>
結點一致。這個結點只有一個屬性︰type,這個屬性可用各種方式來存取並更新︰
alert(languages.@type); // 警報 "dynamic" languages.@type = "agile"; alert(languages.@type); // 警報 "agile"
alert(languages.toString()); /* 警報: <languages type="agile"><lang>JavaScript</lang><lang>Python</lang></languages> */
注意,如果想要把取回的屬性與其他的字串作比較,就有必要先把屬性作轉換,即使屬性有可能會在其他地方被轉換成字串(例如插入到文字框中)。
if (languages.@type.toString() === 'agile') { ... }
XML 物件的運用
XML 物件提供一系列的方法用來查閱並更新其中的內容。這類物件亦支援 JavaScript 的點 (.) 和 []
符號,但不是用來存取物件的屬性,E4X 覆蓋了這些運算子,改用來存取元素的子結點︰
var person = <person> <name>Bob Smith</name> <likes> <os>Linux</os> <browser>Firefox</browser> <language>JavaScript</language> <language>Python</language> </likes> </person>; alert(person.name); // Bob Smith alert(person['name']); // Bob Smith alert(person.likes.browser); // Firefox alert(person['likes'].browser); // Firefox
如果你使用多於一個以上的元素來存取的話,你會得到 XMLList
︰
alert(person.likes.language.length()); // 2
和 DOM 一樣,*
可以用來存取所有的子結點︰
alert(person.likes.*.length()); // 4
.
運算子會直接存取指定結點的子結點,..
運算子則會存取所有的子結點,不論嵌入的有多深︰
alert(person..*.length()); // 11
length()
方法在此返回 11,因為元素結點和文字結點兩者都被包含在 XMLList
的結果中。
用來表示 XML 元素的物件提供一系列實用的方法,部分方法的解說見下︰TODO: Add all of the methods to the JavaScript reference, link from here
alert(person.name.text()) // Bob Smith var xml = person.name.toXMLString(); // 內含 XML 的字串 var personCopy = person.copy(); // XML 物件的深層複製 var child = person.child(1); // 第二個子結點;目前是 <likes> 元素
XMLLists 的運用
除了 XML 物件以外,E4X 也引入了 XMLList
物件。XMLList
是用來表示附有編號的 XML 物件的集合;例如,元素的列表。接續上面的例子,我們可以在頁面上存取 XMLList
的 <lang>
元素如下︰
var langs = languages.lang;
XMLList
提供 length()
方法,可用來查出內含元素的數目︰
alert(languages.lang.length());
注意,有別於 JavaScript 陣列的 length 屬性,這個 length 是方法,不是屬性,而且必須使用 length()
來呼叫。
我們可以迭代所有相配的元素,就像這樣︰
for (var i = 0; i < languages.lang.length(); i++) { alert(languages.lang[i].toString()); }
在此我們使用同一個語法存取陣列中的已編號項。雖然這部分很類似普通的陣列,但 XMLList
並不支援 Array
的方法,如 forEach
,且陣列的通用功能,如 Array.forEach()
也不相容於 XMLList
物件。
我們也可以使用在 JavaScript 1.6 中所引入的 for each...in 語法,他已支援 E4X 的部分︰
for each (var lang in languages.lang) { alert(lang); }
for each...in
也可以用在普通的 JavaScript 物件,迭代出內含在物件中的值(不是鍵)。如同 for...in,非常不建議 和陣列一起使用。
可以使用下面的 XML 字面語法來建立 XMLList
,而無須建立格式正確的 XML 文件︰
var xmllist = <> <lang>JavaScript</lang> <lang>Python</lang> </>;
+=
運算子可用來把新的元素附加到文件中的 XMLList
︰
languages.lang += <lang>Ruby</lang>;
注意,有別於由普通的 DOM 方法所返回的結點列表,XMLList
只是靜態的,而且不會自動更新以反映在 DOM 中的改變。如果你在既有的 XML
物件底下建立 XMLList
作為子集,XMLList
並不會反映 XML 物件的改變;你需要重新建立 XMLList 以取得最近的更新︰
var languages = <languages> <lang>JavaScript</lang> <lang>Python</lang> </languages>; var lang = languages.lang; alert(lang.length()); // 警報 2 languages.lang += <lang>Ruby</lang>; alert(lang.length()); // 仍舊警報 2 lang = languages.lang; // 重新建立 XMLList alert(lang.length()); // 警報 3
搜尋和過濾
E4X 提供特殊的運算子用來在通過指定條件的文件內部選取結點。這種過濾動作是由括弧裡面的表達式所指定的︰
var html = <html> <p id="p1">First paragraph</p> <p id="p2">Second paragraph</p> </html>; alert(html.p.(@id == "p1")); // 警報 "First paragraph"
在表達式前面比對路徑的結點(在本例中即 p 元素)會在表達式求值之前先加上作用域的限縮。就如同已經用 with 語法 指定了一樣。
因此,過濾器也可以依據在目前元素中內含的單一結點的值來執行︰
var people = <people> <person> <name>Bob</name> <age>32</age> </person> <person> <name>Joe</name> <age>46</age> </person> </people>; alert(people.person.(name == "Joe").age); // 警報 46
也可以使用 JavaScript 函數當作過濾器的表達式︰
function over40(i) { return i > 40; } alert(people.person.(over40(parseInt(age))).name); // 警報 Joe
命名空間的處理
E4X 也全面考慮到命名空間。任何代表結點或屬性的 XML 物件都提供了可返回 QName
物件的 name()
方法,使加上命名空間的元素的查詢更為容易。
預設
default xml namespace = "http://www.w3.org/1999/xhtml"; // 目前還沒有必要在 HTML 標記裡指定命名空間 var xhtml = <html><head><title></title></head><body> <p>text</p></body></html>; alert(xhtml.head); // 目前還沒有必要在此處的子元素中指定命名空間
非預設
var xhtml = <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Embedded SVG demo</title> </head> <body> <h1>Embedded SVG demo</h1> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> <circle cx="50" cy="50" r="20" stroke="orange" stroke-width="2px" fill="yellow" /> </svg> </body> </html>; alert(xhtml.name().localName); // 警報 "html" alert(xhtml.name().uri); // 警報 "http://www.w3.org/1999/xhtml"
若要存取非預設命名空間內部的元素,首先建立 Namespace
物件把特定的命名空間的 URI 加以封裝︰
var svgns = new Namespace('http://www.w3.org/2000/svg');
目前可以在 E4X 中使用 namespace::localName
代替一般的元素指定子來作查詢︰
var svg = xhtml..svgns::svg; alert(svg); // 顯示文件中的 <svg> 部分