菜单

从原型链看DOM,返本求源

2019年10月30日 - 前端排行
从原型链看DOM,返本求源

返本求源——DOM元素的特性与属性

2015/09/06 · HTML5,
JavaScript ·
DOM

原文出处: 木的树   

抛砖引玉

很多前端类库(比如dojo与JQuery)在涉及dom操作时都会见到两个模块:attr、prop。某天代码复查时,见到一段为某节点设置文本的代码:

JavaScript

attr.set(node, ‘innerText’, ‘Hello World!’)

1
attr.set(node, ‘innerText’, ‘Hello World!’)

这段代码执行后并未生效,虽说innerText不是标准属性,尚未被ff支持,可用的是chrome,这个属性是被支持的。既然显示的文本没变,那就查看一下元素吧。
图片 1

innerText被添加到了html标签上,而换成prop模块后,成功的为节点替换文本。

以上的这个小案例就涉及到了DOM操作时常常被忽略的一个问题:特性与属性的区别

返本求源

在DOM中,特性指的是html标签上的属性,比如:

图片 2

Property是对于某一类型特征的描述。可以这样理解,在DOM元素中可以通过点语法访问,又不是标准特性的都可以成为属性。

DOM中所有的节点都实现了Node接口。Node接口是在DOM1级中定义的,其中定义了一些用来描述DOM节点的属性和操作方法。

图片 3

常见的nodeType、nodeValue、节点关系(parentNode、childNodes、firstChild、lastChild、previousSibling、nextSibling等)都属于Node接口定义的属性。对于Node接口的具体实现者,HTMLElement不仅继承了这些属性,还拥有五个wac规范中的五个标准特性:id、title、lang、dir、class和一个属性:attributes。

每一个元素都有一个或多个特性,这些特性的用途是给出相应元素或其内容的附加信息。通过DOM元素直接操作特性的的方法有三个:

这三个方法都可以操作自定义特性。但是只有公认的(非自定义)特性才会以属性的形式添加到DOM对象中,以属性方式操作这些特性会被同步到html标签中。HTMLElement的五个特性都有相应属性与其对待:id、title、lang、dir、className。在DOM中以属性方式操作这几个特性会同步到html标签中。

不过,HTML5规范对自定义特性做了增强,只要自定义特性以”data-attrName”的形式写入到html标签中,在DOM属性中就可以通过element.dataset.attrName的形式来访问自定义特性,如:

XHTML

<input type=​”text” name=​”as_q” class=​”box”
id=​”searched_content” title=​”在此输入搜索内容。” disabled=​”false”
data-ff=​”fsdf”>​ seh.dataset.ff

1
2
<input type=​"text" name=​"as_q" class=​"box" id=​"searched_content" title=​"在此输入搜索内容。" disabled=​"false" data-ff=​"fsdf">​
seh.dataset.ff

元素的特性在DOM中以Attr类型来表示,Attr类型也实现了Node接口。Attr对象有三个属性:name、value、specified。其中,name是特性的名称,value是特性值,specified是一个布尔值,用来指示该特性是否被明确设置。

document.createAttribute方法可以用来创建特性节点。例如,要为元素添加align特性可以使用如下方法:

JavaScript

ar attr = document.createAttribute(‘align’) attr.value = ‘left’
seh.setAttributeNode(attr)

1
2
3
ar attr = document.createAttribute(‘align’)
attr.value = ‘left’
seh.setAttributeNode(attr)

要将新创建的特性添加到元素上,必须使用元素的setAttributeNode方法。添加特性后,特性会反映在html标签上:

图片 4

注意,尽管特性节点也实现了Node接口,但特性却不被认为是DOM文档树的一部分。

在所有的DOM节点中attributes属性是Element类型所独有的的属性。从技术角度来说,特性就是存在于元素的attributes属性中的节点。attributes属性属于NamedNodeMap类型的实例。元素的每一个特性节点都保存在NamedNodeMap对象中。NamedNodeMap类型拥有如下方法:

