Skip to content

2-1DOM及相关案例(二) ​

本页对应 第二章WebAPIs,共 5 段视频,约 41 分钟。

学习目标 ​

  • 能够使用 getElementById、getElementsByClassName、getElementsByTagName、getElementsByName 获取指定 DOM 元素,并正确判断返回结果是单个元素还是类数组。
  • 能够通过 DOM 对象读取、修改、新增和清空元素内容,分别使用 innerText 和 innerHTML 完成文本写入、内容替换与空字符串删除。
  • 能够对比说明 innerText 与 innerHTML 的差异,并在需要解析 HTML 标签时选择 innerHTML,在仅写入纯文本时选择 innerText。
  • 能够使用字符串拼接和 innerHTML 将循环生成的列表项一次性渲染到指定父元素中,完成学成在线案例的重构。
  • 能够使用随机数从数组中抽取姓名并写入指定 span,使用 splice 删除已中奖姓名,完成一等奖抽取且不重复。
  • 能够复用一等奖逻辑完成二、三等奖抽取,检查随机索引、目标元素和删除位置,确保三个奖项姓名不重复。

知识点 ​

DOM 是什么:先理解它解决的问题 ​

DOM 的英文是 Document Object Model,中文叫“文档对象模型”。本节笔记给它的定位很明确:DOM 用来操作网页文档,开发网页特效和实现用户交互。

为什么需要 DOM?因为网页写好后,如果只用 HTML 和 CSS,页面内容是静态的:文字、盒子、图片都在那里,但 JavaScript 很难直接“读到”或“改掉”它们。DOM 提供了一层可被程序操作的对象模型,让 JavaScript 能访问网页内容,并修改页面。

怎么想?不要把 DOM 想成另一门语法,而要想成“浏览器把网页内容整理成对象,JavaScript 通过对象去操作页面”。所以核心不是背 API,而是记住一条链路:先拿到对象,再改对象属性或调用方法,页面会跟着变化。

DOM 把网页文档抽象为对象示意图

易错点:DOM 不是 CSS,也不是 HTML 标签本身;它是浏览器提供给程序操作网页内容的接口。后面所有“获取元素、改内容、改样式”的操作,都建立在这个对象思想上。

核心思想:把网页内容当成对象处理 ​

本节笔记强调:DOM 的核心思想就是把网页内容当做对象来处理,通过对象的属性和方法对网页内容操作。

为什么需要这种思想?因为对象有属性、有方法,程序才能“查”和“改”。例如一个盒子在页面上是标签,在 DOM 里可以变成一个对象;你读取它的内容、替换它的文字,本质上是在操作这个对象。

怎么想?遇到 DOM 题目时先问自己三件事:我要操作谁?这个“谁”怎么获取成对象?我要改它的哪个属性或调哪个方法?转写稿里反复出现“先获取 DOM 对象,再操作对象属性”,就是这个推导过程。

怎么做?典型流程是:用 document 提供的方法获取元素对象,再用对象属性完成增、删、改、查。转写稿中演示过用 console.log 验证是否拿到元素,用 console.dir 展开对象查看属性,这都是调试手段。

易错点:不要一上来就写赋值,先确认对象拿到了没有;变量名、选择器写错时,对象可能是 null 或空伪数组,后面再改属性就会报错。

document 对象:网页内容的入口 ​

笔记说:document 是 DOM 里提供的一个对象,是 DOM 顶级对象,作为网页内容的入口,所以它提供的属性和方法都是用来访问和操作网页内容的。例子是:

js
document.write()

为什么需要 document?因为 JavaScript 要操作页面,总得有一个入口。document 就代表当前网页文档,通过它可以进一步获取元素、写入内容。

怎么想?可以把它理解成“页面的总对象”。要拿页面里的盒子、段落、输入框,通常都从 document 开始,例如 document.querySelector()、document.getElementById() 等。转写稿中还提到其他获取方式:getElementById、getElementsByClassName、getElementsByTagName、getElementsByName。其中 getElementById 按 ID 获取单个元素;其余几个通常返回伪数组。转写稿里口述的“尾数组”应纠正为“伪数组”。

