七、JavaScript的DOM操作

2022/7/5 JS基础DOM操作节点的属性元素的属性元素的相关操作元素的dataset属性元素大小、滚动window大小、滚动

# 1、认识DOM和BOM

  • 前面我们花了很多时间学习JavaScript的基本语法,但是这些基本语法,但是这些语法好像和做网页没有什么关系,和前面学习的HTML、CSS也没有什么关系呢?

    • 这是因为我们前面学习的部分属于ECMAScript,也就是JavaScript本身的语法部分;
    • 除了语法部分之外,我们还需要学习浏览器提供给我们开发者的DOM、BOM相关的API才能对页面、浏览器进行操作;
  • 前面我们学习了一个window的全局对象,window上事实上就包含了这些内容:

    • 我们已经学习了JavaScript语法部分的Object、Array、Date等;
    • 另外还有DOM、BOM部分;

jtp8Te.png

  • DOM:文档对象模型(Document Object Model)

    • 简称 DOM,将页面所有的内容表示为可以修改的对象;
  • BOM:浏览器对象模型(Browser Object Model)

    • 简称 BOM,由浏览器提供的用于处理文档(document)之外的所有内容的其他对象;
    • 比如navigator、location、history等对象;

# 2、深入理解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"
1
  • 所以我们学习DOM,就是在学习如何通过JavaScript对文档进行操作的;

# 3、DOM Tree的理解

  • 一个页面不只是有html、head、body元素,也包括很多的子元素:
    • 在html结构中,最终会形成一个树结构;
    • 在抽象成DOM对象的时候,它们也会形成一个树结构,我们称之为DOM Tree;
<!DOCTYPE html>
<html lang="en">
<head>
  <title> My Title </title>
</head>
<body>
  <h1>A Heading</h1>
  <a href="#">Link Text</a>
</body>
</html>
<!-- 上面的HTML结构;抽象成DOM对象,形成的树结构 如下图: -->
1
2
3
4
5
6
7
8
9
10
11

jtpYYd.png

# 4、DOM的学习顺序

  • DOM相关的API非常多,我们会通过如下顺序来学习:

    1.DOM元素之间的关系

    2.获取DOM元素

    3.DOM节点的type、tag、content

    4.DOM节点的attributes、properies

    5.DOM节点的创建、插入、克隆、删除

    6.DOM节点的样式、类

    7.DOM元素/window的大小、滚动、坐标

# 5、DOM的继承关系图

  • DOM相当于是JavaScript和HTML、CSS之间的桥梁

    • 通过浏览器提供给我们的DOM API,我们可以对元素以及其中的内容做任何事情;
  • 类型之间有如下的继承关系:

jtUL8g.png

# 6、document对象

  • Document节点表示的整个载入的网页,它的实例是全局的document对象:

    • 对DOM的所有操作都是从 document 对象开始的;
    • 它是DOM的 入口点,可以从document开始去访问任何节点元素;
  • 对于最顶层的html、head、body元素,我们可以直接在document对象中获取到:

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

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

  • 如果我们获取到一个节点(Node)后,可以根据这个节点去获取其他的节点,我们称之为节点之间的导航**。**
  • 节点之间存在如下的关系:
    • 父节点:parentNode
    • 前兄弟节点:previousSibling
    • 后兄弟节点:nextSibling
    • 所有子节点:childNodes
    • 第一个子节点:firstChild
    • 最后一个子节点:lastChild

jtalGD.png

  • 注意:在html结构中:
    • 两元素之间 换行导致出现的空格 或者 不换行且输入空格 都是 text节点;
    • comment是 注释节点
  • 尝试获取下面结构的节点
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div class="box">
    <!-- 我是注释 -->
    <h1 class="title">我是标题</h1><div class="container">我是div元素</div><p class="desc">我是第一个段落</p>
    我是文本
  </div>

  <script>
    // 获取body元素
      var bodyEl = document.body

    // 1.拿到div.box元素 的节点
      var boxNode = bodyEl.childNodes[1]
      console.log(boxNode)

    // 2.拿到div.box元素的节点 的所有子节点
      var boxChildNodes = boxNode.childNodes
      console.log(boxChildNodes) //NodeList(7) [text, comment, text, h1.title, div.container, p.desc, text]  

    // 3.拿到div.box元素的节点 的第一个子节点
      var boxFirstNode = boxNode.firstChild
      console.log(boxFirstNode)
      
    // 4.拿到div.box元素的节点 的最后一个子节点
      var boxLastNode = boxNode.lastChild
      console.log(boxLastNode)
      
    // 5.拿到div.container元素 的节点
      var containerNode = boxChildNodes[4]
      console.log(containerNode)
      
    // 6.拿到div.container元素的节点 的前兄弟节点
      var titleNode = containerNode.previousSibling
      console.log(titleNode)
      
    // 7.拿到div.container元素的节点  的后兄弟节点
      var descNode = containerNode.nextSibling
      console.log(descNode)
      
    // 8.拿到div.container元素的节点 的父节点
      var containerParentNode = containerNode.parentNode
      console.log(containerParentNode)
  </script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

