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 优化

innerTexttextContent
前者的 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 节点为例,假如这个节点有一个或者多个子节,那么深拷贝会连这个节点及其子节点都拷贝过去,而 浅拷贝则只会拷贝这个节点,不会拷贝这个节点的子节点

isEqualNodeisSameNode
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

innerHTMLinnerText 的区别
以一个输入框举例

<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只能使用数字索引引用。