DOM 操作

DOM 的全称是 Document Object Model(文档对象模型),相当于把页面的内容映射成了一个对象,通过 DOM 我们可以获取并修改页面上的任何内容。

本章节的内容就是对如何获取与修改页面元素有一个初步的了解。

获取元素

一般我们获取元素有两个步骤:

  1. HTML 中设置元素的 id 属性。
  2. JavaScript 中通过 document.getElementById() 方法获取元素。

点击实例中的 执行代码 并查看控制台中的输出,在实例中的 HTML 定义了一个 p 元素并设置 id 为 title,还定义了一个按钮绑定了点击事件,当点击时通过 document.getElementById() 获取元素并打印到控制台中。

<iframe height="300" style="width: 100%" scrolling="no" title="演示" src="https://codepen.io/3yya/embed/XWVPzLB?default-tab=js%2Cresult&editable=true&theme-id=light" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"></iframe>

通过 document.getElementById() 方法获取到的是一个元素对象,便可以通过操作此元素对象获取或修改元素内容。

获取或修改元素内 HTML

innerHTML 属性可以通过 HTML 代码的形式获取或修改元素内 HTML 内容。

实例中定义了绑定了两个按钮事件,一个在点击时获取元素的内容并通过 alert 输出,另一个则修改元素内的 HTML 代码为一个 img 元素。

<iframe height="300" style="width: 100%" scrolling="no" title="演示" src="https://codepen.io/3yya/embed/abEaEKo?default-tab=js%2Cresult&editable=true&theme-id=light" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"></iframe>

获取或修改元素 HTML

outerHTMLinnerHTML 的区别在于, outerHTML 属性获取或修改的元素 HTML 内容包括了自身。因此当修改 outerHTML 的内容时就相当于替换了一个新的元素。

实例中修改元素的 outerHTML 时将自身也替换掉了。

<iframe height="300" style="width: 100%" scrolling="no" title="演示" src="https://codepen.io/3yya/embed/qBpMpLO?default-tab=js%2Cresult&editable=true&theme-id=light" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"></iframe>

获取或修改元素内文本

textContent 属性获取的是元素内所有的纯文本内容,不包括标签或属性。当设置 textContent 属性时,设置的值也会被当作纯文本内容。

实例中获取 textContent 时仅输出了纯文本,并且修改的 img HTML 代码并没有生效,原因是这些代码被当成了纯文本对待。

<iframe height="300" style="width: 100%" scrolling="no" title="演示" src="https://codepen.io/3yya/embed/oNpPpRz?default-tab=js%2Cresult&editable=true&theme-id=light" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"></iframe>

获取或修改 input 的值

对于 input 元素,可以通过 value 属性获取或修改其值。

实例中定义了一个输入框,并绑定了三个按钮事件:

  • 获取用户名:输出元素的 value 值
  • 默认用户名:将元素的 value 值设置为 三眼鸭
  • 清空用户名:将元素的 value 值设置为空字符串

<iframe height="300" style="width: 100%" scrolling="no" title="演示" src="https://codepen.io/3yya/embed/OJzoQNE?default-tab=js%2Cresult&editable=true&theme-id=light" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"></iframe>

计算器实例

以下代码演示了一个加法计算器的实例。

<iframe height="300" style="width: 100%" scrolling="no" title="演示" src="https://codepen.io/3yya/embed/xxpaWza?default-tab=js%2Cresult&editable=true&theme-id=light" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"></iframe>


练习

  1. 使用 JavaScript 代码在页面上显示以下题库内容。
// 题库
let items = [
    {
        question: "中国的首都是?",
        answers: ["上海", "广州", "北京"],
        rightIdx: 2,
    },
    {
        question: "美国的首都是?",
        answers: ["纽约", "华盛顿", "伦敦"],
        rightIdx: 1,
    },
    {
        question: "新加坡的首都是?",
        answers: ["后港", "新加坡市", "裕华"],
        rightIdx: 1,
    },
    {
        question: "越南的首都是?",
        answers: ["河内", "胡志明市", "海防"],
        rightIdx: 0,
    },
    {
        question: "印度的首都是?",
        answers: ["新德里", "巴黎", "孟买"],
        rightIdx: 0,
    },
]