# 8、元素(Element)之间的导航(navigator)

  • 如果我们获取到一个元素(Element)后,可以根据这个元素去获取其他的元素,我们称之为元素之间的导航。
  • 元素之间存在如下的关系:
    • 父元素:parentElement
    • 前兄弟元素:previousElementSibling
    • 后兄弟元素:nextElementSibling
    • 所有子元素:children
    • 第一个子元素:firstElementChild
    • 最后一个子元素:lastElementChild

jtaUdP.png

  • 尝试获取下面结构的元素:
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div class="box">
    <!-- 我是注释 -->
    <h1 class="title">我是标题</h1><div class="container">我是div元素</div><p class="desc">我是第一个段落</p>
    我是文本
  </div>

  <script>
    // 获取body元素
      var bodyEl = document.body

    // 1.拿到div.box元素
      var boxEl = bodyEl.children[0]
      console.log(boxEl)

    // 2.拿到div.box元素 的所有子元素
      var boxChildEl = boxEl.children
      console.log(boxChildEl) //HTMLCollection(3) [h1.title, div.container, p.desc]

    // 3.拿到div.box元素 的第一个子元素
      var boxFirstEl = boxEl.firstElementChild
      console.log(boxFirstEl)
      
    // 4.拿到div.box元素 的最后一个子元素
      var boxLastEl = boxEl.lastElementChild
      console.log(boxLastEl)
      
    // 5.拿到div.container元素
      var containerEl = boxChildEl[1]
      console.log(containerEl)
      
    // 6.拿到div.container元素 的前兄弟元素
      var titleEl = containerEl.previousElementSibling
      console.log(titleEl)
      
    // 7.拿到div.container元素  的后兄弟元素
      var descEl = containerEl.nextElementSibling
      console.log(descEl)
      
    // 8.拿到div.container元素 的父元素
      var containerParentEl = containerEl.parentElement
      console.log(containerParentEl)
  </script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

# 9、表格(table)元素的导航(navigator)

  • <table>元素支持 (除了上面给出的,之外) 以下这些属性:

    • table.rows — <tr> 元素的集合;
    • table.caption/tHead/tFoot — 引用元素<caption><thead><tfoot>
    • table.tBodies — <tbody> 元素的集合;
  • <thead><tfoot><tbody>元素提供了 rows 属性:

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

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

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

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    table {
      border-collapse: collapse;
    }

    td {
      border: 1px solid #000;
      padding: 8px 12px;
    }
  </style>
</head>
<body>
  
  <table>
    <tr>
      <td>1-1</td>
      <td>2-1</td>
      <td>3-1</td>
      <td>4-1</td>
    </tr>
    <tr>
      <td>1-2</td>
      <td>2-2</td>
      <td>3-2</td>
      <td>4-2</td>
    </tr>
    <tr>
      <td>1-3</td>
      <td>2-3</td>
      <td>3-3</td>
      <td>4-3</td>
    </tr>
    <tr>
      <td>1-4</td>
      <td>2-4</td>
      <td>3-4</td>
      <td>4-4</td>
    </tr>
  </table>

  <script>

    var tableEl = document.body.firstElementChild

    // // 1.获取1-1
    // var row1El = tableEl.rows[0]
    // var cell1El = row1El.cells[0]
    // console.log(cell1El)

    // // 2.获取2-2
    // var row2El = tableEl.rows[1]
    // var cell2El = row1El.cells[1]

    // for循环
    for (var i = 0; i < tableEl.rows.length; i++) {
      var rowEl = tableEl.rows[i]
      var cellEl = rowEl.cells[i]

      // 设置样式
      cellEl.style.backgroundColor = "red"
      cellEl.style.color = "white"
    }

  </script>

</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

# 10、表单(form)元素的导航(navigator)

  • <form>元素可以直接通过document来获取:document.forms
  • <form> 元素中的内容可以通过elements来获取:form.elements
  • 我们可以设置表单子元素的name来获取它们
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>

  <form action="">
    <input name="account" type="text" value="我将在5秒后会被打印,请进行输入修改我">
    <input name="password" type="password">
    <input name="hobbies" type="checkbox" checked>
    <select name="fruits">
      <option value="apple">苹果</option>
      <option value="orange">橘子</option>
    </select>
  </form>
  
  <script>
    // 1.获取form的方式
    var form1Els = document.body.firstElementChild
    var form2Els = document.forms[0]
    var form3Els = form2Els.elements
    console.log(form1Els,form2Els,form3Els)

    // 2.通过form表单子元素的name来获取 具体的form子元素
    var inputEl = form2Els.elements.account
    console.log(inputEl)
    console.log(form3Els.fruits)

    setTimeout(function() {
      console.log(inputEl.value)
    }, 5000)

  </script>

</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