获取、设置、删除元素节点可以如下方式:

JavaScript

element.attributes.getNamedItem(‘align’) //获取 var attr =
document.createAttribute(‘align’); attr.value = ‘right’;
element.attributes.setNamedItem(attr); //添加
element.attributes.removeNamedItem(‘align’); //删除

1
2
3
4
5
6
7
element.attributes.getNamedItem(‘align’) //获取
 
var attr = document.createAttribute(‘align’);
attr.value = ‘right’;
element.attributes.setNamedItem(attr); //添加
 
element.attributes.removeNamedItem(‘align’); //删除

实际应用中并不建议使用特性节点的方式,而getAttribute、setAttribute、removeAttribute方法远比操作特性节点更方便。

DOM、attributes、Attr三者关系应该这么画:

图片 5

应用总结

基于以上DOM基础知识和实际工作经验,我将特性和属性的区别联系总结如下:

  1. 属性以及公认特性可以通过点语法访问;html5规范中,data-*形式的自定义特性可以通过element.dataset.*的形式来访问,否则用getAttribute
  2. 特性值只能是字符串,而属性值可以是任意JavaScript支持的类型
  3. 几个特殊特性:
    1. style,通过getAttrbute和setAttribute来操作这个特性只能得到或设置字符串;而已属性方式来操作就是在操作CSSStyleDeclaration对象
    2. 事件处理程序,通过特性方式得到和传递的都只是函数字符串;而已属性方式操作的是函数对象
    3. value,对于支持value的元素,最好通过属性方式操作,而且操作不会反映在html标签上
    XHTML

    seh.value = 10 &lt;input type="text" name="as\_q" class="box"
    id="searched\_content" title="在此输入搜索内容。"
    disabled="false" data-ff="fsdf" align="left"&gt;

    <table>
    <colgroup>
    <col style="width: 50%" />
    <col style="width: 50%" />
    </colgroup>
    <tbody>
    <tr class="odd">
    <td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
    <div class="crayon-num" data-line="crayon-5b8f379c97d12396477911-1">
    1
    </div>
    <div class="crayon-num crayon-striped-num" data-line="crayon-5b8f379c97d12396477911-2">
    2
    </div>
    </div></td>
    <td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
    <div id="crayon-5b8f379c97d12396477911-1" class="crayon-line">
    seh.value = 10
    </div>
    <div id="crayon-5b8f379c97d12396477911-2" class="crayon-line crayon-striped-line">
    &lt;input type=&quot;text&quot; name=&quot;as_q&quot; class=&quot;box&quot; id=&quot;searched_content&quot; title=&quot;在此输入搜索内容。&quot; disabled=&quot;false&quot; data-ff=&quot;fsdf&quot; align=&quot;left&quot;&gt;
    </div>
    </div></td>
    </tr>
    </tbody>
    </table>

