文档对象模型 DOM
文档对象模型 (DOM) 是表示和操作 HTML 和 XML 文档内容的基础 API.
DOM树: HTML 文档的树状结构包含表示 HTML 标签或元素 (如 、
) 和表示文本字符串的节点.
利用家族图谱可以非常好的形容 HTML 的树状结构
- 在一个节点之上的直接节点是其父节点
- 在其下一层的直接节点是其子节点
- 在同一层上具有相同父节点的节点是兄弟节点
- 在一个节点之下的所有层级的一组节点是其后代节点
- 一个节点的父节点和其上层的所有节点是祖先节点
node 节点
每一个节点表示一个 node 对象
-
Document: 树形的根部是 Document 节点, 它代表整个文档.
-
Element: HTML 元素的节点是 Element 节点.
-
Text: 代表文本的节点是 Text 节点.
选取文档元素
大多数客户端 JavaScript 程序运行时总是在操作一个或多个文档元素.当这些程序启动时,可以使用全局变量 document 来引用 Document 对象.为了操作文档中的元素,我们必须通过某种方式选取元素
通过 ID 选取元素
任何 HTML 元素可以有一个 id 属性,在文档中该值必须唯一.
可以用 Document 对象的 getElementById()
方法选取一个基于 ID 的元素.
var logo = document.getElementById();
复制代码
通过名字选取元素
HTML 的 name 属性最初打算为表单元素分配名字,在表单数据提交到服务器时使用该属性的值.类似 id 属性,name 是给元素分配名字,但是区别于 id, name 属性的值不是必须唯一.
var newsList = document.getElementByName("news");
复制代码
返回一个 NodeList 对象数组
通过标签名选取元素
Document 对象的 getElementsByTagName()
方法可用来选取指定类型 (标签名) 所有 HTML 元素.
var pList = document.getElementsByTagName("p");
复制代码
类型于 getElementByName(), getElementsByTagName() 返回一个 NodeList 对象数组.
通过 CSS 类选取元素
基于 class 属性值中的标识符来选取成组的文档元素.
var cats = document.getElementsByClassName("cat");
复制代码
getElementsByclassName() 返回一个实时的 NodeList 对象数组
通过 CSS 选择器选取元素
我们通过 CSS 选择器,可以用来准确的描述文档中的若干或多组元素.
同样我们也可以通过 CSS 选择器的语法规则获取一个或一组 HTML 元素.
Document方法 querySelectorAll()
和 querySelector()
, 接受一个 CSS 选择器的字符串参数.
var newsList = document.querySelectorAll(".news");
复制代码
querySelectorAll() 方法返回的 NodeList 对象数组并不是实时的:它包含在调用时刻选择器所匹配的元素,但它并不更新后续文档变化.
区别于 querySelectorAll(), querySelector(), 它返回第一个匹配的元素如果没有匹配的元素返回 null
文档结构和遍历
一旦从文档中选取一个元素,有时需要查找文档中与之在结构上相关的部分(父亲、兄弟和子女).节点类型定义了遍历该树所需的属性.
作为节点树的文档
Document 对象、它的 Element 对象和文档中表示文本的 Text 对象都是 Node 对象.
Node 定义了以下属性:
-
parentNode, 该节点的父节点,或者针对类似 Document 对象应该是 null,因为它没有父节点.
-
childNodes, 只读的类数组对象(NodeList对象),它是该节点的子节点的实时表示.
-
firstChild、lastChild, 该节点的子节点的第一个和最后一个,如果该节点没有子节点则为 null.
-
nextSibling、previousSibling, 该节点的兄弟节点中的前一个和下一个.具有相同父亲节点的两个节点为兄弟节点.节点的顺序反映了它们在文档中出现的顺序.
-
nodeType, 该节点的类型. 9 代表 Document 节点, 1 代表 Element 节点, 3 代表 Text 节点, 8 代表 Comment 节点, 11 代表 DocumentFragemnt 节点.
-
nodeValue, Text 节点或 Comment 节点的文本内容.
-
nodeName, 元素的标签名,以大写形式表示.
作为元素树的文档
当将主要的兴趣点集中在文档中的元素上而非它们之间的文本上时,我们可以使用另一个更用用的 API. 它将文档看做是 Element 对象树.
属性
HTML 元素有一个标签和一组称为属性的名/值对组成. HTML 元素的属性值在代表这些元素的 HTMLElement 对象的属性(property) 中是可用的.
HTML 属性作为 Element 的属性
表示 HTML 文档元素的 HTMLElement 对象定义了读/写属性,它们映射了元素的 HTML 属性.
例如,查询一张图片的 URL, 可以使用表示元素的 HTMLElement 对象的 src 属性:
var image = document.getElementById("logo");
var logoUrl = image.src; // src 属性是图片的 URL
复制代码
同样,可以为一个
元素设置表单提交的属性:
var form = document.forms[0]; // 文档中第一个 form
form.action = "http://www.baidu.com";
form.method = "GET";
复制代码
有些 HTML 属性名在 JavaScript 中是保留字.对于这些属性,一般的规则是为属性名加前缀“html”.
例如, HTML 中的 for 属性(lable 元素) 在 JavaScript 中变为 htmlFor.
class 属性是一个例外,在 JavaScript 中它变为 classname.
var lable = document.getElementsByTagName("lable")[0];
lable.htmlFor === "name";
lable.classname = "form-name";
复制代码
获取和设置非标准 HTML 属性
Element 类型还定义了 getAttribute() 和 setAttribute() 方法来查询和设置非标准的 HTML 属性.
var image = document.images[0];
var width = parseInt(image.getAttribute("width"));
复制代码
对于 HTML 元素来说,属性名不区分大小写
Element 类型还定义了两个相关的方法, hasAttribute() 和 removeAttribute(), 它们用来检测命名属性是否存在和完全删除属性.
数据集属性
HTML5 中,任意以 “data-” 为前缀的小写的属性名字都是合法的.这些“数据集属性”将不会对其元素的表现产生影响,它们定义了一种标准、附加额外数据的方法,并不是在文档合法性上做出让步.
HTML5 还在 Element 对象上定义了 dataset 属性. 该属性指代一个对象,它的各个属性对应于去掉前缀的 data- 属性.带连字符的属性对应于驼峰命名法属性名: data-name-a 属性就变成 dataset.nameA 属性.
作为 Attr 节点的属性
对于 Element 对象, attributes 属性是只读的类数组对象,它代表元素的所有属性.attributes 对象是实时的.
document.body.attributes[0]; // <body> 元素的第一个属性
document.body.attributes.bgcolor // <body> 元素的 background 属性
document.body.attributes["onload"] // <body> 元素的 onload 属性
复制代码
元素的内容
- 内容是 HTML 字符串
- 内容是纯文本字符串
- 内容是一个 Text 节点、一个节点包含一个 Text 子节点的 Elemnt 节点和另一个 Text 节点.
作为 HTML 的元素内容
读取 Element 的 innerHTML 属性作为字符串标记返回那个元素的内容.在元素上设置该属性调用了 Web 浏览器的解析器,用新字符串内容的解析展现形式替换元素当前内容.
Web 浏览器很擅长解析 HTML,通常设置 innerHTML 效率非常高,甚至在指定的值需要解析时效率也是相当不错的.但注意,对 innerHTML 属性用 “+=”操作符重复添加一段内容通常效率低下,因为他要序列化又要解析.
作为纯文本的元素内容
有时需要查询纯文本形式的元素内容,或者在文档中插入纯文本(不必转译HTML标签中使用的尖括号和&符号).标准的方法是用 Node 的 textContent 属性来实现:
var para = document.getElementByTagName("p")[0];
var text = para.textContent;
para.textContent = "have a good time!";
复制代码
作为 Text节点的元素内容
当考虑元素的内容时,通常感兴趣的是它的 Text 节点.
创建、插入和删除节点
Document 类型定义了创建 Element 和 Text 对象的方法, Node 类型定义了节点树中插入、删除和替换的方法.
创建节点
创建新的 Element 节点可以使用 Document 对象的 createElement() 方法. 给方法传递标签名.
var newElement = document.createElement("DIV"); // html 不区分大小写
复制代码
Text 节点用类似的方法创建:
var newNode = document.createTextNode("text node content");
复制代码
复制已存在的节点.每个节点有一个 cloneNode() 方法来返回该节点的一个全新副本:
var div = document.getElementsByTagName("div")[0];
div.cloneNode(); // 默认浅复制
div.cloneNode(true); // 传递 true ,递归复制所有后代子节点
复制代码
插入节点
-
appendChild() 方法是在需要插入的 Elemnet 节点上调用的,它插入指定的节点使其称为那个节点的最后一个子节点.
-
insertBefore() 方法接受两个参数,第一个参数是带插入的新节点,第二个参数是已存在的节点,新节点插入到该节点的前面,如果传递 null 作为第二个参数,insertBefore() 的行为类似 appendChild().
删除和替换节点
- removeChild() 方法是从文档树中删除一个节点.不是在待删除的节点上调用,而是删除该节点的子节点.
假设删除 n 节点:
n.parentNode.removeChild(n);
复制代码
- replaceChild() 方法删除一个子节点并用一个新的节点取而代之.
n.parentNode.replaceChild(document.createTextNode("hello"), n);
复制代码
使用 DocumentFragment
DocumentFragment 是一种特殊的 Node, 它作为其他节点的一个临时容器.
DocumentFragment 的特殊指出在于它使得一组节点被被当做一个节点看待: 如果给 appendChild()、insertBefore() 或 replaceChild() 传递一个 DocumentFragment,其实是将该文档片段的所有子节点插入到文档中,而非片段本身.
与document相比,最大的区别是DocumentFragment 不是真实 DOM 树的一部分,它的变化不会触发 DOM 树的重新渲染,且不会导致性能等问题。
DocumentFragment 应用(MDN):
const list = document.querySelector('#list');
const fruits = ['Apple', 'Orange', 'Banana', 'Melon'];
const fragment = document.createDocumentFragment();
fruits.forEach(fruit => {
const li = document.createElement('li');
li.innerHTML = fruit;
fragment.appendChild(li);
});
list.appendChild(fragment);
复制代码