# 11、获取元素的方法

  • 当元素彼此靠近或者相邻时,DOM 导航属性(navigation property)非常有用。

    • 但是,在实际开发中,我们希望可以任意的获取到某一个元素应该如何操作呢?
  • DOM为我们提供了获取元素的方法:

方法名 搜索方式 可以在元素上调用吗? 实时的?
querySelector CSS-selector -
querySelectorAll CSS-selector -
getElementById id - -
getElementsByName name -
getElementsByTagName tag or '*'
getElementsByClassName class
  • 开发中如何选择呢?
    • 目前最常用的是querySelector和querySelectAll;
    • getElementById偶尔也会使用或者在适配一些低版本浏览器时;

# 12、节点的属性 - nodeType

  • 目前,我们已经可以获取到节点了,接下来我们来看一下节点中有哪些常见的属性:

    • 当然,不同的节点类型有可能有不同的属性;
    • 这里我们主要讨论节点共有的属性;
  • nodeType属性:

    • nodeType 属性提供了一种获取节点类型的方法;
    • 它有一个数值型值(numeric value);
  • 常见的节点类型有如下:

常量 描述
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 。

# 13、节点的属性 – nodeName、tagName

  • nodeName:获取node节点的名字;

  • tagName:获取元素的标签名词;

  • tagName 和 nodeName 之间有什么不同呢?

    • tagName 属性仅适用于 Element 节点;
    • nodeName 是为任意 Node 定义的:
      • 对于元素,它的意义与 tagName 相同,所以使用哪一个都是可以的;
      • 对于其他节点类型(text,comment 等),它拥有一个对应节点类型的字符串;
<body><!-- 我是一行注释 --> 
  <h1 class="title">我是标题</h1>
  <div class="box">我是div元素</div>
</body>
<script>
  var boxEl = document.querySelector('.box')
  console.log(boxEl.tagName)//DIV
  console.log(boxEl.nodeName)//DIV
  var commentNode = document.body.firstChild // 获取注释节点
  console.log(commentNode.nodeName)//#comment
  console.log(commentNode.tagName)//undefined 
</script>
1
2
3
4
5
6
7
8
9
10
11
12

# 14、节点的属性 - innerHTML、textContent

  • innerHTML 属性

    • 获取:将元素中的内容获取为字符串形式;例:var elecontent = element.innerHTML
    • 设置:设置元素中的内容(设置的时候,元素中的内容全部都会被替换掉)例:element.innerHTML = '<i>元素中的内容被我替换啦</i>'
    • 如果要向一个元素中插入一段 HTML,而不是替换它的内容,那么请使用 insertAdjacentHTML() (opens new window) 方法。
  • outerHTML 属性

    • 获取/设置 的是 包含了元素本身的完整 HTML
    • 相当于:innerHTML(获取/设置 元素中的全部内容) 加上元素本身一样;
  • textContent 属性:仅仅 获取/设置 元素中的文本内容;(设置时,浏览器不会解析字符串里面的HTML标签,而是当内容展示)

  • innerHTML和textContent的区别:

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

# 15、节点的属性 - nodeValue

  • nodeValue/data
    • 用于获取非元素节点的文本内容
<body>
  lyk
  <!-- 注释内容 -->
  <div class="box">我是box元素</div>
  <script>
  //1.非元素节点案例
    var text = document.body.firstChild
    console.log(text.nodeValue)//lyk
    console.log(text.data)//lyk
    var comment = text.nextSibling
    console.log(comment.nodeValue)//注释内容 
    console.log(comment.data)//注释内容 
    
  //2.元素节点案例:
    var boxEl = document.querySelector('.box')
    console.log(boxEl.nodeValue)//null
    console.log(boxEl.data)//undefined
  </script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 16、元素的属性

  • hidden属性:也是一个全局属性,可以用于设置元素隐藏。
<body>
  <div class="hidden" hidden>我是隐藏元素</div>
  <div class="box">hidden:true</div>
  <div class="bar" hidden>hidden:false</div>
  <script>
    var boxEl = document.querySelector('.box')
    boxEl.hidden = true  //将div.box 元素隐藏

    var barEl = document.querySelector('.bar')
    barEl.hidden = false  //将div.bar 元素显示
  </script> 
</body>
1
2
3
4
5
6
7
8
9
10
11
12
  • DOM 元素还有其他属性:

    • value

      • <input><select><textarea>(HTMLInputElement,HTMLSelectElement……)的 value。
    • href

      • <a href="...">(HTMLAnchorElement)的 href。
    • id

      • 所有元素(HTMLElement)的 “id” 特性(attribute)的值。
  • class和style我们会在后续专门讲解的。

# 17、元素的属性和特性

  • 前面我们已经学习了如何获取节点,以及节点通常所包含的属性,接下来我们来仔细研究元素Element。
  • 我们知道,一个元素除了有开始标签、结束标签、内容之外,还有很多的属性(attribute)