4.  href,通过属性方式设置可以反映到html标签上,但用过点语法和getAttribute能够取到的值并不一定相同


    XHTML

    &lt;a href="/jsref/prop\_checkbox\_tabindex.asp"
    id="tabI"&gt;tabIndex&lt;/a&gt; link.getAttribute('href') //
    "/jsref/prop\_checkbox\_tabindex.asp" link.href //
    "http://www.w3school.com.cn/jsref/prop\_checkbox\_tabindex.asp"

    <table>
    <colgroup>
    <col style="width: 50%" />
    <col style="width: 50%" />
    </colgroup>
    <tbody>
    <tr class="odd">
    <td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
    <div class="crayon-num" data-line="crayon-5b8f379c97d15903857159-1">
    1
    </div>
    <div class="crayon-num crayon-striped-num" data-line="crayon-5b8f379c97d15903857159-2">
    2
    </div>
    <div class="crayon-num" data-line="crayon-5b8f379c97d15903857159-3">
    3
    </div>
    <div class="crayon-num crayon-striped-num" data-line="crayon-5b8f379c97d15903857159-4">
    4
    </div>
    <div class="crayon-num" data-line="crayon-5b8f379c97d15903857159-5">
    5
    </div>
    </div></td>
    <td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
    <div id="crayon-5b8f379c97d15903857159-1" class="crayon-line">
    &lt;a href=&quot;/jsref/prop_checkbox_tabindex.asp&quot; id=&quot;tabI&quot;&gt;tabIndex&lt;/a&gt;
    </div>
    <div id="crayon-5b8f379c97d15903857159-2" class="crayon-line crayon-striped-line">
     
    </div>
    <div id="crayon-5b8f379c97d15903857159-3" class="crayon-line">
    link.getAttribute('href') // &quot;/jsref/prop_checkbox_tabindex.asp&quot;
    </div>
    <div id="crayon-5b8f379c97d15903857159-4" class="crayon-line crayon-striped-line">
     
    </div>
    <div id="crayon-5b8f379c97d15903857159-5" class="crayon-line">
    link.href // &quot;http://www.w3school.com.cn/jsref/prop_checkbox_tabindex.asp&quot;
    </div>
    </div></td>
    </tr>
    </tbody>
    </table>

5.  disabled和checked,对于支持这两个特性的元素来说,他们在html标签中都是无状态的,只要有独立的标签属性在以点语法访问时就返回true,如果html标签属性不存在,则以点语法访问时就是false


    XHTML

    &lt;input type=​"text" name=​"as\_q" class=​"box"
    id=​"searched\_content" title=​"在此输入搜索内容。"
    disabled=​"false" data-ff=​"fsdf" align=​"left"&gt;​
    seh.disabled // true seh.disabled = false &lt;input type=​"text"
    name=​"as\_q" class=​"box" id=​"searched\_content"
    title=​"在此输入搜索内容。" data-ff=​"fsdf" align=​"left"&gt;​

    <table>
    <colgroup>
    <col style="width: 50%" />
    <col style="width: 50%" />
    </colgroup>
    <tbody>
    <tr class="odd">
    <td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
    <div class="crayon-num" data-line="crayon-5b8f379c97d19172676562-1">
    1
    </div>
    <div class="crayon-num crayon-striped-num" data-line="crayon-5b8f379c97d19172676562-2">
    2
    </div>
    <div class="crayon-num" data-line="crayon-5b8f379c97d19172676562-3">
    3
    </div>
    <div class="crayon-num crayon-striped-num" data-line="crayon-5b8f379c97d19172676562-4">
    4
    </div>
    <div class="crayon-num" data-line="crayon-5b8f379c97d19172676562-5">
    5
    </div>
    <div class="crayon-num crayon-striped-num" data-line="crayon-5b8f379c97d19172676562-6">
    6
    </div>
    </div></td>
    <td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
    <div id="crayon-5b8f379c97d19172676562-1" class="crayon-line">
    &lt;input type=​&quot;text&quot; name=​&quot;as_q&quot; class=​&quot;box&quot; id=​&quot;searched_content&quot; title=​&quot;在此输入搜索内容。&quot; disabled=​&quot;false&quot; data-ff=​&quot;fsdf&quot; align=​&quot;left&quot;&gt;​
    </div>
    <div id="crayon-5b8f379c97d19172676562-2" class="crayon-line crayon-striped-line">
     
    </div>
    <div id="crayon-5b8f379c97d19172676562-3" class="crayon-line">
    seh.disabled // true
    </div>
    <div id="crayon-5b8f379c97d19172676562-4" class="crayon-line crayon-striped-line">
     
    </div>
    <div id="crayon-5b8f379c97d19172676562-5" class="crayon-line">
    seh.disabled = false
    </div>
    <div id="crayon-5b8f379c97d19172676562-6" class="crayon-line crayon-striped-line">
    &lt;input type=​&quot;text&quot; name=​&quot;as_q&quot; class=​&quot;box&quot; id=​&quot;searched_content&quot; title=​&quot;在此输入搜索内容。&quot; data-ff=​&quot;fsdf&quot; align=​&quot;left&quot;&gt;​
    </div>
    </div></td>
    </tr>
    </tbody>
    </table>