怎么做?比如按 ID 获取时,参数直接写 ID 名,不写 #;按类名获取时,参数直接写类名,不写 .。这是转写稿调试时特别提醒的点。

易错点:document 是入口,不是某个具体盒子;不要把它和普通元素对象混在一起。获取多个元素时返回的是伪数组,不能直接当单个元素改 innerText。

从获取对象到操作内容:innerText 与 innerHTML ​

虽然本节笔记重点是 DOM 简介,但转写稿用“操作元素内容”说明了 DOM 的核心用法:先获取对象,再通过对象属性操作页面。常见两个属性是 innerText 和 innerHTML。

为什么需要它们?因为很多交互最终表现为“改盒子里的内容”。怎么想?如果只改纯文字,用 innerText;如果内容里包含标签并希望标签生效,用 innerHTML。怎么做?转写稿中的思路可以简化为:

js
const box = document.querySelector('.box')
console.log(box.innerText)          // 查
box.innerText = '迪丽热巴'           // 改
box.innerHTML = '<strong>迪丽热巴</strong>' // 解析标签

转写稿还演示了“增”和“改”本质相同:给属性一个新值;“删”可以给空字符串。易错点:innerText 不解析标签,写 <strong> 会原样显示;innerHTML 会解析标签,所以能配合 CSS 做出加粗、变色等效果。复制代码时尤其要检查变量名和随机数编号,转写稿里就出现过复制二、三等奖代码后忘记改 random3 导致重复的调试过程。

调试与易错点:先验证,再修改 ​

为什么需要调试?DOM 操作是“改对象影响页面”,一旦选择器或属性写错,页面不会按预期变化。怎么想?先验证对象是否存在,再验证属性值,最后看页面。怎么做?转写稿里的做法是先 console.log(box) 看是否拿到元素,再用 console.dir(box) 展开对象,找到 innerText、innerHTML 等属性,改完保存后到控制台和 Elements 面板确认。

易错点归纳:getElementById 参数不加 #;getElementsByClassName 参数不加 .;多个元素返回伪数组;innerText 不解析标签;删除内容给空字符串;复制代码要逐个改选择器、变量名和编号。掌握“先获取对象,再操作属性”的顺序,DOM 简介这一节就真正落地了。

先获取对象,才能操作页面 ​

为什么需要:DOM 操作的本质是“先拿到元素对象,再改对象的属性”,对象改了,页面标签才会跟着变。
怎么想:把 document 当成入口,用 CSS 选择器去找元素;找第一个用 querySelector,找全部用 querySelectorAll。
怎么做:先写选择器字符串,再用 console.log 验证是否拿到;想详细看对象结构,用 console.dir(box)。
易错点:参数必须是字符串;匹配不到时返回 null,后面再点属性会报错;不要凭感觉写选择器,先打印验证。

利用 CSS 选择器获取第一个 DOM 元素 ​

为什么需要:页面里常只要操作第一个匹配元素,例如第一个 div、列表里的第二个 li。
怎么想:querySelector 的规则和 CSS 选择器一致,CSS 怎么写,参数就怎么写。
怎么做:

javascript
// 利用css选择器来获取DOM元素
// 1. document.querySelector() 选择指定css选择器的第一个元素
// 1.1 参数是字符串的css选择器
const box = document.querySelector('div')
console.log(box)

// 1.2 返回值是dom对象
console.log(typeof box)  // object
console.dir(box)

const box = document.querySelector('.box')
console.log(box)

const li = document.querySelector('ol li')
console.log(li)   // 只选择满足条件的第一个元素li

const li = document.querySelector('ol li:nth-child(2)')
console.log(li)   // 选择第2个小li

// 1.3 如果获取不到则返回 null
const p = document.querySelector('p')
console.log(p)  // null

调试时可以先打印 box,确认是不是目标元素;再用 console.dir(box) 展开对象,观察 DOM 对象里的属性。
易错点:querySelector('ol li') 只拿第一个,不是全部;p 不存在时是 null,不是 undefined;示例代码分段演示,实际写在一起不要重复声明同名 const。

