目录

JavaScript中DOM操作

# DOM和BOM

DOM(文档对象模型),将页面所有的内容表示为可以修改的对象

BOM(浏览器对象模型),由浏览器提供的用于处理文档之外的所有内容的其它对象,比如navigator, location, history等对象

# DOM理解

浏览器会对HTML、CSS进行渲染,同时又要考虑可能会通过JavaScript来对其进行操作,因此浏览器将编写在HTML中的每一个元素(Element)都抽象成了一个个对象;这些对象都可以通过JavaScript来对其进行访问,那么就可以通过JavaScript来操作页面;这个抽象的过程称之为文档对象模型(Document Object Model)

整个文档被抽象到document中。比如document.documentElement对应的是HTML元素。document.body对应的是body元素。document.head对应的是head元素。

例如修改整个页面的背景颜色document.body.style.backgroundColor = 'red',因此DOM就是通过JavaScript对文档进行操作的。

# DOM Tree

一个页面不只有html, head, body元素,还有很多其他的子元素,这些结构最终形成了一个树结构,在抽象成DOM对象的时候,也会形成一个树结构,称之为DOM Tree

image

# DOM 的继承关系图

DOM相当于是JavaScript和HTML,CSS之间的桥梁,通过浏览器提供的DOM API,可以对元素以及其中的内容做任何事情。

image

# document对象

Document节点表示的整个载入的网页,他的实例是全局的document对象。对DOM的所有操作都是从document 开始的;它是DOM的入口点,可以从document开始去访问任何节点元素。

对于最顶层的html、head、body元素,可以直接在document对象中获取到

  • html 元素:document.documentElement <html>
  • body 元素:document.body <body>
  • head 元素:document.head <head>
  • 文档声明:document.doctype <!DOCTYPE html>
console.log('doctype: ', document.doctype)
console.log('html: ', document.documentElement)
console.log('head: ', document.head)
console.log('body: ', document.body)
1
2
3
4

image

# 节点(Node)之间的导航(navigator)

如果获取到一个节点后,可以根据这个节点去获取其它的节点,称之为节点之间的导航

节点之间存在如下的关系

  • 父节点:parentNode
  • 前兄弟节点:previousSibling
  • 后兄弟节点:nextSibling
  • 子节点:childNodes
  • 第一个子节点:firstChild
  • 最后一个子节点:lastChild
// 获取所有的节点的导航
var bodyEl = document.body
// 获取body所有的子节点
console.log(bodyEl.childNodes)
// 获取body的第一个子节点
console.log(bodyEl.firstChild)
// 获取body中的注释
console.log(bodyEl.firstChild.nextSibling)
// 获取body的父节点
console.log(bodyEl.parentNode)
1
2
3
4
5
6
7
8
9
10

# 元素之间的导航

如果获取到一个元素后,可以根据这个元素去获取其它的元素,称之为元素之间的导航

元素之间存在如下的关系

  • 父元素:parentElement
  • 前兄弟元素:previousElementSibling
  • 后兄弟元素:nextElementSibling
  • 子元素:children
  • 第一个子元素:firstElementChild
  • 最后一个子元素:lastElementChild

const bodyEl = document.body
// 根据body获取子元素
console.log(bodyEl.children)
// 获取第一个子元素
var firstChildElement1 = bodyEl.firstElementChild
var firstChildElement2 = bodyEl.children[0]
console.log(firstChildElement1)
console.log(firstChildElement2)
// 获取第二个子元素
const nextEle = firstChildElement1.nextElementSibling
console.log(nextEle)

// 假设第二个子元素为ul,那么接下来获取里面的所有li元素
var liEls = nextEle.children
console.log(liEls)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 表格元素的导航

table元素除了支持上面给出的属性之外,还支持以下属性

  • table.rows - <tr>元素集合
  • table.caption/thead/tfoot - 引用元素 <caption>,<thead>,<tfoot>
  • table.tbodies - <tbody>元素的集合

thead, tfoot, tbody 提供了rows属性

  • tbody.rows - 表格内部的<tr>元素的集合

