1. JavaScript 基础

  2. Document

  3. 运算符

  4. 深入数据和类型

  5. 函数进阶

  6. 原型、继承

  7. 浏览器存储

  8. Web API

  9. 事件

  10. 错误处理

  11. 异步编程

  12. 网络请求

  13. 模块

  14. 练习

  15. 实例

  16. 工具与规范

  17. 软件架构模式

  18. 设计模式

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>

$$