jU0lz8.png

  • 浏览器在解析HTML元素时,会将对应的attribute(属性)也创建出来放到对应的元素对象上。

    • 比如id、class就是全局的attribute,会有对应的id、class属性;
    • 比如href属性是针对a元素的,type、value属性是针对input元素的;
  • 接下来我们学习一下如何获取和设置这些属性。

  • attribute的分类

    • 属性attribute的分类:
      • 标准的attribute:某些attribute属性是标准的,比如id、class、href、type、value等;
      • 非标准的attribute:某些attribute属性是自定义的,比如abc、age、height等;
<!-- 如下:class、id是标准的attribute;
          age、abc是非标准的attribute -->
<div class="box" id="bar" age="999" abc='hehe'>
    哈哈哈
</div>
1
2
3
4
5

# 19、attribute的操作(元素的方法)

  • 对于所有的attribute访问都支持如下的方法:

    • elem.hasAttribute(name) — 检查特性是否存在。
    • elem.getAttribute(name) — 获取这个特性值。
    • elem.setAttribute(name, value) — 设置这个特性值。
    • elem.removeAttribute(name) — 移除这个特性。
    • attributes:attr对象的集合,具有name、value属性;
  • attribute具备以下特征:

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

<body>
  <form>
    <label for="password">密码:</label>
    <input id="password" type="password" name="password" value="123456" age="999">

    <input type="button" value="显示密码" name="btn">
    <br>

    <label for="checked">
        <input id="checked" type="checkbox" name="checked" checked>你觉得这个例子如何
    </label>
    <button type="button" class="btn">取消复选框选中状态</button>
  </form>
  <script>
    //获取表单所有子元素
    var formEls = document.forms[0]
    //通过name属性获取密码输入框元素
    var passwordEl = formEls.password
    
// 1- elem.hasAttribute(name) — 检查特性是否存在。
    console.log(passwordEl.hasAttribute('placeholder'))//false
    console.log(passwordEl.hasAttribute('age'))//true
      
// 2- elem.getAttribute(name) — 获取这个特性值。
    console.log(passwordEl.getAttribute('name'))//password
    console.log(passwordEl.getAttribute('age'))//999
      
// 3- elem.setAttribute(name, value) — 设置这个特性值。
    passwordEl.setAttribute('age',999)
    console.log(passwordEl.getAttribute('age'))//999
    console.log(typeof passwordEl.getAttribute('age'))//string
      
// 4- elem.removeAttribute(name) — 移除这个特性。
    passwordEl.removeAttribute('age')
    console.log(passwordEl.getAttribute('age'))//null
      
// 5- attributes:attr对象的集合,具有name、value属性;
    console.log(passwordEl.attributes)//NamedNodeMap {0: id, 1: type, 2: name, 3: value, id: id, type: type, name: name, value: value, length: 4}
      
// 6- 它们的名字是大小写不敏感的(id 与 ID 相同)。
    console.log(passwordEl.getAttribute('VALUE'))//123456
      
// 7- 它们的返回值总是字符串类型的
    // (但是复选框的情况,我们希望获取的是checked的布尔值来进行操作,而不是字符串)
    // 这个时候就需要用到下面讲到的《20、元素的属性(property)》来进行获取和操作设置了
    var checkedEl = formEls.checked
    console.log(typeof checkedEl.getAttribute('checked'))//string
    console.log(typeof checkedEl.checked,checkedEl.checked)//boolean,true

// 8、功能实现取消复选框选中状态
    var btncheckedEl = document.querySelector('.btn')
    btncheckedEl.onclick = function() {
        // checkedEl.checked = !checkedEl.checked
        checkedEl.checked = false
    }

// 9、 功能实现案例:实现点击密码显示跟隐藏
    var btnEl = formEls.btn
    var isflag = false
    btnEl.onclick = function(event) {
        isflag = !isflag
        if(isflag) {
            passwordEl.type = 'text'
            this.value= '隐藏密码'
        }else {
            passwordEl.type = 'password'
            this.value= '显示密码'
        }
    }
  </script> 
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

# 20、元素的属性(property)

  • 理解重点:对于标准的attribute,会在DOM对象上创建与其对应的property属性
<body>
  <div class="box" id="boxid" age="999" abc='hehe'>
  <script>
    var boxEl = document.querySelector('.box')
    console.log(boxEl.id,boxEl.className)//boxid,box
    console.log(boxEl.age,boxEl.abc)//undefined undefined
  </script> 
</body>
1
2
3
4
5
6
7
8
  • 在大多数情况下,它们是相互作用的
    • 改变property,通过attribute获取的值,会随着改变;
    • 通过attribute操作修改,property的值会随着改变;
<body>
  <div class="box" id="boxid">
  <script>
    var boxEl = document.querySelector('.box')
    boxEl.id = 'name'
    console.log(boxEl.getAttribute('id'))//name
    boxEl.setAttribute('id','lyk')
    console.log(boxEl.id)//lyk
  </script> 
