摘要:五的子類對象會返回一個集合對象,集合內存儲類型的元素。七的子類初看很有可能以為集合元素就是單選表單元素,其實可以存儲任意類型的表單元素。八的子類開始,將返回子類的對象,其行為特征和一致。但在前,我們應該先了解清楚的類型的特征。
一、前言
大家先看看下面的js,猜猜結果會怎樣吧!
可選答案:
①. 獲取id屬性值為id的節點元素
②. 拋namedItem is undefined的異常
var nodes = document.getElementsByName("dummyName"); var node = nodes.namedItem("id");
答案是兩種都有可能哦!document.getElementsByName在Chrome和FF30.0中返回NodeList(木有namedItem方法的),在IE全系列中都返回HTMLCollection,吐血了吧?
DOM集合又何止這些呢,下面我們就一起來探討一下吧!
相同點:
1. 類數組。有length屬性,可以用下標索引來訪問其中的元素,但沒有Array的slice等方法;
2. 只讀。無法增刪其中的元素;
3. 實時同步DOM樹的變化。若DOM樹有新元素加入,該類型的對象也會將新元素包含進來;
4. 可通過下標數字類型索引獲取集合中指定位置的元素;
5. 可通過item({String | Number} 索引)方法獲取集合中指定位置的元素,若通過索引找不到元素,則以第一個元素作為返回值。
不同點(主要表現在HTMLCollection比NodeList能力更強大):
1. HTMLCollection對象可通過namedItem({String} id或name)獲取首個匹配的元素,若沒有則返回null;
2. HTMLCollection對象可通過點方式獲取第個id或name匹配的元素,若沒有則返回undefined。
各瀏覽器選擇器返回類型差別:
// IE678 返回具有HTMLCollection特征(有namedItem方法)的[object Object]對象 // IE9、10、11、FF、Chrome均返回HTMLCollection document.images; document.links; document.anchors; document.forms; document.embeds; document.scripts; document.applets; document.plugins; Node對象.getElementsByTagName; Node對象.getElementsByTagNameNS; Node對象.getElementsByClassName; HTMLTableElement對象.tBodies; HTMLTableElement對象.children; HTMLTableElement對象.rows; HTMLTableRowElement對象.cells; HTMLMapElement對象.areas; // IE678 返回具有HTMLCollection特征(有namedItem方法)的[object Object]對象 // IE9、10、11返回HTMLCollection // FF30.0、Chrome返回NodeList document.getElementsByName; // IE678 返回具有NodeList特征(無namedItem方法)的[object Object]對象 // IE9、10、11、FF、Chrome均返回NodeList Node對象.childNodes; // IE5678 返回具有HTMLCollection特征(有namedItem方法)的[object Object]對象 // IE9、10返回[object HTMLCollection] // IE11、Chrome返回[object HTMLAllCollection] // FF30.0返回[object HTML document.all class] document.all;
總體來說Chrome的實現更接近W3C規范;
HTMLAllCollection、HTMLCollection和[object HTML document.all class]功能沒什么區別,只是類型不同而已;
由于document.getElementsByName在不同的瀏覽器中返回不同類型的對象,因此推薦使用[{Number} 索引]的方法來訪問集合元素會省心一些;
題外話:children屬性僅獲取nodeType為1的元素,而childNodes會將所有子元素的包含進來;
注意:IE9、10、11的HTMLCollection與其他瀏覽器的HTMLCollection可不相同哦,具體請看下一節吧!
假如大家看過《JS魔法堂:追憶那些原始的選擇器》,應該會了解到在IE5678下,document.all會返回一個類函數對象,也就是上文說到的帶有HTMLCollection特征的[object Object]對象。其實IE這一傳統一直延續到IE11,這就導致IE9、10、11下的HTMLCollection與W3C標準出現同名而不同性質的問題了。
何為類函數?
純屬本人私自定義而已,用于指那些擁有函數的特征,但instanceof Function卻返回false的對象。
真心想對IE說一句,你這么吊,你媽媽知道嗎?
從IE8開始就多了個querySelectorAll選擇器方法。具體行為如下:
// IE8返回 [object Object], IE9+和chrome、FF就返回[object NodeList] var nodes = document.querySelectorAll("*"); // IE8返回 空集合[object Object],IE9+和chrome、FF就拋至少是1個函數入參的異常 nodes = document.querySelectorAll(); // 各瀏覽器均拋SyntaxError異常 nodes = document.querySelectorAll("") 或 document.querySelectorAll(非字符串類型入參);
大家不要被瀏覽器返回的NodeList所蒙騙,其實querySelectorAll返回的是StaticNodeList對象。其特征與NodeList基本無異,唯一的區別就是StaticNodeList是不會實時同步DOM樹變化,因此在polyfill querySelectorAll的時候就不用考慮實時同步DOM樹變化的問題了。
五、HTMLOptionsCollection——HTMLCollection的子類HTMLSelectElement對象.options會返回一個HTMLOptionsCollection集合對象,集合內存儲HTMLOptionElement類型的元素。HTMLOptionsCollection類型除了父類HTMLCollection的特征外,還有如下成員方法、屬性可用。
add({HTMLOptionElement} opt[, {HTMLOption | Number} before]); // 將選項元素加入到集合的最后,或指定的元素(位置)的后面
remove({Number} index);// 刪除指定位置的選項
selectedIndex; // 當前選中項的索引,從0開始
HTMLFormElement對象.elements會返回一個HTMLFormControllersCollection集合對象,集合內存儲各種表單元素。它特別之處是通過點屬性獲取id或name匹配的元素時,一般的HTMLCollection集合對象在即使有多個匹配的元素的情況下,僅返回首個匹配的元素;而HTMLFormControllersCollection,在有一個匹配的元素時就返回該元素,若有多個匹配的元素則返回一個RadioNodeList集合對象。
七、RadioNodeList——NodeList的子類
初看RadioNodeList很有可能以為集合元素就是單選表單元素,其實RadioNodeList可以存儲任意類型的表單元素。不過其value屬性就值顯示其中被選中的單選項表單元素的value值,若沒有單選項表單元素,或沒有選中單選項表單元素,那么value值為空字符串。
八、HTMLAllCollection——HTMLCollection的子類IE11、Chrome開始,document.all將返回HTMLCollection子類HTMLAllCollection的對象,其行為特征和HTMLCollection一致。但IE11中的HTMLAllCollection還可以當作函數使用,具體請看本文的第三節。
九、NamedNodeMap——無序Attr元素集合HTMLElement對象.attributes會返回NamedNodeMap集合對象,內部保存的是[object Attr]類型的對象。NamedNodeMap和HTMLCollection、NodeList不同,因為它是無序集合,雖然可以通過數字類型的下標索引訪問NamedNodeMap集合中的元素,但該索引值并不真實代表元素在集合中的位置。下面是NamedNodeMap的成員方法:
[{String} 屬性名] item({Number | String} 索引) getNamedItem(); //通過名稱返回指定的屬性節點 getNamedItemNS(); //通過名稱和命名空間返回指定的屬性節點 setNamedItem(); //通過名稱設置指定的屬性節點 setNamedItemNS(); //通過名稱和命名空間設置指定的屬性節點 removeNamedItem(); //通過名稱刪除指定的屬性節點 removeNamedItemNS(); //通過名稱和命名空間刪除指定的屬性節點
注意:HTMLElement對象.attributes僅返回顯示屬性(簡單地說就是直接寫在html標簽上的屬性,或通過setAttribute設置的屬性,具體請看《JS魔法堂:不要再被Attribute和Property困擾我們了》)
十、DOMTokenList——HTML5新特性classList的類型哦!用過classList的都知道它大大提高了我們設置css類的效率,但IE10以下卻不支持,polyfill可以幫我們一把。但在polyfill前,我們應該先了解清楚classList的類型DOMTokenList的特征。
1. 只讀
2. 實時同步相應元素的className屬性值的變化
3. 擁有以下方法和屬性
{Undefined} add({String} class); // 已存在的類不會被重復添加 {Undefined} remove({String} class) {Undefined} toggle({String} class) {Boolean} contains({String} class); //檢查是否有指定的類 item({Number} 索引); //通過索引獲取指定位置的類 length; //表示類的個數 // 無法通過[{Number} 索引]的方式來設置類,只能通過該方式來獲取類
那么現在我們就著手polyfill吧,注意難點在實時同步這一塊,解決辦法就是用onpropertychange來監聽className的變化(想了解更多,請看《JS魔法堂:DOM世界的觀察者》)
function polyfillClassList(el){ var r = /s+/, cls = el.className, _inner = cls ? cls.trim().split(r) : []; var listener = function(e){ if (e.propertyName !== "className") return void 0; var cLst = el.classList, oLen = _inner.length, cls= el.className; _inner = cls ? cls.trim().split(r) : []; var len = (cLst.length = _inner.length); for (var i = 0, maxLen = Math.max(oLen, len); i < maxLen; ++i){ if (i < len){ cLst[i] = _inner[i]) } else { delete cLst[i]; } } }; el.attachEvent("onpropertychange", listener); el.classList = { length: _inner.length, item: function(index){ return _inner[index] || null; }, add: function(cls){ // 省略檢查cls值是否有效的代碼 if (this.contains(cls)) return void 0; el.detachEvent("onpropertychange", listener); el.className += " " + cls; _inner.push(cls); this[this.length++] = cls; el.attachEvent("onpropertychage", listener); }, remove: function(cls){ // 省略檢查cls值是否有效的代碼 if (!this.contains(cls)) return void 0; el.detachEvent("onpropertychange", listener); el.className = el.className.replace(new RegExp("" + cls + "", "i"), "").trim(); _inner.splice(_inner.indexOf(cls), 1); --this.length; el.attachEvent("onpropertychage", listener); }, toggle: function(cls){ // 省略檢查cls值是否有效的代碼 this[this.contains(cls) ? "remove" : "add"](cls); }, contains: function(cls){ // 省略檢查cls值是否有效的代碼 return el.className.search(new RegExp("" + cls + "", "i")) >= 0; }, toString: function(){ return _inner.toString(); } }; // 初始化classList[{Number} 索引]獲取Attr元素 for (var i = 0, len = _inner.length; i < len; ++i ){ el.classList[i] = _inner[i]; } }
由于當原生的add、remove、contains和toggle方法的入參值包含空格時,會拋出InvalidCharacterError,因此在polyfill時也要做相應的檢查和拋出異常
// 模擬InvalidCharacterError類 var InvalidCharacterError = function(msg){ this.code = 5; this.message = msg; this.name = "InvalidCharacterError"; }; InvalidCharacterError.prototype = DOMException; // 檢查入參并拋異常 // @param {String} methodName add、remove等方法名 // @param {String} cls css類 var check = function(methodName, cls){ var msgTpl = ["Failed to execute "", , "" on "DOMTokenList": The token provided ("", ,"") contains HTML space characters, which are not valid in tokens."]; if (/s+/.test(cls)){ throw new InvalidCharacterError((msgTpl[1] = methodName, msgTpl[3] = cls, msgTpl).join("")); } };
更多關于異常處理、Error和Exception的信息請留意《JS魔法堂:異常處理并不那么簡單》
十一、DOMStringMap類型——HTML5新特性dataset的類型哦!IE11開始支持 HTML5 JS API的dataset,它是就專門用來操作自定義特性(custom attribute,屬性的分類請看《JS魔法堂:特性、屬性,傻傻分不清楚》)的對象,其類型為DOMStringMap,從名稱可知其為字符串字典。下面結合dataset說明其特點吧,具體如下:
①. dataset針對以"data-"開頭的自定義特性操作;
②. 通過形如dataset.rawData獲取data-raw-data的屬性值;
③. 通過形如dataset.rawData = "hello world!"給data-raw-data的屬性賦值;
④. 通過形如delete dataset.rawData刪除屬性data-raw-data;
⑤. 通過for in 遍歷dataset的屬性;
⑥. 屬性值必須或將自動轉換為String類型;
⑦. 其實它就是除了setAttribute、getAttribute等操作自定義特性的另一個接口而已,而且效率比get/setAttribute低,但大大簡化操作代碼。
另外,JQuery中也有一個data函數,那么它跟以"data-"開頭的自定義特性有什么關聯呢?
html:,使用jquery-1.10.2
var $el = $("#div"), el = $el[0]; function log(){ console.log($el.data("raw")); console.log(el.dataset["raw"]); console.log(el.outerHTML); } log(); // 輸出: // raw // raw // $el.data("raw", "$"); log(); $el.data("raw", "raw"); // 輸出: // $ // raw // el.dataset.raw = "dataset"; log(); el.dataset.raw = "raw"; // 輸出: // raw // dataset // delete el.dataset.raw; log(); // 輸出: // raw // undefined // el.dataset.newRaw = "newRaw"; console.log($el.data("newRaw")); // 輸出newRaw
從上面的實例可知:
調用JQuery的data函數訪問屬性時,它會在庫內部的特性映射表中尋找同屬性名的鍵值對,沒有則采取與dataset相同的方式獲取屬性值,若成功則將在特性映射表中新建一個鍵值對,然后后續的訪問和賦值操作均僅僅針對該鍵值對。賦值操作時,僅僅在特性映射表中新建鍵值對,并不會賦值到標簽對應的"data-*"特性中。
為何JQuery要設計成這樣呢?因為dataset的自定義特性值必須為String類型,賦予其他類型時會發生隱式類型轉換,不便于暫存對象、數組等數據。JQuery這種算是折中的做法吧,所以用JQuery的data API操作自定義特性時最好不要跟dataset或get/setAttribute等原生API混合用咯。
本節參考:《HTML5自定義屬性對象Dataset 簡介》
十二、 總結其實DOM的集合又何止上述的這些呢,在后續的日子里我會邊學習邊完善本文的,謝謝收看!
如果您覺得本文的內容有趣就掃一下吧!捐贈互勉!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/85860.html
摘要:一前言圖片上傳是一個普通不過的功能,而圖片預覽就是就是上傳功能中必不可少的子功能了。偶然從上找到純前端圖片預覽的相關資料,經過整理后記錄下來以便日后查閱。類型為,表示在讀取文件時發生的錯誤,只讀。 一、前言 圖片上傳是一個普通不過的功能,而圖片預覽就是就是上傳功能中必不可少的子功能了。在這之前,我曾經通過訂閱input[type=file]元素的onchange事件,一旦更改路徑...
摘要:前言對于問題多多的,瀏覽器樣式閃爍是一個不可忽視的話題,但對于的瀏覽器就不用理會了嗎下面嘗試較全面地解密。示例說明,不管在哪里引入,在頁面的所有下載完成前,整個頁面將不會被渲染。 前言 ?對于問題多多的IE678,FOUC(flash of unstyled content)——瀏覽器樣式閃爍是一個不可忽視的話題,但對于ever green的瀏覽器就不用理會了嗎?下面嘗試較全面地解密F...
摘要:前言對于問題多多的,瀏覽器樣式閃爍是一個不可忽視的話題,但對于的瀏覽器就不用理會了嗎下面嘗試較全面地解密。示例說明,不管在哪里引入,在頁面的所有下載完成前,整個頁面將不會被渲染。 前言 ?對于問題多多的IE678,FOUC(flash of unstyled content)——瀏覽器樣式閃爍是一個不可忽視的話題,但對于ever green的瀏覽器就不用理會了嗎?下面嘗試較全面地解密F...
摘要:前言對于問題多多的,瀏覽器樣式閃爍是一個不可忽視的話題,但對于的瀏覽器就不用理會了嗎下面嘗試較全面地解密。示例說明,不管在哪里引入,在頁面的所有下載完成前,整個頁面將不會被渲染。 前言 ?對于問題多多的IE678,FOUC(flash of unstyled content)——瀏覽器樣式閃爍是一個不可忽視的話題,但對于ever green的瀏覽器就不用理會了嗎?下面嘗試較全面地解密F...
摘要:前言繼上篇魔法堂稍稍深入偽類選擇器記錄完偽類后,我自然而然要向偽元素伸出魔掌的啦。和的注意事項默認必須設置屬性,否則一切都是無用功默認,就是和的內容無法被用戶選中的偽元素和偽類結合使用形如。 前言 ?繼上篇《CSS魔法堂:稍稍深入偽類選擇器》記錄完偽類后,我自然而然要向偽元素伸出魔掌的啦^_^。本文講講述偽元素以及功能強大的Contet屬性,讓我們可以通過偽元素更好地實現更多的可能! ...
閱讀 1698·2023-04-26 01:02
閱讀 4841·2021-11-24 09:39
閱讀 1803·2019-08-30 15:44
閱讀 2872·2019-08-30 11:10
閱讀 1783·2019-08-30 10:49
閱讀 984·2019-08-29 17:06
閱讀 609·2019-08-29 16:15
閱讀 902·2019-08-29 15:17