JavaScript-Document Object Model

Browser Object Model

1
2
3
4
5
6
7
8
9
10
11
12
13
window
├── navigator
├── location
├── frames
├── screen
├── document
│   ├── forms
│   ├── links
│   ├── anchors
│   ├── images
│   ├── all
│   ├── cookie
└── history

document

window.document 是 Document 的实例。代表整个 HTML 文件。该对象上提供一组 HTMLCollection 实例

name description
document.forms 获取所有窗体
document.images 获取所有的图片元素
document.links a 标签定义,超链接用 href 属性
document.anchors a 标签定义,锚点使用 name 属性
document.cookie 设定或读取 cookie,使用不便,容量只有 4kb

具体如下 DEMO

1
2
3
4
5
6
7
8
9
10
11
12
13
<!Doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
</head>
<body>
<form action="" name="login">
姓名:<input type="text" name="user" value="admin" />
密码:<input type="password" name="password" value="123456" />
<button type="submit">登录</button>
</form>
</body>
</html>

获取 forms

  1. 用[]加索引获取 document.forms[0]
  2. 用[]加 id 或 name 取 document.forms['login]
  3. 点运算符加名称取 document.forms.login

    以上三种方式适用于 document.forms/document.links/document.anchors/document.images

继续用elements获取子元素

  1. 用[]加索引获取 document.forms[0].elements[0]
  2. 用[]加 id 或 name 取 document.forms['login].elements['user']
  3. 点运算符加名称取 document.forms.login.elements.user

    在父子元素中都有 name 属性时,可以直接采用 document.login.user.value

文件对象模型(Document Object Model, DOM)

浏览器厂商实现的DOM——目的是解决浏览器间的对象模型不一致的问题

卷标以及文字都有相应的对象,这些就形成了树状结构

1
2
3
4
5
6
7
8
9
10
document                                    (Document)
├── html (HTMLHtmlElement)
│ ├── head (HTMLHeadElement)
│ │ ├── meta
│ ├── body
│ │ ├── form
│ │ │ ├── input[name=user]
│ │ │ ├── input[name=password]
│ │ │ ├── button
│ │ │ │ ├── 登录 (Text)

document 是 Document 的实例,代表整份文件,使用 document.childNodes[0] 或者 document.documentElement 取 html 卷标的 DOM 元素。

DOM API 分两部分
核心 API 可以用任何语言实现操作接口,可操作的对象基于 XML 文件
HTML DOM API 是核心 DOM API 的延伸,专门用于操作 HTML

使用 JavaScript 取 HTML 中的信息

文件结构

核心 DOM API 取出 HTML 中某个节点,再取其以下节点

API 描述
parentNode 获取当前节点的父节点
previousSibling 获取当前节点的前临节点
nextSibling 获取当前节点的后邻节点
firstChild 获取当前节点的首个子节点
lastChild 获取当前节点的最后一个子节点
childNodes 获取当前节点的所有子节点
1
2
3
4
5
6
7
8
9
10
document.body.lastChild
/*#text
assignedSlot: null
baseURI: "http://localhost:4000/2019/02/11/JavaScript-Docment-Object-Model/"
childNodes: NodeList []
data: "\n\n\n"
firstChild: null
isConnected: true
lastChild: null
...*/

doucment.body.parentNode 得到 text,header 的最后一个子节点是文本节点

通过以下特性获取元素

API 描述
parentElement 获取当前元素的父元素
previousElementSibling 获取当前元素的前邻接元素
nextElementSibling 获取当前元素的后邻接元素
firstElementChild 获取当前元素的第一个子元素
lastElementChild 获取当前元素的最后一个子元素
children 获取当前元素的所有子元素
1
2
document.body.lastElementChild
// <div class="banner">...</div>

id 属性、卷标名称、class 属性(取 Element 类型的节点)

name description return
getElementsByTagName 顺序是子树中的顺序,使用索引取对应节点 HTMLCollection
getElementsById 卷标对应 id 属性,独一无二,重复时取子树中第一个符合的元素 /
getElementsByName HTML 卷标定义 name 属性,name 属性值可以重复 HTMLCollection
getElementsByClassName HTML 卷标上定义有 class 属性 HTMLCollection
1
2
3
4
5
6
document.getElementById('nav')
/*
<span id="nav" style="display: none;">
...
</span>
*/

innerHTML 取卷标内的 HTML,HTML5 正式将 innerHTML 纳入标准

选择器语法

通过 querySelector()querySelectorAll() + CSS 选择器取元素

id 选择器
document.getElementById(‘test’) document.querySelector(‘#test’)
document.getElementsByTagName(‘div’) document.querySelectorAll(‘div’)
1
2
3
4
5
6
document.querySelector('#nav')
/*
<span id="nav" style="display: none;">
...
</span>
*/

卷标属性与DOM特性

这里稍做个理解上的区分,方便理解

通过 JavaScript 获取 DOM,继续取 DOM 具有的特性(Property)
HTML 卷标上设置的为属性(attribute)
通常情况下,JavaScript 特性与 HTML 卷标上的属性是对应的,也有因保留字或关键字原因造成的不一致如下表

html 属性 DOM特性
<input name=”user” value=”admin”> let input=document.querySelector(‘input’)[0];
let {name, value} = input
<label class=”label” for=”radio”>demo</>
class 为保留字
for 为关键字
let label = document.getElementsByTagName(‘label’)[0];
let className=label.className;
let labelFor = label.htmlFor

特别说明:文本也是一个 DOM 节点
取 body 中最后一个子节点的内容

1
2
3
4
5
6
7
8
9
10
11
document.body.lastChild
/**
* text
assignedSlot: null
baseURI: "http://localhost:4000/2019/02/11/JavaScript-Docment-Object-Model/"
childNodes: NodeList []
data: "\n\n\n"
...
*/
document.body.lastChild.data
// "\n\n\n"

innerHTML 设定 HTML 片段给 innerHTML,标签会被解析、建立对应的 DOM 对象,script 卷标被忽略,避免 XSS(Cross Site Script)的问题

如果卷标间只有文字,想要获取文件,使用 textContent

1
2
3
document.body.innerHTML = "<div id = 'a'>12</div>"
document.querySelector('#a').textContent
// "12"

attributes 特性

卷标设置的属性,在 DOM 对象的 attributes 特性中记录。attributes 类型为 NamedNodeMap,为类数组。

API 描述
getAttribute() 取 attribute 记录的属性值,取 attribute 中不存在指定属性时,返回 null。指的是 DOM 上没有对应的特性,该值为默认值。
setAttribute() 设定属性
removeAttribute() 来移除 attributes 属性。操作后只是回到默认值,不是直接将特性移除。没有任何操作可以将 DOM 对应于属性的特性移除
1
2
3
4
5
6
7
var dom = document.getElementsByName('input')
dom.getAttribute('readonly')

var imgDom = document.getElementById('img')
img.removeAttribute('src') // src = ''

dom.setAttribute('value', 'helenZhang') // 修改 input 的 defaultValue

基于安全考虑,input type 为 file 时,defaultValue、value 属性的设置会被忽略,无法通过程序代码取得 DOM 的 defaultValue、value 特性,使用程序代码设置 DOM 的 defaultValue、value 或通过 setAttribute() 设值会影响 DOM 相对应的特性,但对浏览器窗体或文件上传行为没有影响,只能由使用者亲自选取文件。

修改 DOM 树

浏览器解析 HTML,生成 DOM 树。根据 DOM 树绘制浏览器中的画面,改变 DOM 树,浏览器重绘画面。如此构成修改文件的基本原理。

修改 DOM 的几个 API

API name API description
createElement 建立卷标对应的元素
createTextNode 建立文本节点
appendChild 添加子节点
removeChild 删除子节点
cloneNode(true) 节点复制,默认不进行深层复制,即不复制子节点。加参数 true,则是指复制子节点

有关节点处理的 demo

操作多DOM的几种方式

  • createDocumentFragment,建立 DocumentFragment 实例,利用它作为容器在背景建立 DOM 结构,最后将 DocumentFragment 实例通过 appendChild() 附加至
    DOM 树

    有关节点处理的 demo
  • html 片段字符串,最后再设定给 innerHTML
    1
    2
    3
    4
    5
    let frag = ''
    for (let i=0; i<10; i++) {
    frag += '<img src={imgs[i]} />'
    }
    document.body.innerHTML = frag