1 赞 1 收藏
评论

图片 6

Element类型用于表现XML或HTML元素,提供对元素标签名,子节点及特性的访问。原型链的继承关系为
某节点元素.__proto__->(HTML某元素Element.prototype)->HTMLElement.prototype->Element.prototype->Node.prototype->EventTarget.prototype。
以HTML元素为例:document.documentElement.__proto__->HTMLHtmlElement.prototype->HTMLElement.prototype->Element.prototype->Node.prototype->EventTarget.prototype
图片 7
图片 8

 

Element节点实例有以下特征:以下特征均继承自Node.prototype

要访问元素标签名,可以用nodeName(继承自Node.prototype)属性也可用tagName(继承自Element.prototype)属性,这两个属性会返回相同的值。但注意返回的字符串是大写。在HTML中标签名始终以全部大写表示,而在XML中(有时也包括XHTML)标签名则始终会与源码中的保持一致。假如你不确定自己的脚本将会在HTML还是XML中执行,最好在比较之前进行大小写转化。

document.documentElement.tagName;// "HTML"
document.documentElement.nodeName;// "HTML"
document.documentElement.nodeName.toLowerCase();// "html"

 

目录

 

HTML元素

HTML元素的五种标准特性(ele.attributes[index或’属性’]或ele.getAttributeNode(‘属性’)得到特性节点),可以取得或修改。
(1).id:继承自Element.prototype,元素在文档中唯一的标识符。 document.body.id;//
“Posts” 
(2).className:继承自Element.prototype,与元素的class特性对应,即为元素指定的css类。没有将这个属性命名为class,是因为class是ECMAScript的保留字。 document.forms[0].getElementsByTagName(‘div’)[0].className;//
“aspNetHidden” 

(3).title:继承自HTMLElement.prototype。有关元素的附加说明信息,一般通过工具提示条显示出来。
(4).lang:继承自HTMLElement.prototype。元素内容的语言代码, document.documentElement.lang;//
“zh-cn” 
(5).dir:继承自HTMLElement.prototype。语言的方向,值为”ltr”(从左至右)或”rtl”(从右至左)。是规定语言内容的文本方向不是文字顺序颠倒。注意一点,应用dir=”rtl”后虽然对文本整体是方向性的改变,但对标点符号和文本整体却做了颠倒。其实很好理解,这个属性是规定语言的方向,从右向左读,句号肯定在读的顺序的最后也就是左边。在换行的时候还是从截断的文本整体偏向右侧。
图片 9

<div id="myID" class="bd" title="body" lang="en" dir="ltr">...</div>

并不是对所有属性的修改都能直观在页面上表现出来。
对id或lang的修改对用户而言是透明不可见的;
对title的修改只会在鼠标移动到这个元素上时才显示出来;
对dir的修改会在属性被重写的那一刻立即影响页面中文本左右对齐方式;
修改className时,如果新类关联了与此前不同的CSS样式那么就会立即应用该样式;
关于了解所有HTML元素以及与之关联的原型类型的构造器可参考高程三P263,有的元素是直接继承自HTMLElement.prototype比如b元素,有的是继承自HTML某元素Element.prototype,比如a元素,它的原型属性指向HTMLAnchorElement.prototype。
(6).attributes:继承自Element.prototype。返回一个NamedNodeMap的实例对象。
这里扩展了解一下NamedNodeMap接口,原型链继承关系为:ele.attributes.__proto__->NamedNodeMap.prototype->Object.prototype。NamedNodeMap接口表示属性节点对象的集合,尽管NamedNodeMap里面的对象可以像数组一样通过索引进行访问但它和NodeList不一样,对象的顺序没有指定。NamedNodeMap集合是即时更新的,因此如果它内部包含的对象发生改变的话,该对象会自动更新到最新状态。
图片 10

图片 11

 

