介绍
文档对象模型(DOM,Document Object Model)是 HTML 和 XML 文档的编程接口。
脱胎于网景和微软早期的动态 HTML(DHTML,Dynamic HTML),DOM 现在是真正跨平台、语言无关的表示和操作网页的方式。
简单的说,DOM,就是帮助你通过JS,去操作HTML元素,如
改变页面中的所有 HTML 元素、HTML 属性;
改变页面中的所有 CSS 样式;
添加、删除 HTML 元素和属性;
对页面中所有已有的 HTML 事件作出反应;
在页面中创建新的 HTML 事件;
等等
DOM架构设计
================
EventTarget 事件对象
主要有3个方法,所有DOM里面的对象都继承这个对象,因此大家都有这三种方法可以调用
MDN文档:https://developer.mozilla.org/zh-CN/docs/Web/Events
Node 节点对象
所有节点,都继承自 Node节点对象,因此都有节点的属性和方法,
如nodeName 节点名、firstChild 第一个子节点等等属性;
如appendChild( ) 添加子节点方法等等;
可以查看MDN文档,不用刻意去记,但是要知道哪里可以查到:https://developer.mozilla.org/zh-CN/docs/Web/API/Node
Document 网页对象
说明
Document节点表示的整个载入的网页,如下图,所有整个都是 Document节点,也就是标签内的所有元素。
每个文档只能有一个标签,所有的元素都是以树型的数据结构,他们的根就是标签。
MDN文档:https://developer.mozilla.org/zh-CN/docs/Web/API/Document
document 是 window对象的属性,因此是一个全局对象,可以直接用document ,而不用 window.document 。
常见属性
1、文档标题(浏览器标签标题)
2、head标签 和 body标签
3、子元素合集,只要是合集,都是HTMLCollection类型,类似数组
4、也有location属性,就是当前文档输入的链接url,和window (BOM)的是同一个true
常用方法
1、创建元素
本质上上面两种方式是一样的,第一个方式内部也是帮你new 一个元素
2、获取元素
这里简单介绍,详细操作见下
原生操作DOM
https://www.yuque.com/yejielin/mypn47/zr9hho#Ge1OJ
现在比较少原生操作DOM,一个是API比较长且多,不好操作,后来是通过JQuery,现在主要是通过前端三大框架(Vue、React、Angular)帮我们操作
Element 元素对象
说明
我们平时创建的div、p、span等元素在DOM中表示为Element元素对象,有对应的属性和方法
MDN文档,不用可以记,但是要知道在哪里可以查:https://developer.mozilla.org/zh-CN/docs/Web/API/Element
常用属性和方法
1、元素属性操作
//<div id="myDiv" my_special_attribute="hello!"></div> 自定义一个属性
let div = document.getElementById("myDiv");
// ============ 1、获取属性 ============
// 这种只能获取公认的 DOM 对象的属性,不能获取自定义属性
let id = div.id; // myDiv
// 主要可以获取自定义的属性
let value = div.getAttribute("my_special_attribute"); // hello!
// 获取属性集合,该集合是一个 NamedNodeMap 对象,不是一个数组,所以它没有 数组 的方法
let divAttr = div.attributes;
for(var i=divAttr.length-1; i>=0; i--) { // 可以遍历获取属性和值
console.log(divAttr[i].name + " : " + divAttr[i].value;)
}
// ============ 2、设置属性 ============
// 如果属性已经存在,则 setAttribute()会以指定的值替换原来的值;
// 如果属性不存在,则 setAttribute()会以指定的值创建该属性。
div.setAttribute("my_special_attribute", "123123");
// ============ 3、删除属性 ============
div.removeAttribute("my_special_attribute");
3、元素后代 childNodes
childNodes属性包含元素所有的子节点,这些子节点可能是其他元素、文本节点、注释或处理指令。
不同浏览器在识别这些节点时的表现有明显不同。
<ul id="myList">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
<!--
在解析以上代码时,<ul>元素会包含 7 个子元素,其中 3 个是<li>元素,还有 4 个 Text 节点(表示<li>元素周围的空格)。
如果把元素之间的空格删掉,变成下面这样,则所有浏览器都会返回同样数量的子节点:
-->
<ul id="myList"><li>Item 1</li><li>Item 2</li><li>Item 3</li></ul>
考虑到这种情况,通常在执行某个操作之后需要先检测一下节点的 nodeType
for (let i = 0, len = element.childNodes.length; i < len; ++i) {
if (element.childNodes[i].nodeType == 1) { // nodeType == 1是Element 元素节点
// 执行某个操作
}
}
CharacterData 字符数据对象
主要包含 Text 文本对象,和 Comment 注释对象。
操作文本和注释的方法,基本都是这个对象提供
MDN文档:https://developer.mozilla.org/zh-CN/docs/Web/API/CharacterData
Text 文本对象
包含按字面解释的纯文本,也可能包含转义后的 HTML 字符,但不包含 HTML 代码。
只要开始标签和结束标签之间有内容,就会创建一个文本节点,最多也就是只有1个文本节点,里面不支持子节点。
<!-- 没有内容,因此没有文本节点 -->
<div></div>
<!-- 有空格,因此有一个文本节点 -->
<div> </div>
<!-- 有内容,因此有一个文本节点 -->
<div>Hello World!</div>
常见操作
继承自CharacterData 字符数据对象,大部分方法都是 CharacterData 字符数据对象 提供
// <div id='test'>Hello World!</div>
let div = document.getElementById("test");
// =============== 0、获取文本节点 ===============
let textNode = div.firstChild; // 或 div.childNodes[0]
// =============== 1、获取文本值 ===============
textNode.nodeValue // "Hello World!"
textNode.data // "Hello World!"
// 文本的长度
textNode.nodeValue.length //12
textNode.data.length // 12
// =============== 2、修改 ===============
// 修改,只要节点在当前的文档树中,这样的修改就会马上反映出来
textNode.nodeValue = 'new World' // 直接改
textNode.appendData("!") // 末尾追加, new World!
// =============== 3、创建 & 合并 ===============
// 一般来说一个元素只包含一个文本子节点,
// 如果添加多个文本子节点,那么需要用element.normalize();方法合并一起,中间没有空格
let anotherTextNode = document.createTextNode("Yippee!"); // 创建另一个文本节点
div.appendChild(anotherTextNode); // 加进去,浏览器在解析文档时,永远不会创建同胞文本节点。
div.normalize(); // 合并2个文本子节点, new World!Yippee!
// =============== 4、插入 ===============
textNode.insertData(0, "A ") // 在位置 0个字符的位置后插入 A ,A new World!Yippee!
// =============== 5、拆分 / 提取 ===============
let newNode = textNode.splitText(12); // 拆分,12是偏移量,从左算起第12个位置, newNode = "A new World!"
textNode; // Yippee!
// =============== 6、删除 ===============
textNode.deleteData(0, 1) // 从位置第0个字符右边 开始删除 1 个字符, ippee!
textNode.replaceData(0, 1, "text") // 用 text 替换从第0个字符右边到 第1个字符右边;pppee!
其他对象
=================
虚拟节点
let list = document.getElementById("myList"),
fragment = document.createDocumentFragment(), // 创建虚拟节点
item;
for (let i = 0; i < 10; i++) {
item = document.createElement("li");
// 1、使用虚拟节点构建 DOM 结构,此时不会显示在DOM界面上
fragment.appendChild(item);
item.appendChild(document.createTextNode("Item " + i));
}
// 2、然后一次性将它添加到 list 元素,减少DOM的更新,优化性能
list.appendChild(fragment);
=================
Dom编程
==================
观察DOM元素变化接口
==================
性能
1、尽量少访问DOM和尽量减少标记
因为访问DOM查询某个元素,都会检索一次DOM树。
优化就是存储搜索的结果(赋值给变量),然后重复使用。
2、尽量减少标记
标记多,检索一次耗时就长。
3、合并和放置脚本
减少请求。
4、脚本的位置
放在末尾以及之前
5、压缩脚本
不必要的字节、空格、注释删除,很多工具可以帮忙做到,比如很多min.js