querySelectorAll 获取所有匹配元素 ​

为什么需要:列表、导航、多个卡片等场景要一次拿到全部匹配项。
怎么想:返回值不是普通数组,而是 NodeList 伪数组;伪数组有长度和索引,但没有 push、pop、splice 等数组方法。
怎么做:

javascript
// 2. document.querySelectorAll() 选择指定css选择器的所有元素
// 2.1 参数还是字符串的css选择器
const lis = document.querySelectorAll('.nav li')

// 2.2 返回值是一个伪数组里面包含了所有的dom对象 li
console.log(lis)

// 2.3 伪数组
// (1) 有长度和索引号
// (2) 没有数组的一些常用方法 比如 push  pop  splice等方法
// lis.push(1)
// console.log(lis)  // 因为lis是伪数组无法使用push方法所以报错

// 2.4 利用循环遍历伪数组得到里面的每一个元素对象
for (let i = 0; i < lis.length; i++) {
  console.log(lis[i]) // 里面的每一个元素对象
}

// 2.5 即使只有1个元素,我们querySelectorAll 获得的也是一个伪数组,里面只有1个元素而已

const boxs = document.querySelectorAll('.box')
console.log(boxs)

调试时若执行 lis.push(1) 报错,正好说明它是伪数组;要操作每一项,用 for 循环按索引取。
易错点:不要直接对 querySelectorAll 结果用数组方法;即使只匹配一个元素,它仍然返回伪数组。

其他获取方式(了解) ​

为什么需要:读旧项目或别人代码时,要能认出这些写法。
怎么想:按 id 获取单个元素;按类名、标签名、name 属性获取通常是伪数组。
怎么做:

语法实例描述
getElementByIddocument.getElementById('box')根据id获取元素,单个元素
getElementsByTagNamedocument.getElementsByTagName('li')根据标签名获取元素,伪数组
getElementsByClassNamedocument.getElementsByClassName('one')根据类名获取元素,伪数组
getElementsByNamedocument.getElementsByName('sex')根据name属性值获取元素,伪数组
javascript
// 1. getElementById   根据id获取
const box = document.getElementById('box')
console.log(box)

// 2. getElementsByClassName   根据类名获取  返回伪数组
const items = document.getElementsByClassName('item')
console.log(items)

// 3. getElementsByTagName  根据标签名获取 返回伪数组
const ps = document.getElementsByTagName('p')
console.log(ps)

// 4. getElementsByName  根据name属性获取 返回伪数组
const inputs = document.getElementsByName('username')
console.log(inputs)

转写稿里的调试过程是:getElementById('box') 能拿到 id 盒子,注意参数不加 #;getElementsByClassName('item') 拿到三个元素的伪数组,类名不加点;getElementsByTagName('p') 返回伪数组;getElementsByName('username') 即使只匹配一个 input,返回的仍是伪数组。
易错点:getElementById 只返回单个元素,参数不加 #;getElementsByClassName 参数不加 .;带 s 的 getElements... 通常返回伪数组,不能当普通数组直接用。

课件图示 ​

课件第68页:描述 属性/方法 效果 获取DOM对象 document.querySelect

课件第9页:Web APIs 课程安排 7天 第二天 DOM-事件基础 注册事件、tab栏切

课件第24页:2.1 根据CSS选择器来获取DOM元素 (重点) 1. 选择匹配的第一个元素

课件第26页:2.1 根据CSS选择器来获取DOM元素 (重点) 2. 选择匹配的多个元素对象

课件第28页:总结 总结 1. DOM中获取页面中的元素我们最常用哪两种方式? Ø query

课件第30页:2.2 其他获取DOM元素方法(了解) 语法 实例 描述 getElementB

课件第56页:4.3 操作表单元素属性 l 表单很多情况,也需要修改属性,比如点击眼睛,可以看

课件第61页:4.4 自定义属性 l 标准属性: 标签天生自带的属性 比如class、id、t

代码示例 ​