取得特性

(1).每个元素都有一个或多个特性,这些特性的用途是给相应元素或其内容附加信息。元素继承自Element.prototype上的三个属性,它们的功能是操作特性(不是属性)的方法:

这三个方法都可操作自定义特性,但只有公认的特性才能被应用以属性的形式添加到DOM对象中,以属性方式操作这些特性会被同步到html标签中。HTMLElement的5个特性都有相应属性(意思是Element.prototype或HTMLElement.prototype上的属性可直接通过.形式访问)与其对应:id,title,lang,dir,className。在DOM中以属性方式操作这几个特性会同步到html标签中。因为class特性是这5种特性之一,可以通过className属性访问,xsf特性不在Element.prototype或HTMLElement.prototype对象中有对应属性所以通过属性访问方式获取的值为undefiend。要想访问xsf特性值可以通过getAttribute(‘xsf’)(推荐)或getAttributeNode(‘xsf’).value或attributes[“xsf”].value访问。
图片 17
(2).当然元素还能通过继承HTML某元素Element.prototype上的一些属性,比如input元素的HTMLInputElement.prototype上的disabled可以通过inputele.disabled取得或设置值。inputele.disabled;//
false表示该元素未被设置disabled属性即未被禁用,inputele.disabled=true;//
表示为该元素设置不可用属性。

图片 18

(3).HTML5规范对自定义特性做了增强,只要自定义特性以’data-attrName’形式写到html标签中(setAttribute或直接html代码存在均可),在DOM属性中就可通过ele.dataset.attrName形式访问自定义特性。
图片 19

dataset属性继自HTMLElement.prototype,它的值是DOMStringMap的实例集合,原型链继承关系为:ele.dataset.__proto__->DOMStringMap.prototype->Object.prototype。
图片 20
(4).特性名是不区分大小写的,getAttribute(‘id’)和getAttribute(‘ID’)都代表同一个特性。
介绍两个特殊的特性:它们虽然有对应的属性名,但属性的值与通过getAttribute()返回的值并不相同。style属性继承自HTMLElement.prototype,on事件处理属性继承自HTMLElement.prototype或Element.prototype。
(a).style:用于通过css为元素指定样式。
通过getAttribute()访问返回的style特性值(在标签中定义的)中包含CSS文本
通过style属性访问返回一个CSSStyleDeclaration类型集合对象,由于style属性是用于以编程方式访问访问元素样式的因此并没有直接映射到style特性。该css属性来自标签中被设置的style特性。
图片 21
并没有background特性值,可以看到不论通过哪个方式获得的结果都只有元素上style特性设置的属性才会出现。
通过style属性返回了一个CSSStyleDeclaration类型实例集合,原型链继承关系为:div.style.__proto__->CSSStyleDeclaration.prototype->Object.prototype
图片 22
获得的集合中的属性只有已设置的才有值,其余的属性为空字符串即使它们都有默认值。
图片 23
简单学习下CSSStyleDeclaration接口:代表css键值对的集合,它在一些API中被使用