</body>
1
2
3
4
5
6
7
8
9
10
  • 除非特别情况,大多数情况下,设置、获取attribute,推荐使用property的方式:
    • 这是因为它默认情况下是有类型的;
<body>
  <label for="football">
    <input type="checkbox" id="football" checked>足球
  </label>
  <button class="btn">选中</button>
  <script>
    var inputEl = document.querySelector('input')
    var btnEl = document.querySelector('.btn')
    
//默认情况下property是有类型的;二attribute返回的总是字符串类型
    console.log(inputEl.checked)//true
    console.log(typeof inputEl.checked)//boolean
    console.log(inputEl.getAttribute('checked'))//''
    console.log(typeof inputEl.getAttribute('checked'))//string
    
    btnEl.onclick = function () {
      if(!inputEl.checked) {
        inputEl.checked = true
        btnEl.textContent = "不选中";
        return
      }
      inputEl.checked = false
      btnEl.textContent = "选中"
    }
  </script> 
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 21、HTML5的 data-* 自定义属性(elem.dataset)

  • 前面我们有学习HTML5的data-*自定义属性,那么它们也是可以在元素的dataset属性中获取到的
<body>
  <div class="box" data-age="19" data-name="lyk" data-flag></div>
  <script>
    var boxEl = document.querySelector('.box')
    console.log(boxEl.dataset, boxEl.dataset.age, boxEl.dataset.name, boxEl.dataset.flag)
  //DOMStringMap {age: "19", name: "lyk", flag: ""}      "19"     "lyk"    ""
  </script>
</body>
1
2
3
4
5
6
7
8

# 22、JavaScript动态修改样式:元素的className和classList和style属性

# 22.1.JavaScript动态修改样式

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

  • 选择一:在CSS中编写好对应的样式,动态的添加class;

  • 选择二:动态的修改style属性;

  • 开发中如何选择呢?

    • 在大多数情况下,如果可以动态修改class完成某个功能,更推荐使用动态class;
    • 如果对于某些情况,无法通过动态修改class(比如精准修改某个css属性的值),那么就可以修改style属性;
  • 接下来,我们对于两种方式分别来进行学习。

# 22.2.元素的className和classList

  • 元素的class attribute,对应的property并非叫class,而是className

    • 这是因为JavaScript早期是不允许使用class这种关键字来作为对象的属性,所以DOM规范使用了className;
    • 虽然现在JavaScript已经没有这样的限制,但是并不推荐,并且依然在使用className这个名称;
  • 我们可以对className进行赋值,它会替换整个类中的字符串

<style>
  .box {
    width:200px;
    height:30px;
  }
  .bar {
    background-color:yellow;
  }
  .active {
    font-size:20px;
    color:red;
  }
</style>
<body>
  <div class="box bar">我是一个div元素</div>
  <button class="btn">给上面元素的class属性值替换成:active</button>
  <script>
    var boxEl = document.querySelector('.box')
    var btnEl = document.querySelector('.btn')
    btnEl.onclick = function() {
      boxEl.className = 'active'
    }
  </script> 
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  • 如果我们需要添加或者移除单个的class,那么可以使用classList属性
  • elem.classList 是一个特殊的对象:
    • elem.classList.add (class) :添加 一个或多个 类
    • elem.classList.remove(class):移除 一个或 多个类。
    • elem.classList.toggle(class) :如果类不存在就添加类,存在就移除它。
    • elem.classList.contains(class):检查给定类,返回 true/false。
  • classList是可迭代对象,可以通过for of进行遍历。
<style>
  .box {
    width:200px;
    height:30px;
  }
  .bar {
    background-color:yellow;
  }
  .active {
    font-size:20px;
    color:red;
  }
  .desc {
    height:60px;
    background-color:pink;
  }
</style>
<body>
  此时下面div,class类的值为:<span class="boxClass" style="color:red;"></span>
  <div class="box bar">我是一个div元素</div>
  <button class="btn">添加一个类:active</button>
  <button class="btn">移除一个类:active</button>
  <button class="btn">没有desc类,添加它</button>
  <script>

    var boxEl = document.querySelector('.box')
    var btnEls = document.querySelectorAll('.btn')
    var boxClassEl = document.querySelector('.boxClass')

    setInterval(function(){//利用无限循环执行js函数,来时刻获取div中的class值
      boxClassEl.textContent = boxEl.classList
    },1)

// 1- elem.classList 是一个特殊的对象【可迭代对象】: (拿到元素所有class类的属性值)
   console.log(boxEl.classList)// {"0":"box","1":"bar"}

// 2- elem.classList.add (class) :添加一个/多个类
   btnEls[0].onclick = function() {
      boxEl.classList.add('active')
    //   boxEl.classList.add('active','desc')
    }

// 3- elem.classList.remove(class):移除一个/多个类。
    btnEls[1].onclick = function() {
      boxEl.classList.remove('active')
    //   boxEl.classList.remove('active','bar')
    }