示例一:获取 DOM 对象 + 操作元素内容(innerText / innerHTML) ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>获取DOM对象与操作元素内容</title>
  <style>
    .box  { width: 200px; height: 50px; line-height: 50px; background: #eee; margin-bottom: 10px; }
    .box1 { width: 200px; height: 50px; line-height: 50px; background: #ddf; margin-bottom: 10px; }
  </style>
</head>
<body>
  <div class="box">鼓励占拉</div>
  <div class="box1"></div>

  <ol class="nav">
    <li>第1个小li</li>
    <li>第2个小li</li>
    <li>第3个小li</li>
  </ol>

  <script>
    // ===== 1. querySelector:选择指定css选择器的第一个元素 =====
    const box = document.querySelector('.box')
    console.log(box)              // 拿到第一个匹配的元素对象
    console.log(typeof box)       // object —— 返回的是 dom 对象
    console.dir(box)              // 以对象形式展开查看属性

    const li = document.querySelector('ol li')                  // 只选第一个 li
    console.log(li)
    const li2 = document.querySelector('ol li:nth-child(2)')    // 选第2个 li
    console.log(li2)

    // 获取不到则返回 null
    const p = document.querySelector('p')
    console.log(p)                // null

    // ===== 2. querySelectorAll:选择指定css选择器的所有元素 =====
    const lis = document.querySelectorAll('.nav li')
    console.log(lis)              // 伪数组 NodeList,有 length 和索引号
    // lis.push(1)                // 报错!伪数组没有 push / pop / splice 等方法

    for (let i = 0; i < lis.length; i++) {
      console.log(lis[i])         // 循环遍历伪数组,拿到每一个元素对象
    }

    // 即使只有1个元素,querySelectorAll 得到的也是伪数组
    const boxs = document.querySelectorAll('.box')
    console.log(boxs)

    // ===== 3. 操作元素内容:查、改、增、删 =====
    console.log(box.innerText)          // 查:鼓励占拉
    box.innerText = '迪丽热巴'           // 改:用新值替换旧值

    const box1 = document.querySelector('.box1')
    box1.innerText = '佟丽娅'            // 增:原来没内容,给新值即新增

    // box.innerText = ''               // 删:给空字符串即清空内容
    // box1.innerText = ''

    // ===== 4. innerText 不能解析标签 =====
    box.innerText = '<strong>迪丽热巴</strong>'   // 标签被原样显示,不会加粗

    // ===== 5. innerHTML 能解析标签 =====
    box.innerHTML = '<strong>迪丽热巴</strong>'   // 加粗生效
  </script>
</body>
</html>

关键行与易错点

  • document.querySelector('.box'):参数是字符串形式的 CSS 选择器,类名要写 .、id 要写 #;匹配不到返回 null,后续直接点属性会报错。
  • document.querySelectorAll() 返回的是伪数组 NodeList,只能用 length 和索引号遍历,不能调用 push、splice 等数组方法;即使只匹配到一个,也是伪数组。
  • innerText 是内部文本的意思,只能渲染纯文本,不解析标签——写 <strong> 会把标签原样显示出来。
  • innerHTML 用法与 innerText 完全一致,但会解析标签;HTML 四个字母必须全大写。
  • 增、改、删本质都是"给属性赋新值":新值替换旧值是改,原本为空给新值是增,给空字符串 '' 就是删。
  • 顺序不能颠倒:先获取 DOM 对象,再通过"对象.属性"操作内容;对象修改后,页面标签会自动跟着变。

示例二:重构学成在线案例(循环拼接字符串 + innerHTML) ​

js
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>重构学成在线案例</title>
  <style>
    .box { width: 600px; margin: 50px auto; }
    .box-hd {
      height: 60px; line-height: 60px; text-align: center;
      font-size: 20px; background: #f5f5f5;
    }
    .common { display: flex; flex-wrap: wrap; list-style: none; padding: 0; margin: 0; }
    .common li { width: 33.33%; box-sizing: border-box; padding: 10px; }
    .common li a { display: block; text-decoration: none; color: #333; }
    .common li .pic {
      height: 60px; line-height: 60px; text-align: center; background: pink;
    }
  </style>
</head>
<body>
  <!-- 页面结构:不变的 hd 直接写在 html 里,只有 ul 里的 li 需要动态渲染 -->
  <div class="box">
    <div class="box-hd">学成在线</div>
    <div class="box-bd">
      <ul class="common"></ul>
    </div>
  </div>

  <script>
    // 1. 准备数据(11 条,比原来多加了一条)
    const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

    // 2. 利用循环生成多个 li 标签,用字符串拼接
    let str = ''                          // 必须用 let,循环中会反复重新赋值
    for (let i = 0; i < data.length; i++) {
      str += `<li>
                <a href="#">
                  <div class="pic">课程${data[i]}</div>
                  <h4>学成在线 - 课程${data[i]}</h4>
                </a>
              </li>`
    }
    console.log(str)

    // 3. 获取 li 的父亲 ul
    const ul = document.querySelector('.common')

    // 4. 把拼接好的字符串作为元素内容放入 ul
    ul.innerHTML = str                    // 只能用 innerHTML,因为字符串里包含标签
  </script>
</body>
</html>

关键行与易错点

  • 只把会变化的部分交给 JS 渲染:box-hd 和 ul 外壳不变,直接写在 HTML 中,不再用 document.write 把整页打印一遍。
  • let str = '' 必须用 let:循环中 str += ... 反复重新赋值,写成 const 会报错。
  • for 循环里的 let i 也不能改成 const,因为 i 每次循环都会变化。
  • 第 4 步必须用 innerHTML 而不是 innerText:拼接出来的字符串中包含 <li>、<a>、<div> 等标签,需要被浏览器解析成真正的元素。
  • 顺序是"先获取盒子,再往里塞内容";ul 在循环结束后只获取一次即可,不要放进循环里重复获取。
  • 这种"先拼字符串 → 再 innerHTML 赋值"的两步思路,是以后渲染列表数据的常用写法。

示例三:年会抽奖案例(一等奖、二等奖、三等奖) ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>年会抽奖</title>
  <style>
    .wrapper {
      width: 400px; margin: 50px auto; padding: 20px;
      background: #fdf1f1; text-align: center;
    }
    .wrapper strong { display: block; font-size: 22px; margin-bottom: 10px; }
    h1, h3, h5 { margin: 10px 0; }
    span { color: pink; }
  </style>
</head>
<body>
  <div class="wrapper">
    <strong>年会抽奖</strong>
    <h1>一等奖:<span class="one">???</span></h1>
    <h3>二等奖:<span class="two">???</span></h3>
    <h5>三等奖:<span class="three">???</span></h5>
  </div>

  <script>
    const arr = ['迪丽热巴', '马尔扎哈', '鼓励占拉', '佟丽娅']

    // ===== 一等奖 =====
    // 1.1 利用随机数选取数组中的名字
    const random = Math.floor(Math.random() * arr.length)
    console.log(random, arr[random])

    // 1.2 把名字放入对应的盒子
    const one = document.querySelector('.one')
    one.innerText = arr[random]        // 只放文字,用 innerText 即可

    // 1.3 不允许重复抽奖,把抽出的名字从数组中删除
    arr.splice(random, 1)
    console.log(arr)

    // ===== 二等奖(做法与一等奖完全一致)=====
    const random2 = Math.floor(Math.random() * arr.length)
    const two = document.querySelector('.two')
    two.innerText = arr[random2]
    arr.splice(random2, 1)
    console.log(arr)

    // ===== 三等奖 =====
    const random3 = Math.floor(Math.random() * arr.length)
    const three = document.querySelector('.three')
    three.innerText = arr[random3]
    arr.splice(random3, 1)
    console.log(arr)
  </script>
</body>
</html>

关键行与易错点

  • Math.floor(Math.random() * arr.length) 生成 0 ~ arr.length-1 的随机整数,正好作为数组的索引号;每删掉一个人,arr.length 会自动变小,第二次抽取自然只从剩下的 3 个里抽。
  • 随机数变量用 const 没问题,因为它声明后没有再被重新赋值;但如果写成 let 之后再重新赋值,也不能改成 const。
  • arr.splice(random, 1) 的两个参数含义:从哪个索引开始删、删几个。第一个参数必须用刚抽中的 random,删 1 个,才能保证抽过的人不会再出现在后面的奖项里。
  • 最容易出错的地方是复制代码后忘记改变量名:二等奖要用 random2 / .two,三等奖要用 random3 / .three,写串了会出现重复中奖或某个奖项一直是 ???。
  • innerText 与 innerHTML 这里都可以,因为只放纯文字、不涉及标签;养成"有标签用 innerHTML,纯文字用 innerText"的习惯即可。

常见错误 ​

  1. 现象:querySelector('box') 选不到元素。 → 原因: 参数必须是合法 CSS 选择器字符串,类名漏写 .。 → 修法: 改为 querySelector('.box'),并在控制台先验证返回值是否为 null。
  2. 现象:querySelectorAll 的结果调用 push 报错。 → 原因: 返回的是 NodeList 伪数组,没有数组常用方法。 → 修法: 用 for 循环按索引遍历;确需数组方法时先转换再使用。
  3. 现象:用 innerText 写入 <strong>文字</strong>,页面直接显示标签原文。 → 原因: innerText 不解析标签。 → 修法: 需要标签生效时改用 innerHTML,并确认 HTML 全大写。
  4. 现象:抽奖删除时写成 arr.splice(name, 1),结果删错或不删。 → 原因: splice 第一个参数是起始索引,不是姓名。 → 修法: 用抽中的随机索引删除,如 arr.splice(random, 1)。
  5. 现象:复制二等奖代码做三等奖,页面出现重复姓名或写错位置。 → 原因: 复制后未同步修改 random2、to、three 等变量和选择器。 → 修法: 逐项改随机数变量、目标盒子类名、删除索引用量,并刷新多次验证。
  6. 现象:querySelectorAll('.box') 明明只匹配一个元素,却直接写 boxs.innerText 无效。 → 原因: 返回的仍是伪数组,不是单个 DOM 对象。 → 修法: 用 boxs[0] 取到元素后再操作。
  7. 现象:const 声明的拼接字符串或随机数被重新赋值时报错。 → 原因: const 不允许重新赋值。 → 修法: 循环中会反复赋值的变量用 let;确认不再重新赋值再用 const。

练习 ​

  1. 使用 querySelector 获取页面中第一个 .box,再用 querySelectorAll 获取所有 .nav li,用循环输出每个 li 的文本内容。
  2. 准备两个空盒子,分别用 innerText 和 innerHTML 写入包含 <strong> 的姓名内容,观察并说明两者显示差异。
  3. 根据给定数组生成多个 li 字符串,获取目标 ul,用 innerHTML 一次性渲染到页面中。
  4. 完成年会抽奖一等奖逻辑:随机抽取数组中的一个姓名写入指定 span,并把该姓名从数组中删除,保证后续不会重复中奖。
  5. 找错并修正:const lis = document.querySelectorAll('li'); lis.push(document.querySelector('li')); 以及 box.innerHTML = '<strong>测试</strong>'; 中可能存在的书写与使用问题。

原视频对照 ​

#视频时长
1其他方式获取DOM元素04:26
2操作元素内容innerText和innerHTML13:21
3重构学成在线案例07:05
4年会抽奖案例-一等奖做法11:52
5年会抽奖案例-二三等奖做法04:42

小结 ​

  1. 操作元素前先获取到正确的 DOM 对象,选择器字符串写错时优先检查返回值是否为 null。
  2. 单个元素与元素集合要分清:querySelector 取第一个,querySelectorAll 始终得到伪数组。
  3. 只改文字优先考虑 innerText;需要让标签生效时使用 innerHTML。
  4. 批量渲染列表时,先拼字符串,再获取目标容器,用 innerHTML 一次性写入更简洁。
  5. 抽奖类需求的关键是“随机索引、写入对应盒子、按同一索引删除”,三步要保持一致。
  6. 复制代码后必须逐项核对变量名、选择器和索引,复制不改是高频错误来源。
  7. 变量声明按是否重新赋值选择 const 或 let,循环拼接和随机数复用时尤其要注意。
  8. 调试时多用 console.log 看值、console.dir 看对象结构,先验证再继续写。