ele.style.cssText:声明块的文本内容,修改这个属性会直接将标签中的style特性内容改变。
ele.style.length:属性的数量即有具体值的css声明的数量。window.getComputedStyle()返回值为262。
ele.style.parentRule:包含的CSSRule;
ele.style.getPropertyPriority(‘attr’):返回可选的优先级
ele.style.getPropertyValue(‘attr’):返回属性值
ele.style.item(idx):返回属性名,有具体的值的返回属性名,没有具体值的返回””
ele.style.removeProperty():删除的属性,会直接反映到HTML文档中元素style特性节点。返回””
ele.style.setProperty(‘attr’,’value’,’priority’):设置属性,eg: document.body.style.setProperty(‘color’,’red’,’important’) 
(b).on事件属性:以onclick为例,在元素上使用时,onclick特性中包含的是JavaScript代码,但通过getAttribute()访问返回相应代码的字符串。在访问onclick属性时会返回一个JavaScript函数(当onclick属性上不存在函数对象且未在元素标签中指定相应特性(为什么说不是属性是因为如果在ele上没有找到onclick属性那就去标签中找onclick特性值)返回null)。由于存在这些差异,在通过JavaScript以编程方式操作DOM时建议使用onclick属性值,只有在取得自定义特性值的情况下才通过getAttribute()访问。
当一个元素标签中既有onclick特性,同时给ele.onclick指定函数(这操作并不会影响原来标签中onclick特性的值),则最后只执行ele.onclick属性的函数。
图片 24
并且通过getAttribute访问仍得到的是标签上的特性值,且即使之前已经给onclick属性赋值了但控制台显示元素自身并没有这个属性。我不明白为什么document.body自身上会没有onclick属性,那当访问document.body.onclick时候去哪访问onclick的值,按着原型链吗?如果函数是在原型链上的onclick属性上也不应该啊HTMLElement.prototype.onclick=function(){console.log(2)}这样不就使这个方法成共享的了任何HTML元素实例都能访问,这显然不实际因为我们只想为某一元素设置某事件函数。对于document.body自身上会没有onclick属性不知道是不是JS引擎内部实现的,如果是那具体是怎么实现的?知道的盆友麻烦告知~

document.body.getAttribute('onclick');// "(function(){console.log(1)})()"
document.body.onclick;// function (){
  console.log(2)
}
document.body.hasOwnProperty('onclick');// false

(c).在<=IE7通过getAttribute()方法访问style特性和onclick这样的事件处理程序特性时,返回的值与属性的值相同。即getAttribute(‘style’)返回的不是字符串而是对象,getAttribute(‘onclick’)返回的不是字符串而是函数。虽然IE8已修复该bug但不同版本的不一致性还是建议使用属性访问HTML特性。
一道面试题:下列关于IE,FF下面脚本的区别描述错误的是:感觉这道怪怪的考的是早期支持情况??
A.innerText IE支持,FF不支持  FF早期不支持
B.document.createElement FF支持,IE不支持 X
C.setAttribute(‘class’,’styleclass’)FF支持,IE不支持   IE早期不支持
D.用setAttribute设置事件FF不支持,IE支持 X
IE:all支持innerText  >IE8支持setAttribute设置特性或事件
FF:新版本支持,旧版本不支持outerHTML outerText
innerText;setAttribute支持

设置特性

setAttribute(‘attr’,’value’):继承自Element.prototype。参数为要设置的特性名和值,如果特性已经存在,setAttribute()会以指定值替换现有值,如果特性不存在,setAttribute会创建该属性并设置相应值。
通过该方法可以操作HTML特性也可以操作自定义特性,通过这个方法设置的特性名会被统一转换为小写形式即”ID”转换为”id”。
因为所有特性都是属性,所以直接给属性赋值就可以设置特性的值,但通过添加自定义属性并不会成为该元素的特性这样实际上为元素自身添加了属性。

document.body.id="test";
document.body.getAttribute('id');// "test"
document.body.hasOwnProperty('id');//false

document.body.myid="test";
document.body.getAttribute("myid");// null
document.body.hasOwnProperty('myid');//true

注:<=IE7中,setAtttribute()存在一些异常行为不但通过setAttribute()设置元素基本特性或事件特性没用而且通过点赋值法设置元素属性也不会反应到元素标签中。尽管到IE8才解决这个bug,但还是推荐用点赋值法设置特性。
removeAttribute():继承自Element.prototype,可以彻底删除元素特性,不仅会清除特性值还会从元素中完全删除特性。该方法不常用,但在序列化DOM元素时,可以通过它来确切指定要包含哪些特性。

attributes属性

Element类型是使用attributes属性的唯一一个DOM节点类型,attributes属性继承自Element.prototype。它的值是NamedNodeMap类型实例,也是个动态集合,元素的每一个特性都由一个Attr类型节点表示,每个节点都保存在NamedNodeMap对象中。

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图