$$demo <div id="container" class="container"> <!-- <h3>中国的首都是?</h3> <div class="options"> <div>上海</div> <div>广州</div> <div>北京</div> </div> --> </div> <style> .container { display: flex; flex-direction: column;

    align-items: center;
}

.options {
    display: flex;

    gap: 20px;
}

</style> <script> // 题库 let items = [ { question: "中国的首都是?", answers: ["上海", "广州", "北京"], rightIdx: 2, }, { question: "美国的首都是?", answers: ["纽约", "华盛顿", "伦敦"], rightIdx: 1, }, { question: "新加坡的首都是?", answers: ["后港", "新加坡市", "裕华"], rightIdx: 1, }, { question: "越南的首都是?", answers: ["河内", "胡志明市", "海防"], rightIdx: 0, }, { question: "印度的首都是?", answers: ["新德里", "巴黎", "孟买"], rightIdx: 0, }, ]

// container 元素
let container = document.getElementById("container")

for (let item of items) {
    container.innerHTML += `
    <h3>${item.question}</h3>
    <div class="options">
        <div>${item.answers[0]}</div>
        <div>${item.answers[1]}</div>
        <div>${item.answers[2]}</div>
    </div>
    `
}

</script> $$

$$answer

<div id="container" class="container">
    <!-- <h3>中国的首都是?</h3>
    <div class="options">
        <div>上海</div>
        <div>广州</div>
        <div>北京</div>
    </div> -->
</div>
<style>
    .container {
        display: flex;
        flex-direction: column;

        align-items: center;
    }

    .options {
        display: flex;

        gap: 20px;
    }
</style>
<script>
    // 题库
    let items = [
        {
            question: "中国的首都是?",
            answers: ["上海", "广州", "北京"],
            rightIdx: 2,
        },
        {
            question: "美国的首都是?",
            answers: ["纽约", "华盛顿", "伦敦"],
            rightIdx: 1,
        },
        {
            question: "新加坡的首都是?",
            answers: ["后港", "新加坡市", "裕华"],
            rightIdx: 1,
        },
        {
            question: "越南的首都是?",
            answers: ["河内", "胡志明市", "海防"],
            rightIdx: 0,
        },
        {
            question: "印度的首都是?",
            answers: ["新德里", "巴黎", "孟买"],
            rightIdx: 0,
        },
    ]

    // container 元素
    let container = document.getElementById("container")

    for (let item of items) {
        container.innerHTML += `
        <h3>${item.question}</h3>
        <div class="options">
            <div>${item.answers[0]}</div>
            <div>${item.answers[1]}</div>
            <div>${item.answers[2]}</div>
        </div>
        `
    }
</script>

$$ 2. 补充实例,使得正确答案能够高亮。 $$demo <div id="container" class="container"> <!-- <h3>中国的首都是?</h3> <div class="options"> <div>上海</div> <div>广州</div> <div>北京</div> </div> --> </div> <style> .container { display: flex; flex-direction: column;

    align-items: center;
}

.options {
    display: flex;

    gap: 20px;
}

.right {
    font-weight: bold;
    color: #67c23a;
}

</style> <script> // 题库 let items = [ { question: "中国的首都是?", answers: ["上海", "广州", "北京"], rightIdx: 2, }, { question: "美国的首都是?", answers: ["纽约", "华盛顿", "伦敦"], rightIdx: 1, }, { question: "新加坡的首都是?", answers: ["后港", "新加坡市", "裕华"], rightIdx: 1, }, { question: "越南的首都是?", answers: ["河内", "胡志明市", "海防"], rightIdx: 0, }, { question: "印度的首都是?", answers: ["新德里", "巴黎", "孟买"], rightIdx: 0, }, ]

