JavaScript HTML DOM
DOM stands for Document Object Model, 是一个独立于平台和语言的接口,允许程序和脚本动态获取和更新一个文档的内容、结构和样式。
W3C DOM 标注分为3种不同的部分:Core DOM,XML DOM 和 HTML DOM。
HTML DOM
HTML DOM 规定了如何获取、改变、添加或删除 HTML 元素。
在 DOM 中,所有 HTML 元素被定义为对象。编程接口就是每个对象的属性(你可以获取和设定的值,比如 innnerHTML,该属性可以用来获取、改变任何HTML元素)和方法(可执行的操作,如 getElementById()
)。
HTML DOM Document
当一个 HTML 文档加载入浏览器中,它就变成了一个 document
对象.
如果你想获取一个页面中的某个元素,always 从获取 document 对象开始。
第一个 HTML DOM Level 1(1998)定义了11个对象,对象集合和属性。下面列举一些:
属性 | 描述 | DOM Level |
---|---|---|
document.anchors | 返回所有包含一个 name 特性的 <a> 元素 | 1 |
document.body | 返回 <body> 元素 | 1 |
document.cookie | 返回文档的 cookie | 1 |
document.domain | 返回文档服务器的 domain nanme | 1 |
document.forms | 返回所有 <form> 元素 | 1 |
document.images | 返回所有 <img> 元素 | 1 |
document.links | 返回所有包含一个 href 特性的 <area> 和 <a> 元素 | 1 |
document.referrer | 返回 referrer(链接的文档) 的 URL | 1 |
document.title | 返回 <title> 元素 | 1 |
document.URL | 返回当前 HTML 文档的完整 URL,与 location.href 相似 | 1 |
document.baseURL | 返回文档的 absolute base URL | 3 |
document.doctype | 返回文档类型 | 3 |
document.documentElement | 返回 <html> 元素,即全部文档 | 3 |
document.documentMode | 返回浏览器渲染当前文档使用的模式,该属性是一个 IE only 的属性 | 3 |
document.documentURL | 返回文档的 URL,该属性可以适用于任何文档类型,而上面的 document.URL 只可以用在 HTML 文档 | 3 |
document.embeds | 返回所有 <embed> 元素 | 3 |
document.head | 返回 <head> 元素 | 3 |
document.implementation | 返回文档的 DOMimplementation 对象,目前不知道有什么用 | 3 |
document.inputEncoding | 返回文档的编码(character set) | 3 |
document.lastModified | 返回文档更新的 date 和 time | 3 |
document.readyState | 返回文档的 loading 状态:uninitialized(还没有开始下载)、loading、loaded、interactive(已经下载的足够用户可以与它交互)、complete(fully loaded) | 3 |
document.scripts | 返回所有 <script> 元素 | 3 |
document.strictErrorChecking | 目前浏览器都不支持 = = | 3 |
获取 HTML Element
常见的查找 HTML 元素的方法:
getElementById(
‘myEle’)
, 如果找到,返回该元素作为一个 object,没找到则返回 nullgetElementsByTagName(
‘p’)
, 以标签名查找。该方法返回一个 node list(像 array 一样的 nodes 集合),因此 nodes 可以使用 index number 像数组那样获取,也可使用 length 属性,获取 node list 长度。但是 node list 不是数组!不能使用数组的方法,比如valueOf()
或者join()
getElementsByClassName(
‘test’)
, 以 CSS 样式名查找。-
document.querySelectorAll( CSS selectors)
, 查找所有匹配一个特定 CSS 选择器的 HTML 元素。其中 CSS 选择器可以是由逗号分隔的 string。只返回第一个匹配的元素使用
document.querySelector(
CSS selectors)
,没找到返回 null - Element.closest(selectors),返回符合条件的当前元素最近的祖先元素,没找到返回 null
- 以 HTML Object Collections 查找元素,比如查找 id 为 frm1 的表单,
document.form['frm1']
, 其他可以获取的文档对象集合可参考上面的表格。
改变 HTML
使用 document.write()
,可以直接向 HTML 输出流写东西,但是要注意不要在文档 loaded 之后使用,这样会 overwrite 文档。
改变 HTML 元素内容最简单的方法就是使用 innerHTML 属性。
注意:HTML5 specifies that a <script>
tag inserted with innerHTML should not execute(通过此方法插入DOM的script标签不会执行!https://www.w3.org/TR/2008/WD-html5-20080610/dom.html#innerhtml0)
改变一个 Attribute 特性值的方法有 2 种:
element.attribute = new value
element.setAttribute(attribute, value)
改变 HTML 元素的样式可以使用 HTML DOM Style 对象:
element.style.property = new style
上面的 property 就是 CSS 属性名,对于使用 -
连字符的属性名,用法为 paddingTop
DOM EventListener
element.addEventListener(event, function, useCapture)
方法用来给指定元素绑定一个事件处理函数。最后一个参数是可选的 boolean 值,用来指定是否使用事件冒泡或事件捕捉。默认值是 false,所以默认使用 bubbling 冒泡传播。(在 bubbling 阶段,inner most 元素的事件先被处理,然后依次向外。而 capturing 阶段则相反,outer most 元素的事件先被处理)
- 可以对同一个元素,添加多个不同类型事件的 event handle
- 可以对一个元素添加多个相同类型的事件处理函数,比如,两个 click 事件处理,without overwriting existing codes
- 可以给任何 DOM 元素添加事件监听器,包括 window 对象
- 使用
addEventListener()
方法可以实现 JS 代码和 HTML 内容分离 - 使用
element.removeEventListener(event, function)
方法,移除事件监听器
给事件处理函数传递参数
使用匿名函数来调用指定事件处理函数,将需要传递的参数作为指定函数的实参。
element.addEventListener("click", function(){ myFunction(p1, p2); });
DOM Nodes
根据 HTML DOM标准,在 HTML 文档中所有东西都是一个 node 节点。每一个 HTML element 是一个 element node 元素节点,元素里的 text 是 text node 文本节点,每一个 HTML attribute 是一个 attribute node 特性节点,所有 comment 则是 comment node 注释节点。
Node relationships
除了 <html>
元素,每个节点都有唯一的父节点。可以使用以下的节点属性,通过 JS 在节点之间 navigating。
- parentNode,父节点
- childNodes[nodenumber],孩子节点列表
- firstChild,第一个子节点
- lastChild,最后一个子节点
- previousSibling,前一个相邻节点
- nextSibling,后一个相邻节点
Node Value
nodeValue 对于元素节点,是 undefined;对于文本节点,就是文字本身;对于属性节点,就是特性值。
注意:元素包含的文字内容是 text node,而不是 text。
获取文本节点的值,除了使用 .innerHTML,还可以组合使用 childNodes 和 nodeValue.
<p id="intro">My first page.</p>
var myText = document.getElementById('intro').childNodes[0].nodeValue;
// 或者
var myText = document.getElementById('intro').firstChild.nodeValue;
nodeName 和 nodeType
nodeName 是只读的,元素节点的 nodeName 与 tag 名字相同,特性节点就是 attribute 名字,文本节点的 nodeName 总是 #text
,而文档节点则是 #document
。
nodeType 也是只读的:Element, Attribute, Text, Comment, Document.
Nodes 操作
常见的操作如添加和删除。
- 添加操作可以使用
createElement(
tagname)
,createTextNode(
string)
,appendChild(
node)
,parentNode.insertBefore(
newNode, existingNode)
- 删除操作使用
parent.removeChild(
child)
。虽然删除元素如果不需要 referring 父元素的话会更好,但是 DOM 操作需要知道你想删掉的元素和它的父元素。常用的方法是child.parentNode.removeChild(
child)
- 替换操作使用
parent.replaceChild(
newchild, oldchild)
ChildNode.remove()
刪除元素所在的树结构
// html
<div id="div-01">Here is div-01</div>
<div id="div-02">Here is div-02</div>
// js
var el = document.getElementById('div-02');
el.remove(); // Removes the div with the 'div-02' id
DocumentFragments
DocumentFragment
是DOM节点,但不属于主DOM树,而存在于memory中。
用法:创建document fragment(简称DF),在DF后append子DOM元素,然后把DFappend到DOM树,最终DF被其所有的子元素替换(即不会渲染出单独的html节点)。
因为append子元素到DF不会引起页面reflow,所以使用DF会有更好的performance。
性能优化
- 不要逐条地修改 DOM 的样式。使用预先定义好css class 名称,然后修改 DOM 的 className。
// bad
var left = 10,
top = 10;
el.style.left = left + "px";
el.style.top = top + "px";
// Good
el.className += " theclassname";
// Good
el.style.cssText += "; left: " + left + "px; top: " + top + "px;";
- 离线修改DOM
- 使用 documentFragment 对象在内存里操作DOM
- 把需要频繁改动的 DOM 给 display:none,(涉及一次 reflow),进行 DOM 操作,最后再显示出来
- 不要把 DOM 结点的属性值作为一个循环里的变量。否则导致大量地读写这个结点的属性
- 对含有动效的 HTML 元素的 position 设为 fixed 或 absoult,这样修改他们的 CSS 不会引起 reflow