// 4- elem.classList.toggle(class) :如果类不存在就添加类,存在就移除它。
// 5- elem.classList.contains(class):检查给定类,返回 true/false。 
    btnEls[2].onclick = function() {
      boxEl.classList.toggle('desc')

      if(boxEl.classList.contains('desc')){
        console.log(boxEl.classList.contains('desc'))//true
        this.textContent = "有desc类,移除它"
      }else {
        console.log(boxEl.classList.contains('desc'))//false
        this.textContent = "没有desc类,添加它"
      }
    }

// 6- classList是可迭代对象,可以通过for of进行遍历。
    boxEl.classList.forEach(function(item,index,array){
        console.log(item,index,array)
    })
      
    for(var item of boxEl.classList) {
        console.log(item)
    }
  </script> 
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

# 22.3.元素的style属性

  • 如果需要单独修改某一个CSS属性,那么可以通过style来操作:
    • 对于多词(multi-word)属性,使用驼峰式 camelCase
element.style.width = '100px'
element.style.height = '30px'
element.style.backgroundColor = 'red'
1
2
3
  • 如果我们将值设置为空字符串,那么会使用CSS的默认样式(了解):
element.style.display = ""
1
  • 多个样式的写法,我们需要使用cssText属性: (注意这里后面字符串 不能用单引号或者双引号;要用反引号:``)
    • 不推荐这种用法,因为它会替换整个字符串;
<body>
  <div class="box" style="color:red;">我是div元素</div>
  <button class='btn'>element.style.cssText属性改变样式(会替换内联样式的全部字符串)</button>
  <script>
    var btnEl = document.querySelector('.btn')
    var boxEl = document.querySelector('.box')
    boxEl.style.height= '100px'
    boxEl.style.width = '150px'
    boxEl.style.backgroundColor = 'yellow'
    boxEl.style.textAlign = 'center'
    
    btnEl.onclick = function() {
      boxEl.style.cssText = `
        width:200px;
        height:130px;
        background-color:pink;
      `
    }
  </script> 
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 23、元素style样式的读取 - getComputedStyle

  • 如果我们需要读取样式:

    • 对于内联样式,是可以通过style.*的方式读取到的;
    • 对于style、css文件中的样式,是读取不到的;
  • 这个时候,我们可以通过getComputedStyle的全局函数来实现

<style>
  .box {
    background-color:yellow;
    width:150px;
    height:30px;
    text-align:center;
  }
</style>
<body>
  <div class="box" style="color:red;">我是div元素</div>
  <script>
    var boxEl = document.querySelector('.box')
    console.log(getComputedStyle(boxEl).color)//rgb(255, 0, 0)
    console.log(getComputedStyle(boxEl).backgroundColor)//rgb(255, 255, 0)
    console.log(getComputedStyle(boxEl).width)//150px
    console.log(getComputedStyle(boxEl).textAlign)//center
    console.log(boxEl.style.width)//""
    console.log(boxEl.style.color)//red
  </script> 
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 24、创建元素、插入元素、移除和克隆元素及旧的元素操作方法补充

# 24.1.创建元素document.createElement(tag)

  • 前面我们使用过 document.write 方法写入一个元素:

  • 这种方式写起来非常便捷,但是对于复杂的内容、元素关系拼接并不方便;

  • 它是在早期没有DOM的时候使用的方案,目前依然被保留了下来;

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

    • 步骤一:创建一个元素;
    • 步骤二:插入元素到DOM的某一个位置;
  • 创建元素: document.createElement(tag) tag:元素标签名

<style>
  div {
    background-color:yellow;
    color:red;
    width:500px;
    text-align:center;
    margin:10px 0;
  }
</style>
<body>
  <button class="btn">插入一个div元素</button>
  <div class="box">我是div元素</div>
  <script>
    var boxEl = document.querySelector('.box')
    var btnEl = document.querySelector('.btn')
    var count = 1
    btnEl.onclick = function () {
        //创建一个div元素
        var divCreateEl = document.createElement('div')
        //设置div元素中的文本内容
        divCreateEl.textContent = `我是新添加的div元素${count++}`
        //在div.box元素后面 插入新创建的 div元素
        boxEl.after(divCreateEl)
    }
  </script> 
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 24.2.插入元素:append、prepend、before、after、replaceWith

  • 插入元素的方式如下:
    • 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 替换为给定的 一个或多个 节点或字符串

jUjzm8.png

<style>
  div {
    background-color:yellow;
    color:red;
    width:500px;
    text-align:center;
    margin:10px 0;
    border:1px solid #000;
  }
  span {
    background-color:pink;
    margin:0 10px
  }