// container 元素
let container = document.getElementById("container")

let htmlStr = ""
for (let item of items) {
    htmlStr += `<h3>${item.question}</h3>`
    htmlStr += '<div class="options">'
    for (let idx in item.answers) {
        if (idx == item.rightIdx) {
            // 如果是正确答案
            htmlStr += `<div class="right">${item.answers[idx]}</div>`
        } else {
            htmlStr += `<div>${item.answers[idx]}</div>`
        }
    }
    htmlStr += "</div>"
}

container.innerHTML = htmlStr

</script> $$

$$answer

<div id="container" class="container">
    <!-- <h3>中国的首都是?</h3>
    <div class="options">
        <div>上海</div>
        <div>广州</div>
        <div>北京</div>
    </div> -->
</div>
<style>
    .container {
        display: flex;
        flex-direction: column;

        align-items: center;
    }

    .options {
        display: flex;

        gap: 20px;
    }

    .right {
        font-weight: bold;
        color: #67c23a;
    }
</style>
<script>
    // 题库
    let items = [
        {
            question: "中国的首都是?",
            answers: ["上海", "广州", "北京"],
            rightIdx: 2,
        },
        {
            question: "美国的首都是?",
            answers: ["纽约", "华盛顿", "伦敦"],
            rightIdx: 1,
        },
        {
            question: "新加坡的首都是?",
            answers: ["后港", "新加坡市", "裕华"],
            rightIdx: 1,
        },
        {
            question: "越南的首都是?",
            answers: ["河内", "胡志明市", "海防"],
            rightIdx: 0,
        },
        {
            question: "印度的首都是?",
            answers: ["新德里", "巴黎", "孟买"],
            rightIdx: 0,
        },
    ]

    // container 元素
    let container = document.getElementById("container")

    let htmlStr = ""
    for (let item of items) {
        htmlStr += `<h3>${item.question}</h3>`
        htmlStr += '<div class="options">'
        for (let idx in item.answers) {
            if (idx == item.rightIdx) {
                // 如果是正确答案
                htmlStr += `<div class="right">${item.answers[idx]}</div>`
            } else {
                htmlStr += `<div>${item.answers[idx]}</div>`
            }
        }
        htmlStr += "</div>"
    }

    container.innerHTML = htmlStr
</script>

$$

  1. 实现以下简易计算器实例效果。

$$demo <body> <input id="num1" type="number" placeholder="第一个数" /> <select id="operator"> <option value="plus">+</option> <option value="sub">-</option> <option value="mult">*</option> <option value="divide">/</option> </select> <input id="num2" type="number" placeholder="第二个数" /> <button onclick="calc()">=</button> <span id="result"></span> </body>

<script> function calc() { let num1 = Number(document.getElementById("num1").value) let num2 = Number(document.getElementById("num2").value)

    let op = document.getElementById("operator").value
    let result

    switch (op) {
        case "plus":
            result = num1 + num2
            break
        case "sub":
            result = num1 - num2
            break
        case "mult":
            result = num1 * num2
            break
        case "divide":
            result = num1 / num2
            break
    }
    document.getElementById("result").textContent = result
}

</script> $$

$$answer

<input id="num1" type="number" placeholder="第一个数" />
<select id="operator">
    <option value="plus">+</option>
    <option value="sub">-</option>
    <option value="mult">*</option>
    <option value="divide">/</option>
</select>
<input id="num2" type="number" placeholder="第二个数" />
<button onclick="calc()">=</button>
<span id="result"></span>

<script>
    function calc() {
        let num1 = Number(document.getElementById("num1").value)
        let num2 = Number(document.getElementById("num2").value)

        let op = document.getElementById("operator").value
        let result

        switch (op) {
            case "plus":
                result = num1 + num2
                break
            case "sub":
                result = num1 - num2
                break
            case "mult":
                result = num1 * num2
                break
            case "divide":
                result = num1 / num2
                break
        }
        document.getElementById("result").textContent = result
    }
