DOM原生接口回顾

DOM

什么是 DOM,DOM 就是 Document Object Model(文档对象模型),一般来讲 Document(文档) 就是指 xml/html 文档, html 是 xml 的衍生品, Object 就是把文档变成一个对象, Model 就是被抽象成的模型

写 html 时先写啥

<!DOCTYPE html>
<html lang="zh-Hans">
// head body html 都是可以省略并且符合 html 规范的
<head>
</head>
<body>
</body>
</html>

其中 html 的树形结构如下图所示

然而这个树并不是存在于内存中的,那么怎么样才能把这种结构存在内存中呢?
就是通过一个对象 (Object)来存储,它们中间有一层模型(Model)的映射关系,而通过这个模型,可以将文档(Document)变成对象(Object),也就是所谓的 DOM

那么 Object 里面到底存什么呢,有一个 DOM 标准
可以存成 Element 对象,也可以存成 Text 对象,也可以存成 Document
如下图

这三个原型链的 __proto__ 都是 Node,其继承关系如下

  • Node
    • Document
      • html
    • Element
      • head body meta title script h1 h2 p
    • Text
      • 标题1 span
    • Comment 注释
    • …(其他)

DOM 的转化公式如下

Node 接口

属性

  • childNodes
  • firstChild
  • innerText
  • lastChild
  • nextSibling
  • nodeName
  • nodeType
  • nodeValue
  • outerText
  • ownerDocument
  • parentElement
  • parentNode
  • previousSibling
  • textContent

没有人在工作中用 DOM API ,除非你很闲

html 中回车也算文本

document.body.childNodes 会获取到子标签,包括标签间的空格以及回车
document.body.children 会获取到子节点,不包括标签间的空格以及回车

nextElementSibling 获取到下一个 Element 的兄弟节点

document.documentElement.nodeName 才能获取到 html

nodeType Element node 是 1, Text node 是 3
具体参考nodeType MDN
需要注意的是 DocumentFragment 优化

innerText 和 textContent
前者的 API 是 IE 的,直接获取文本内容,后者是 Firefox 以及 Opera 的,
详情见 textContent 与 innerText 的区别
主要是 innerText 会检查 css,比如会检查 css 中有没有 display: none; 属性,而 textContent 就不会,这样的话就 textContent 而言性能就高一些

构造函数是怎么把标签变成对象的?

function createPerson() {
return {
name: '芳芳',
age: 18
}
}
createPerson() // 调用

只要这个函数返回了一个对象,那么就构造了这个对象,只是说用几个属性抽象成了这个对象

方法

  • appendChild()
  • cloneNode() // 深拷贝节点
  • contains()
  • hasChildNodes()
  • insertBefore()
  • isEqualNode()
  • isSameNode()
  • removeChild()
  • replaceChild()
  • normalize() // 常规化

cloneNode 中间有传的参数表示是否进行深拷贝 var dupNode = node.cloneNode([deep]); 以 div 节点为例,假如这个节点有一个或者多个子节,那么深拷贝会连这个节点及其子节点都拷贝过去,而 浅拷贝则只会拷贝这个节点,不会拷贝这个节点的子节点

isEqualNode 与 isSameNode
isSameNode 可以简写成 ===,而 isEqualNode 从概念上讲有点像 == ,但是对于节点来说不能用 == 符号来替代

normalize
以下是 MDN 上的 demo 代码

var wrapper = document.createElement("div");

wrapper.appendChild( document.createTextNode("Part 1 ") );
wrapper.appendChild( document.createTextNode("Part 2 ") );

// At this point, wrapper.childNodes.length === 2
// wrapper.childNodes[0].textContent === "Part 1 "
// wrapper.childNodes[1].textContent === "Part 2 "

wrapper.normalize();

// Now, wrapper.childNodes.length === 1
// wrapper.childNodes[0].textContent === "Part 1 Part 2 "

在标签中加入文本

div1.innerText = 'hello world'

innerText 有个副作用,假如如上的 div 中有个 span, 那么使用 innerText 就是会将这个 span 元素清空掉

更安全的方式是生成一个节点

div1.appendChild(document.createTextNode('hello world'))

Document 和标签的 API

属性

  • anchors // 获取锚点元素
  • body
  • characterSet //获取字符
  • childElementCount
  • children
  • doctype
  • documentElement
  • domain
  • head
  • hidden
  • images
  • links
  • location
  • onxxxxxxxxx // 一系列的事件监听
  • origin
  • plugins
  • readyState
  • referrer
  • scripts
  • scrollingElement
  • styleSheets
  • title
  • visibilityState

referrer
待续

方法

  • close()
  • createDocumentFragment()
  • createElement()
  • createTextNode()
  • execCommand()
  • exitFullscreen()
  • getElementById()
  • getElementsByClassName()
  • getElementsByName()
  • getElementsByTagName() // 很难用
  • getSelection()
  • hasFocus()
  • open()
  • querySelector() // 返回一个元素
  • querySelectorAll() // 返回所有元素
  • registerElement()
  • write()
  • writeln() // 写一行

close
像如下代码

document.write(1)
document.write(2)
setTimeout(() => {document.write(3)}, 1000)

执行的结果(先显示了 1 跟 2)显示出来是 3, 这是为什么呢

document 运行的过程如下

页面在加载第一个标签时就处于 open 状态,页面加载完毕之后才能 close ,这个时候若想要再 write, 必须先回到最开始的 open 状态,进而把之前的内容给覆盖掉,所以这个 api 在使用时需要特别小心
,尽量少用 write

write MDN

execCommand
写富文本编辑器(有样式的)时可以用到这个 api,

querySelector 返回一个元素
querySelectorAll 返回一个伪数组(原型链中没有 Array.prototype)

Element

innerHTML 与 innerText 的区别
以一个输入框举例

<input type="text" id="input1"><button id=x>
写入页面
</button>
<div id="output"></div>

x.onclick = function() {
output.innerHTML = input1.value
}

加入这个时候用户输入的是 <b>123</b>, innerHTML 会将用户的输入处理成有加粗的 123,所以这就是有危险的,万一输入框中是一段 js 代码呢
而 innerText 则处理的是纯文本,用户输入什么它就输出什么

详情见 Element MDN

注意

  • DOM 现在的通用版本是 DOM Level 3
  • 节点的类型有 7 种,分别是
    • Element node
    • Text node
    • ProcessingInstruction node (Attribute ???)
    • Comment node
    • Document node
    • DocumentType node
    • DocumentFragment node
  • 元素 Element 的 NodeType 值为 1 或者 Node. ELEMENT_NODE
  • 有如下代码, x.nextSibling 的值为 回车构成的文本节点

    <div id=x></div>
    <div id=y></div>
  • 有如下代码, 那么 parent1.childNodes 的值是 {0:child1, length:1} 伪数组

    <div id=parent1><div id=child1></div></div>
  • parent.childNodes 是动态集合。所谓动态集合就是一个活的集合,DOM树删除或新增一个相关节点,都会立刻反映在NodeList接口之中。

  • document.querySelectorAll方法返回的是一个静态集合。DOM内部的变化,并不会实时反映在该方法的返回结果之中。
  • HTMLCollection实例对象的成员只能是Element节点,NodeList实例对象的成员可以包含其他节点。
  • HTMLCollection实例对象可以用id属性或name属性引用节点元素,NodeList只能使用数字索引引用。