</style>
<body>
  <button class="btn">在div元素里面末尾插入一个 新创建的span元素[node.append]</button> <br>
  <button class="btn">在div元素里面开头插入一个 新创建的span元素[node.prepend]</button> <br>
  <button class="btn">在div元素前面插入一个 新创建的span元素[node.before]</button> <br>
  <button class="btn">在div元素后面插入一个 新创建的span元素[node.after]</button> <br>
  <button class="btn">将div元素替换成 新创建的span元素[node.replaceWith]</button>
  <div class="box">
    <i style="display:block;margin:10px;">我是div.box元素中的i元素</i>
  </div>
  <script>
    var boxEl = document.querySelector('.box')
    var btnEls = document.querySelectorAll('.btn')
    var count = 1
    btnEls[0].onclick = function () {
        //创建一个span元素
        var spanCreateEl = document.createElement('span')
        //设置span元素中的文本内容
        spanCreateEl.textContent = `我是新添加的span元素${count++}`
        //在div.box元素里面末尾 插入新创建的 span元素
        boxEl.append(spanCreateEl)
    }

    btnEls[1].onclick = function () {
        //创建一个span元素
        var spanCreateEl = document.createElement('span')
        //设置span元素中的文本内容
        spanCreateEl.textContent = `我是新添加的span元素${count++}`
        //在div.box元素里面开头 插入新创建的 span元素
        boxEl.prepend(spanCreateEl)
    }

    btnEls[2].onclick = function () {
        //创建一个span元素
        var spanCreateEl = document.createElement('span')
        //设置span元素中的文本内容
        spanCreateEl.textContent = `我是新添加的span元素${count++}`
        //在div.box元素前面 插入新创建的 span元素
        boxEl.before(spanCreateEl)
    }

    btnEls[3].onclick = function () {
        //创建一个span元素
        var spanCreateEl = document.createElement('span')
        //设置span元素中的文本内容
        spanCreateEl.textContent = `我是新添加的span元素${count++}`
        //在div.box元素后面 插入新创建的 span元素
        boxEl.after(spanCreateEl)
    }

    btnEls[4].onclick = function () {
        //创建一个span元素
        var spanCreateEl = document.createElement('span')
        //设置span元素中的文本内容
        spanCreateEl.textContent = `我是新添加的span元素${count++}`
        //在div.box元素后面 插入新创建的 span元素
        boxEl.replaceWith(spanCreateEl)
    }
  </script> 
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

# 24.3.移除和克隆元素:remove、cloneNode

  • 移除元素我们可以调用元素本身的remove方法:

  • 如果我们想要复制一个现有的元素,可以通过cloneNode方法:

    • 可以传入一个Boolean类型的值(true/false),来决定是否是深度克隆;
    • 深度克隆会克隆对应元素的子元素,否则不会;
<style>
  div {
    background-color:yellow;
    color:red;
    width:500px;
    text-align:center;
    margin:10px 0;
    border:1px solid #000;
  }
  span {
    background-color:pink;
    display:inline-block;
    width:170px;
    height:56px;
  }
</style>
<body>
  <button class="btn">移除dov.box元素[node.remove]</button> <br>
  <button class="btn">深度克隆一个span元素,并插入到div.box元素前面[node.cloneNode(true)]</button> <br>
  <button class="btn">非深度克隆一个span元素,并插入到div.box元素前面[node.cloneNode(false)]</button> 

  <div class="box">我是div.box元素</div>
  <span>
      我是一个span元素哦!
      <i style="color:red;">span的子元素:i元素</i>
  </span>
  <script>
    var boxEl = document.querySelector('.box')
    var btnEls = document.querySelectorAll('.btn')
    var count = 1
    // 点击删除div.box元素
    btnEls[0].onclick = function () {
        console.log('dgd')
        boxEl.remove()
    }

    //点击深度克隆一个span元素 并插入到div.box元素前面
    var spanEl = document.querySelector('span')
    btnEls[1].onclick = function () {
      var cloneSpanEl = spanEl.cloneNode(true)
      boxEl.before(cloneSpanEl)
    }
    //点击非深度克隆一个span元素 并插入到div.box元素前面
    btnEls[2].onclick = function () {
      var cloneSpanEl = spanEl.cloneNode(false)
      boxEl.before(cloneSpanEl)
    }
  </script> 
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

# 24.4.旧的元素操作方法

  • 在很多地方我们也会看到一些旧的操作方法:
    • parentElem.appendChild(node): 在parentElem的父元素最后位置添加一个子元素
    • parentElem.insertBefore(node, nextSibling): 在parentElem的nextSibling前面插入一个子元素;
    • parentElem.replaceChild(node, oldChild): 在parentElem中,新元素替换之前的oldChild元素;
    • parentElem.removeChild(node) 在parentElem中,移除某一个元素;

# 25、DOM操作案例练习

# 25.1.自定义创建一个元素,并插入到body中展示

<style>
  div,a,span,p,i,ul,li,img,input {
    color:red;
    background-color:yellow;
    margin:10px;
  }