</script>

$$

  1. 完成以下点击放大元素的效果。 $$demo <div id="box" class="box" onclick="enlarge()">点我变大</div> <style> .box { cursor: pointer;

     display: flex;
    
     align-items: center;
     justify-content: center;
    
     background-color: teal;
    
     color: white;
     font-weight: bold;
    
     width: 5em;
     height: 5em;
    
     border: 1em solid pink;
    
     /* 禁止文本被选中 */
     user-select: none;
    

    } </style> <script> // 以字体大小为基准 // 默认字体大小 1em // 每次点击放大到 1.2 倍 let factor = 1

    function refresh() { let box = document.getElementById("box") box.outerHTML = <div id="box" class="box" onclick="enlarge()" style="font-size: ${factor}em" > 点我变大 </div> }

    function enlarge() { // 放大到 1.2 倍 factor *= 1.2

     // 更新 box
     refresh()
    

    } </script> $$

$$answer

<div id="box" class="box" onclick="enlarge()">点我变大</div>
<style>
    .box {
        cursor: pointer;

        display: flex;

        align-items: center;
        justify-content: center;

        background-color: teal;

        color: white;
        font-weight: bold;

        width: 5em;
        height: 5em;

        border: 1em solid pink;

        /* 禁止文本被选中 */
        user-select: none;
    }
</style>
<script>
    // 以字体大小为基准
    // 默认字体大小 1em
    // 每次点击放大到 1.2 倍
    let factor = 1

    function refresh() {
        let box = document.getElementById("box")
        box.outerHTML = `
        <div
            id="box"
            class="box"
            onclick="enlarge()"
            style="font-size: ${factor}em"
        >
            点我变大
        </div>
        `
    }

    function enlarge() {
        // 放大到 1.2 倍
        factor *= 1.2

        // 更新 box
        refresh()
    }
</script>

$$

  1. 使用题库完成以下答题实例。
// 题库
let items = [
    {
        question: "中国的首都是?",
        answers: ["上海", "广州", "北京"],
        rightIdx: 2,
    },
    {
        question: "美国的首都是?",
        answers: ["纽约", "华盛顿", "伦敦"],
        rightIdx: 1,
    },
    {
        question: "新加坡的首都是?",
        answers: ["后港", "新加坡市", "裕华"],
        rightIdx: 1,
    },
    {
        question: "越南的首都是?",
        answers: ["河内", "胡志明市", "海防"],
        rightIdx: 0,
    },
    {
        question: "印度的首都是?",
        answers: ["新德里", "巴黎", "孟买"],
        rightIdx: 0,
    },
]

$$demo <div id="container" class="container"> <!-- <h2>中国的首都是?</h2> <div class="options"> <div class="btn" onclick="choose()">上海</div> <div class="btn" onclick="choose()">广州</div> <div class="btn" onclick="choose()">北京</div> </div> --> <!-- <h2 class="right">答对了</h2> <div class="btn" onclick="next()">下一题</div> --> <!-- <h2 class="wrong">答错了,正确答案是北京</h2> <div class="btn" onclick="next()">下一题</div> --> <!-- <h2>答题结果</h2> <p>总共 5 题,答对了 2 题。</p> <div class="btn" onclick="restart()">重新答题</div> --> </div> <style> /* 全用 flex / .container { display: flex; / 垂直 / flex-direction: column; / 交叉轴居中 */ align-items: center; }

.options {
    display: flex;
    /* 子项间的间距 */
    gap: 20px;
}

.btn {
    padding: 5px 20px;
    border-radius: 8px;

    font-weight: bold;

    /* 小手光标样式 */
    cursor: pointer;
}

.btn:hover {
    background-color: #eee;
}

.right {
    color: #6ac244;
}

.wrong {
    color: #f36a6e;
}