tr

  • tr.ceils - 在给定的tr中的td和th单元格集合
  • tr.sectionRowIndex - 给定的 tr 在封闭的 <thead>/<tbody>/<tfoot>中的位置(索引)
  • tr.rowIndex - 在整个表格中 tr 的编号 (包括表格的所有行)

td 和 th

  • td.cellIndex - 在封闭的<tr>中单元格的编号

# 获取表单元素以及表单元素之间的导航

form元素可以直接通过document.forms获取所有的form元素,form元素中的内容可以通过form.elements来获取。

// 获取form元素
var formEl = document.forms[0]
// 通过form.elements获取form中的内容
var elements = formEl.elements
1
2
3
4

可以设置表单子元素的name来获取

<form action="">
  <input type="text" name="account" />
  <input type="password" type="password" />
  <input type="checkbox" type="sex" checked />
  <select name="fruits" id="fruits">
    <option value="apple">苹果</option>
    <option value="orange">橘子</option>
  </select>
</form>
<script>
  var formEl = document.forms[0];
  var elements = formEl.elements;
  console.log(elements.account.value);
  console.log(elements.fruits.value);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 获取元素的方法

当元素彼此靠近或者相邻时,DOM导航属性非常有用,但是在实际开发中,想要获取到某一个元素该如何操作。

DOM提供了如下获取元素的方法

方法名 搜索方式 是否可以在元素上调用 是否实时的
querySelector css-selector
querySelectorAll css-selector
getElementById id
getElementsByName name
getElementsByTagName tag or *
getElementsByClassName class

开发中的选择

  • 目前最经常使用的querySelector和querySelectorAll
  • getElementById偶尔使用或者适配低版本的浏览器

# 节点属性--nodeType

nodeType属性提供了一种获取节点类型的方法,它有一个数值类型

常量 描述
Node.ELEMENT_NODE 1 一个 元素 节点,例如<p><div>
Node.TEXT_NODE 3 Element 或者 Attr 中实际的 文字
Node.COMMENT_NODE 8 一个 Comment 节点。
Node.DOCUMENT_NODE 9 一个 Document 节点。
Node.DOCUMENT_TYPE_NODE 10 描述文档类型的 DocumentType 节点。例如 <!DOCTYPE html>就是用于 HTML5 的。

文档地址

https://developer.mozilla.org/zh-CN/docs/Web/API/Node/nodeType

# 节点的属性 -- nodeName,tagName

nodeName: 获取node节点的名字,tagName: 获取元素的标签名称

nodeName和tagName之间的不同点

  • tagName属性仅适用于Element节点
  • nodeName是为任意Node定义的,对于元素,它的意义与 tagName 相同,所以使用哪一个都是可以的;对于其他节点类型(text,comment 等),它拥有一个对应节点类型的字符串;

# 节点属性 -- innerHTML,textContent、data

# innerHTML属性

  • 将元素中的 HTML 获取为字符串形式;
  • 设置元素中的内容;

# outerHTML属性

  • 包含了元素的完整 HTML
  • innerHTML 加上元素本身一样;

# textContent属性

  • 仅仅获取元素中的文本内容;

# innerHTML和textContent的区别

  • 使用 innerHTML,我们将其“作为 HTML”插入,带有所有 HTML 标签。
  • 使用 textContent,我们将其“作为文本”插入,所有符号(symbol)均按字面意义处理

# 节点属性 -- nodeValue

nodeValue/data 用于获取非元素节点的文本内容

var text = document.body.firstChild
console.log(text.nodeValue)
1
2

# data(nodeValue)

data是针对于非元素的节点,元素节点获取的结果是 null

# 节点其它属性

hidden 属性,是一个全局属性,可以用于设置元素隐藏

var box = document.querySelector('.box')
box.hidden = true
1
2

DOM 元素的其它属性

  • value
    • <input><select><textarea>(HTMLInputElement,HTMLSelectElement……)的 value。
  • href
    • <a href="...">(HTMLAnchorElement)的 href。
  • id
    • 所有元素(HTMLElement)的 “id” 特性(attribute)的值。

# 元素的属性和特性

一个元素除了有开始标签,结束标签,内置之外还有很多属性

image