</style>
<body>
  <button class="btn" style="margin:10px;">开始自定义创建一个元素</button><br>
  <script>
    var bodyEl = document.body
    var btnEl = document.querySelector('.btn')
    btnEl.onclick = function (event) {
      var elemTag = prompt('请输入你需要创建的元素的标签名:')
      if(!elemTag) return
      var htmlContentStr = prompt('请输入创建元素里面的内容(可以是HTML结构字符串,会进行解析!)')
      console.log(elemTag,htmlContentStr)
      var createEl = document.createElement(elemTag)
      createEl.innerHTML = htmlContentStr
      this.after(createEl)
    }

  </script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 25.2.实现秒杀活动倒计时功能

<style>
    .countdown {
      color: #f00;
      font-size: 36px;
    }

    .countdown .time {
      background-color: #f00;
      color: #fff;
      display: inline-block;
      padding: 5px;
      border-radius: 3px;
    }
</style>
<body>
  <button class="btn" style="margin:10px;">设置秒杀截止时间</button><br>
  <div class="countdown">
    <span>活动秒杀倒计时:</span>
    <span class="time date">00</span>
    <span class="split"></span>
    <span class="time hours">00</span>
    <span class="split"></span>
    <span class="time minutes">00</span>
    <span class="split"></span>
    <span class="time seconds">00</span>
    <span class="split"></span>
    <span class="time milli-seconds">000</span>
    <span class="split">毫秒</span>
    <div class="time-out" style="font-size:50px;"></div>
  </div>
  <div class="bar" style="margin-top:30px;font-size:30px;">
      设置的活动截止时间:
      <span class="set-date"></span>
  </div>
  <script>
    //封装格式化时间函数:在个位数也始终展示为两位数
    function padLeft (dateNum,count,sup) {
      count = count || 2
      sup = sup || '0'

      return String(dateNum).padStart(count,sup)
    }

    //获取元素
    var dateEl = document.querySelector('.date')
    var hoursEl = document.querySelector('.hours')
    var minutesEl = document.querySelector('.minutes')
    var secondsEl = document.querySelector('.seconds')
    var millisecondsEl = document.querySelector('.milli-seconds')
    var btnEl = document.querySelector('.btn')
    var spanDateEl = document.querySelector('.set-date')
    var timeOutEl =document.querySelector('.time-out')

    //设置定时器的初始值为:null
    var timer = null
  
    //点击按钮设置秒杀截止时间,并形成倒计时
    btnEl.onclick = function () {
      //定义一个秒杀截止时间
      var endDate = new Date()

      //获取你设置的时间日期的数字字符串,并设置至秒杀截止时间(endDate)对象中
      var date = prompt('请输入你设置的date(多少号):')
      if(!date || !Number(date)) return alert('设置失败!(输入的必须是纯数字/浮点数内容哦)')
      console.log(timer)
      //清除上一个定时器
      clearInterval(timer)
      var hours = prompt('请输入你设置的hours(多少时):')
      var minutes = prompt('请输入你设置的minutes(多少分):')
      var seconds = prompt('请输入你设置的seconds(多少秒):')
      var dateEndDate = endDate.setDate(date)
      var hoursEndDate = endDate.setHours(hours)
      var minutesEndDate = endDate.setMinutes(minutes)
      var secondsEndDate = endDate.setSeconds(seconds)

      //设置的活动截止时间展示:
      spanDateEl.textContent = endDate

      //重新设置截止时间:清除 开抢提示
      timeOutEl.textContent = ''

      //记录定时器
      timer = setInterval(fn,1)
      
      function fn(){
      //每执行一次该函数,获取一次当时的时间
        startDate = new Date()

      //时间戳/1000 得到一个秒数
        var timeDifference = (endDate - startDate) / 1000  
        //将时间戳/秒 转成对应的天、时、分、秒、毫秒数
        dateEl.textContent = padLeft(Math.floor((timeDifference /3600/24)))
        hoursEl.textContent = padLeft(Math.floor((timeDifference /3600) % 24))
        minutesEl.textContent = padLeft(Math.floor((timeDifference / 60) % 60))
        secondsEl.textContent = padLeft(Math.floor(timeDifference % 60))
        millisecondsEl.textContent = padLeft(Math.floor((timeDifference*1000) % 1000),3)
      //判断时间是否到达截止时间
        if((endDate-startDate) < 100) {
            timeOutEl.textContent = '时间到啦,开抢商品吧!'
            clearInterval(timer)
            millisecondsEl.textContent = padLeft(0,3)
        }
      }
    }

  </script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107

# 26、元素的大小、滚动

  • clientWidth:contentWith+padding(不包含滚动条)
  • clientHeight:contentHeight+padding
  • clientTop:border-top的宽度
  • clientLeft:border-left的宽度
  • offsetWidth:元素完整的宽度
  • offsetHeight:元素完整的高度
  • offsetLeft:距离父元素的x
  • offsetHeight:距离父元素的y
  • scrollHeight:整个可滚动的区域高度
  • scrollTop:滚动部分的高度

jUjHFH.png

# 27、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) 将页面滚动至 绝对坐标;
最后更新时间: 2022/07/08, 11:44:07
彩虹
周杰伦