</style> <script> // 题库 let items = [ { question: "中国的首都是?", answers: ["上海", "广州", "北京"], rightIdx: 2, }, { question: "美国的首都是?", answers: ["纽约", "华盛顿", "伦敦"], rightIdx: 1, }, { question: "新加坡的首都是?", answers: ["后港", "新加坡市", "裕华"], rightIdx: 1, }, { question: "越南的首都是?", answers: ["河内", "胡志明市", "海防"], rightIdx: 0, }, { question: "印度的首都是?", answers: ["新德里", "巴黎", "孟买"], rightIdx: 0, }, ]

// 开发步骤:
// 1. 拆解项目、功能。
// 2. 把 HTML、CSS 部分先写出来。
// 3. 逐步实现 JS 功能。

// 功能:
// 1. 显示题目答案。
// 2. 有选中题目的交互、功能。
// 3. 显示对错的结果。
// 4. 循环以上功能。
// 5. 最后一题答完后,显示总的结果与重新答题。

let container = document.getElementById("container")
console.log(container)

// 当前的题目下标
let currentIdx = 0

// 答对的题数
let rightCount = 0

// 更新题目
refresh()

function next() {
    // 下一步

    if (currentIdx === items.length - 1) {
        console.log("最后一题")
        // 最后一题
        container.innerHTML = `
        <h2>答题结果</h2>
        <p>总共 ${items.length} 题,答对了 ${rightCount} 题。</p>
        <div class="btn" onclick="restart()">重新答题</div>
        `
    } else {
        console.log("不是最后一题")
        // 切换下一题
        currentIdx++
        // 更新题目
        refresh()
    }
}

function refresh() {
    // 更新题目的内容
    container.innerHTML = `
    <h2>${items[currentIdx].question}</h2>
    <div class="options">
        <div class="btn" onclick="choose(0)">${items[currentIdx].answers[0]}</div>
        <div class="btn" onclick="choose(1)">${items[currentIdx].answers[1]}</div>
        <div class="btn" onclick="choose(2)">${items[currentIdx].answers[2]}</div>
    </div>
    `
}

function choose(chooseIdx) {
    // 选中答案
    console.log(`选中答案:${chooseIdx}`)

    // 与正确答案进行比较
    if (chooseIdx === items[currentIdx].rightIdx) {
        // 答对计数
        rightCount++

        container.innerHTML = `
        <h2 class="right">答对了</h2>
        <div class="btn" onclick="next()">${
            currentIdx === items.length - 1 ? "完成" : "下一题"
        }</div>
        `
    } else {
        // 正确答案的下标:items[currentIdx].rightIdx
        // 通过下标去拿正确答案
        let answer = items[currentIdx].answers[items[currentIdx].rightIdx]

        container.innerHTML = `
        <h2 class="wrong">答错了,正确答案是${answer}</h2>
        <div class="btn" onclick="next()">${
            currentIdx === items.length - 1 ? "完成" : "下一题"
        }</div>
        `
    }
}

function restart() {
    // 重新答题

    // 重置数据
    // 将当前的题目下标重置为 0
    currentIdx = 0
    // 当前答对的题数重置为 0
    rightCount = 0

    // 更新题目
    refresh()
}

</script>

$$

$$answer

<div id="container" class="container">
    <!-- <h2>中国的首都是?</h2>
    <div class="options">
        <div class="btn" onclick="choose()">上海</div>
        <div class="btn" onclick="choose()">广州</div>
        <div class="btn" onclick="choose()">北京</div>
    </div> -->
    <!-- <h2 class="right">答对了</h2>
    <div class="btn" onclick="next()">下一题</div> -->
    <!-- <h2 class="wrong">答错了,正确答案是北京</h2>
    <div class="btn" onclick="next()">下一题</div> -->
    <!-- <h2>答题结果</h2>
    <p>总共 5 题,答对了 2 题。</p>
    <div class="btn" onclick="restart()">重新答题</div> -->