浏览器在解析HTML元素时,会将对应的attribute也创建出来放到对应的元素对象上。比如 id、class就是全局的attribute,会有对应的id, class属性。

# attribute的分类

  • 标准的attribute: 某些attribute属性是标准的,比如id, class, href, type, value
  • 非标准的attribute: 某些attribute属性是自定义的,比如abc、age、height等

# attribute的操作

  • ele.hasAttribute(name) 检查特性是否存在
  • ele.getAttribute(name) 获取这个特性值
  • ele.setAttribute(name, value) 设置这个特性值
  • ele.removeAttribute(name) 移除这个特性
  • attributes: attr对象的集合,具有name, value属性
for(var attr of boxEl.attributes){
    console.log(attr.name, attr.value)
}
console.log(boxEl.hasAttribute('age'))
console.log(boxEl.getAttribute('name'))
boxEl.setAttribute('name', 'abc')
boxEl.removeAttribute('abc')
1
2
3
4
5
6
7

attribute具备以下的特征

  • 他们的名字是大小写不敏感的(id与ID相同)
  • 他们的值总是字符串类型的

# 元素的属性 property

对于标准的attribute,会在DOM对象上创建与其对应的property属性

console.log(boxEl.id, boxEl.className)
console.log(boxEl.abc, boxEl.age, boxEl.height)
1
2

在大多数情况下,他们是相互作用的,改变property,通过attribute获取的值,会随着改变。通过attribute操作修改,property的值会随着改变,但是有些浏览器的input的value修改只能通过attribute的方法,目前大部分浏览器都是已经支持的。通过el.value的方式设置值的优先级更高。

除非特殊情况下,大多数情况下,设置、获取attribute,推荐使用property的方式,因为它默认情况下是有类型的。

btn.onclick = function(){
    checkBoxInput.checked = !checkBoxInput.checked
}
1
2
3

# HTML5 的data-* 自定义属性

HTML5中的data-*自定义属性也是可以在dataset属性中获取到的

<div class="box" data-name="b1" data-age="18">
    
</div>
<script>
	var boxEl = document.querySelector('.box')
    console.log(boxEl.dataset.name)
    console.log(boxEl.dataset.age)
</script>
1
2
3
4
5
6
7
8

# JavaScript动态修改样式

有时候会通过JavaScript来动态修改样式,这时候有两个选择

  • 在CSS中编写好对应的样式,动态添加 class

  • 动态修改style属性

  • 在开发中大多数情况下可以动态修改class完成某个功能,因此更推荐使用动态class

  • 对于某些情况如果无法动态修改class(比如精准修改某个css属性的值),那么就可以修改style属性

# 元素的className和classList

元素的attribute,对应的property并非叫class,而是className。因为JavaScript早起不允许使用class关键字来作为对象的属性,所以DOM规范使用了className,虽然想在JavaScript已经没有了这样的限制,但是并不推荐,并且依然在使用className这个名称。

可以对className进行赋值,会替换整个类中的字符串

var boxEl = document.querySelector('.box')
boxEl.className = 'anc'
1
2

如果需要添加或者移除单个class,那么可以使用classList属性

ele.classList是一个特殊的对象

  • ele.classList.add(class) 添加一个类
  • ele.classList.remove(class) 添加/移除类
  • ele.classList.toggle(class) 如果类不存在就添加类,存在就移除它
  • ele.classList.contains(class) 检查给定的类,返回true/false

classList是可迭代对象,可以通过for...of..进行遍历

# 元素的style属性

如果需要单独修改某一个css属性,那么可以通过style来操作,对于多词(multi-word)属性,使用驼峰式

boxEl.style.width = '100px'
boxEl.style.height = '50px'
boxEl.style.backgroundColoe = 'red'
1
2
3

如果将值设置为空字符串,那么会使用CSS的默认样式

boxEl.style.display = ''
1

多个样式的写法,需要使用cssText属性,不推荐这种用法,因为他会替换整个字符串

boxEl.style.cssText = `width: 100px; height: 100px; background-color: red;`
1

# 元素style的读取 -- getComputedStyle

如果需要读取样式,

  • 对于内联样式,是可以通过style.*方式读取到的
  • 对于style、css文件中的样式是读取不到的

