摘要:五的子类对象会返回一个集合对象,集合内存储类型的元素。七的子类初看很有可能以为集合元素就是单选表单元素,其实可以存储任意类型的表单元素。八的子类开始,将返回子类的对象,其行为特征和一致。但在前,我们应该先了解清楚的类型的特征。
一、前言
大家先看看下面的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的集合又何止上述的这些呢,在后续的日子里我会边学习边完善本文的,谢谢收看!
如果您觉得本文的内容有趣就扫一下吧!捐赠互勉!
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/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属性,让我们可以通过伪元素更好地实现更多的可能! ...
阅读 1697·2023-04-26 01:02
阅读 4838·2021-11-24 09:39
阅读 1802·2019-08-30 15:44
阅读 2872·2019-08-30 11:10
阅读 1782·2019-08-30 10:49
阅读 983·2019-08-29 17:06
阅读 608·2019-08-29 16:15
阅读 902·2019-08-29 15:17