</div>
<style>
    /* 全用 flex */
    .container {
        display: flex;
        /* 垂直 */
        flex-direction: column;
        /* 交叉轴居中 */
        align-items: center;
    }

    .options {
        display: flex;
        /* 子项间的间距 */
        gap: 20px;
    }

    .btn {
        padding: 5px 20px;
        border-radius: 8px;

        font-weight: bold;

        /* 小手光标样式 */
        cursor: pointer;
    }

    .btn:hover {
        background-color: #eee;
    }

    .right {
        color: #6ac244;
    }

    .wrong {
        color: #f36a6e;
    }
</style>
<script>
    // 题库
    let items = [
        {
            question: "中国的首都是?",
            answers: ["上海", "广州", "北京"],
            rightIdx: 2,
        },
        {
            question: "美国的首都是?",
            answers: ["纽约", "华盛顿", "伦敦"],
            rightIdx: 1,
        },
        {
            question: "新加坡的首都是?",
            answers: ["后港", "新加坡市", "裕华"],
            rightIdx: 1,
        },
        {
            question: "越南的首都是?",
            answers: ["河内", "胡志明市", "海防"],
            rightIdx: 0,
        },
        {
            question: "印度的首都是?",
            answers: ["新德里", "巴黎", "孟买"],
            rightIdx: 0,
        },
    ]

    // 开发步骤:
    // 1. 拆解项目、功能。
    // 2. 把 HTML、CSS 部分先写出来。
    // 3. 逐步实现 JS 功能。

    // 功能:
    // 1. 显示题目答案。
    // 2. 有选中题目的交互、功能。
    // 3. 显示对错的结果。
    // 4. 循环以上功能。
    // 5. 最后一题答完后,显示总的结果与重新答题。

    let container = document.getElementById("container")
    console.log(container)

    // 当前的题目下标
    let currentIdx = 0

    // 答对的题数
    let rightCount = 0

    // 更新题目
    refresh()

    function next() {
        // 下一步

        if (currentIdx === items.length - 1) {
            console.log("最后一题")
            // 最后一题
            container.innerHTML = `
            <h2>答题结果</h2>
            <p>总共 ${items.length} 题,答对了 ${rightCount} 题。</p>
            <div class="btn" onclick="restart()">重新答题</div>
            `
        } else {
            console.log("不是最后一题")
            // 切换下一题
            currentIdx++
            // 更新题目
            refresh()
        }
    }

    function refresh() {
        // 更新题目的内容
        container.innerHTML = `
        <h2>${items[currentIdx].question}</h2>
        <div class="options">
            <div class="btn" onclick="choose(0)">${items[currentIdx].answers[0]}</div>
            <div class="btn" onclick="choose(1)">${items[currentIdx].answers[1]}</div>
            <div class="btn" onclick="choose(2)">${items[currentIdx].answers[2]}</div>
        </div>
        `
    }

    function choose(chooseIdx) {
        // 选中答案
        console.log(`选中答案:${chooseIdx}`)

        // 与正确答案进行比较
        if (chooseIdx === items[currentIdx].rightIdx) {
            // 答对计数
            rightCount++

            container.innerHTML = `
            <h2 class="right">答对了</h2>
            <div class="btn" onclick="next()">${
                currentIdx === items.length - 1 ? "完成" : "下一题"
            }</div>
            `
        } else {
            // 正确答案的下标:items[currentIdx].rightIdx
            // 通过下标去拿正确答案
            let answer = items[currentIdx].answers[items[currentIdx].rightIdx]

            container.innerHTML = `
            <h2 class="wrong">答错了,正确答案是${answer}</h2>
            <div class="btn" onclick="next()">${
                currentIdx === items.length - 1 ? "完成" : "下一题"
            }</div>
            `
        }
    }

    function restart() {
        // 重新答题

        // 重置数据
        // 将当前的题目下标重置为 0
        currentIdx = 0
        // 当前答对的题数重置为 0
        rightCount = 0

        // 更新题目
        refresh()
    }
</script>

$$