对于读取不到的情况可以通过getComputedStyle的全局函数来实现

console.log(getComputedStyle(boxEl).width)
console.log(getComputedStyle(boxEl).height)
console.log(getComputedStyle(boxEl).backgroundColor)
1
2
3

# 创建元素

可以使用document.write 方法写入一个元素,这种方式写起来非常便捷,但是对于复杂的内容、元素关系拼接并不方便。它是在早期没有DOM的时候使用的方案,目前依然被保留了下来。

目前我们想要插入一个元素,通常会按照如下步骤

  • 创建一个元素
  • 插入元素到DOM的某一个位置;

创建元素

var boxE = document.querySelector('.box')
var h2El = document.createElement('h2')
h2El.innerHTML = '二级标题'
h2El.classList.add('title')
boxEl.append(h2El)
1
2
3
4
5

# 插入元素

  • node.append(...nodes or strings) —— 在 node 末尾 插入节点或字符串
  • node.prepend(...nodes or strings) —— 在 node 开头 插入节点或字符串
  • node.before(...nodes or strings) —— 在 node 前面 插入节点或字符串
  • node.after(...nodes or strings) —— 在 node 后面 插入节点或字符串,
  • node.replaceWith(...nodes or strings) —— 将 node 替换为给定的节点或字符串

image

# 移除和克隆元素

移除元素可以使用元素本身的remove方法,移除自身以及子元素

h2El.remove()
1

如果需要复制一个现有的元素,可以通过cloneNode方法

cloneNode的使用

  • 可以传入一个Boolean类型的值来决定是否是深度克隆
  • 深度克隆会克隆对应的子元素,否则不会
var cloneBoxEl = boxEl.cloneNode(true)
document.body.append(cloneBoxEl)
1
2

# 旧的元素的操作方法

  • parentElem.appendChild(node) 在parentElem的父元素最后位置添加一个子元素
  • parentElem.insertBefore(node, nextSibling) 在parentElem的nextSibling前面插入一个子元素
  • parentElem.replaceChild(node, oldChild) 在parentElem中,新元素替换之前的oldChild元素
  • parentElem.removeChild(node) 在parentElem中,移除某一个元素

# 元素的大小、滚动

  • clientWisth: contentWidth + padding (不包含滚动条)
  • cilentHeight:contentHeight + padding
  • clientTop:border-top边框的宽度
  • clientLeft:border-left边框的宽度
  • offsetWidth:元素完整的宽度
  • offsetHeight:元素完整的高度
  • offsetLeft:左侧距离父元素的宽度
  • offsetTop:顶部距离父元素的宽度
  • scrollHeight:整个可滚动区域的高度
  • scrollTop:滚动部分的高度

image

# window的大小、滚动

# window的width和height

  • innerWidth、innerHeight:获取window窗口的宽度和高度(包含滚动条)
  • outerWidth、outerHeight:获取window窗口的整个宽度和高度(包括调试工具、工具栏)
  • documentElement.clientHeight、documentElement.clientWidth:获取html的宽度和高度(不包含滚动条)

# window的滚动位置

  • scrollX:X轴滚动的位置(别名pageXOffset)
  • scrollY:Y轴滚动的位置(别名pageYOffset)

# 提供对应的滚动方法

  • scrollBy(x,y) :将页面滚动至 相对于当前位置的 (x, y) 位置
  • scrollTo(pageX,pageY) 将页面滚动至 绝对坐标

# 倒计时案例

var endDate = new Date()
endDate.setHours(24)
endDate.setMinutes(0)
endDate.setSeconds(0)
endDate.setMilliseconds(0)

var start = new Date()
vat intervalTime = Math.floot((endDate.getTime() - startDate.getTime())/1000)
var hour = Math.floot(intervalTime / 3600)
var minute = Math.floor(intervalTime / 60) % 60
var second = intervalTime % 60
1
2
3
4
5
6
7
8
9
10
11
上次更新: 2022/09/08, 08:36:47
最近更新
01
防抖和节流
02-06
02
正则表达式
01-29
03
async_await函数
12-30
更多文章>