欢迎光临
我们一直在努力

DOM实战入门教程

DOM 实战入门教程

作者:wcy 适用人群:已经学完基础 HTML、CSS 和 JavaScript,想把页面交互做出来的初学者


目录

  • DOM 是什么
  • DOM 树结构
  • 获取元素
  • 修改内容与属性
  • 修改样式
  • 创建与删除元素
  • 事件详解
  • 表单实战
  • 定时器实战
  • 实战一:选项卡切换
  • 实战二:发消息系统
  • 实战三:待办清单(升级版)
  • 小结与练习

  • 1. DOM 是什么

    原计划学完 JavaScript 语法后,我们发现它只能"自己算",还碰不到页面。而要让网页真正动起来——点击按钮换文字、输入内容动态展示、删除列表项——JS 必须能操作页面上的元素。

    这就是 DOM(Document Object Model,文档对象模型) 的用武之地。

    用大白话解释:

    浏览器把 HTML 网页"翻译"成一棵元素树,树上的每一个节点(body、p、div、文字……)都是 JS 可以访问和操作的对象。通过 JS 修改这些对象,页面就跟着变了。

    简单说,DOM 就是 JS 与 HTML 之间的"桥"。

    本教程不做概念堆砌,直接通过一个个能运行的实战例子,带你掌握 DOM 的核心操作。每一节都附完整可运行代码,建议边看边在浏览器里敲一遍。

    提示:按 F12 打开开发者工具,在 Console(控制台)里可以直接输入下面例子中的 JS 代码实时测试。


    2. DOM 树结构

    先看一个最简单的页面:

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <title></title>
    </head>
    <body>
    <h1>标题</h1>
    <p>一段文字</p>
    </body>
    </html>

    它在浏览器内部会变成这样一棵"树"(父节点在上,子节点在下):

    document(文档)
    └── html
    ├── head
    │ ├── meta
    │ └── title
    └── body
    ├── h1
    └── p

    关键概念:

    概念含义例子
    文档节点 整棵树的根,就是 document 整个页面
    元素节点 各种标签 html、body、h1、p
    文本节点 标签里的文字 “标题”、“一段文字”
    父节点 直接包含某个节点的节点 body 是 h1 的父节点
    子节点 被某个节点直接包含的节点 h1、p 是 body 的子节点
    兄弟节点 同一个父节点下的其他节点 h1 和 p 互为兄弟

    记不住的词没关系,实战里用得最多的是:拿到某个元素 → 改它/加它/删它。我们接下来要学的操作,本质都是在树上"找节点、动节点"。


    3. 获取元素

    要操作一个元素,第一步是"拿到它"。下面是常用的获取方法。

    3.1 用 id 获取(最常用)

    <p id="msg">原来的文字</p>

    <script>
    let msg = document.getElementById("msg");
    console.log(msg); // 打印出这个元素
    </script>

    getElementById:按照 id 精确找到一个元素,找不到返回 null。

    3.2 用 CSS 选择器获取(最灵活)

    querySelector 支持写任何 CSS 选择器,推荐优先使用:

    <p class="box">第一个</p>
    <p class="box">第二个</p>

    <script>
    // 取第一个匹配的元素
    let first = document.querySelector(".box");

    // 取所有匹配的元素,返回一个"类数组"
    let all = document.querySelectorAll(".box");
    console.log(all.length); // 2
    </script>

    常见写法:

    document.querySelector("#msg"); // 按 id
    document.querySelector(".box"); // 按 class
    document.querySelector("p"); // 按标签
    document.querySelector("div.box"); // div 且 class 为 box
    document.querySelector("ul li:first-child"); // 复杂选择器也支持

    注意:querySelector 永远只返回第一个匹配的;要操作全部元素,用 querySelectorAll 后再遍历。

    3.3 遍历元素集合

    <ul id="list">
    <li>苹果</li>
    <li>香蕉</li>
    <li>橙子</li>
    </ul>

    <script>
    let items = document.querySelectorAll("#list li");
    items.forEach(function (li, index) {
    console.log(index, li.textContent);
    });
    </script>

    3.4 父子兄弟关系

    <div id="parent">
    <p id="child">文本</p>
    </div>

    <script>
    let child = document.getElementById("child");

    child.parentNode; // 父节点 → div
    child.nextElementSibling; // 下一个兄弟元素
    child.previousElementSibling;// 上一个兄弟元素
    </script>


    4. 修改内容与属性

    拿到元素后,第一件事通常是改它显示的内容。

    4.1 textContent:改纯文本

    <p id="msg">旧内容</p>
    <button id="btn">点我换文字</button>

    <script>
    document.getElementById("btn").addEventListener("click", function () {
    document.getElementById("msg").textContent = "新内容!";
    });
    </script>

    textContent 只能放纯文本,就算给它 HTML 字符串,也会原样显示成文字(安全)。

    4.2 innerHTML:改 HTML 内容

    innerHTML 会把字符串当作 HTML 来解析:

    <div id="box"></div>

    <script>
    let box = document.getElementById("box");
    box.innerHTML = "<b>加粗文字</b><span style='color:red'>和红色文字</span>";
    </script>

    安全提醒:如果内容来自用户输入(比如表单),不要用 innerHTML,它可能被塞入恶意脚本。用户输入一律用 textContent。

    4.3 修改属性

    <a id="link" href="#">我的博客</a>
    <img id="pic" src="a.jpg" alt="图片">

    <script>
    let link = document.getElementById("link");
    link.href = "https://blog.csdn.net/wangchunyu114";
    link.textContent = "去博客逛逛";

    let pic = document.getElementById("pic");
    pic.src = "b.jpg"; // 直接改属性
    // 通用写法:
    pic.setAttribute("alt", "新的描述");
    pic.getAttribute("alt"); // 读取属性值
    pic.hasAttribute("src"); // 判断是否有该属性 → true
    pic.removeAttribute("src"); // 删除属性
    </script>

    4.4 实战:点击切换标题文字

    <h1 id="title">今天吃什么?</h1>
    <button id="switchBtn">换一个</button>

    <script>
    const foods = ["火锅", "烧烤", "炒饭", "面条", "汉堡"];
    let index = 0;

    document.getElementById("switchBtn").addEventListener("click", function () {
    index = (index + 1) % foods.length; // 循环取下一个
    document.getElementById("title").textContent = foods[index];
    });
    </script>

    保存成 switch.html 打开,每点一次按钮标题就换一个菜名。


    5. 修改样式

    5.1 用 style 改行内样式

    <p id="text">这段文字会很调皮</p>
    <button id="styleBtn">变红加粗</button>

    <script>
    document.getElementById("styleBtn").addEventListener("click", function () {
    let t = document.getElementById("text");
    t.style.color = "red";
    t.style.fontSize = "24px";
    t.style.fontWeight = "bold";
    });
    </script>

    注意:CSS 属性名里的短横线,在 JS 里要换成驼峰写法:

    CSS 写法JS 写法
    font-size fontSize
    background-color backgroundColor
    margin-top marginTop

    5.2 用 classList 切换样式类(更推荐)

    与其在 JS 里写一堆样式,不如定义好 CSS 类,再用 JS 切换类名。这样做"样式归 CSS、逻辑归 JS",更干净:

    .dark {
    background-color: #222222;
    color: white;
    }

    <p id="text">在明暗之间切换</p>
    <button id="toggleBtn">切换主题</button>

    <script>
    let t = document.getElementById("text");

    document.getElementById("toggleBtn").addEventListener("click", function () {
    t.classList.toggle("dark"); // 有则去掉,无则加上
    });
    </script>

    classList 的四个常用方法:

    方法作用
    add("类名") 添加类
    remove("类名") 删除类
    toggle("类名") 有则删、无则加(最常用)
    contains("类名") 判断有没有,返回布尔值

    5.3 实战:暗黑模式开关

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <title>暗黑模式</title>
    <style>
    body {
    transition: background-color 0.3s, color 0.3s;
    }
    body.dark {
    background-color: #1e1e1e;
    color: #eeeeee;
    }
    </style>
    </head>
    <body>
    <button id="themeBtn">切换到暗黑模式</button>

    <script>
    let btn = document.getElementById("themeBtn");
    btn.addEventListener("click", function () {
    document.body.classList.toggle("dark");
    btn.textContent = document.body.classList.contains("dark")
    ? "切换到亮色模式"
    : "切换到暗黑模式";
    });
    </script>
    </body>
    </html>

    5.4 用 style.cssText 一次写多个样式

    let box = document.getElementById("box");
    box.style.cssText = "width: 100px; height: 100px; background: red;";

    适合一次性重置大量样式,日常用得不多,了解即可。


    6. 创建与删除元素

    动态增删元素,是做"列表、消息流、购物车"等功能的基础。

    6.1 创建并插入元素

    <ul id="list">
    <li>已有一项</li>
    </ul>
    <button id="addBtn">添加一项</button>

    <script>
    document.getElementById("addBtn").addEventListener("click", function () {
    let li = document.createElement("li"); // 1. 创建
    li.textContent = "新项目"; // 2. 设置内容
    document.getElementById("list").appendChild(li); // 3. 插到末尾
    });
    </script>

    标准三步:创建 → 设置内容 → 插入页面。

    6.2 插入位置:appendChild / insertBefore

    let parent = document.getElementById("list");

    parent.appendChild(li); // 放到最后
    parent.insertBefore(li, parent.firstChild); // 放到最前面

    6.3 删除元素

    li.remove(); // 直接删除自己(现代写法,推荐)

    parent.removeChild(li); // 通过父节点删除(旧写法)

    6.4 清空内容

    document.getElementById("list").innerHTML = ""; // 清空所有子元素

    6.5 实战:动态添加和删除

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <title>动态列表</title>
    </head>
    <body>
    <input type="text" id="itemInput" placeholder="输入内容">
    <button id="addBtn">添加</button>
    <ul id="list"></ul>

    <script>
    let list = document.getElementById("list");
    let input = document.getElementById("itemInput");

    document.getElementById("addBtn").addEventListener("click", addItem);

    input.addEventListener("keydown", function (e) {
    if (e.key === "Enter") { // 按回车也添加
    addItem();
    }
    });

    function addItem() {
    let text = input.value.trim();
    if (!text) return; // 空内容不添加

    let li = document.createElement("li");
    li.textContent = text;

    let del = document.createElement("button");
    del.textContent = "删除";
    del.addEventListener("click", function () {
    li.remove(); // 点击删除自己
    });

    li.appendChild(del);
    list.appendChild(li);
    input.value = "";
    }
    </script>
    </body>
    </html>


    7. 事件详解

    前面几节的例子都在用 addEventListener,这一节把事件讲透。

    7.1 常用事件一览

    事件触发时机
    click 鼠标单击
    dblclick 鼠标双击
    mouseover / mouseout 鼠标移入 / 移出
    mouseenter / mouseleave 鼠标进入 / 离开(不冒泡)
    keydown / keyup 键盘按下 / 松开
    input 输入框内容变化(即时触发)
    change 输入框/下拉框失去焦点且值变了
    submit 表单提交
    focus / blur 获得焦点 / 失去焦点

    7.2 event 事件对象

    事件发生时,浏览器会传入一个事件对象 e,里面装着这次事件的细节:

    <input type="text" id="myInput" placeholder="打字试试">

    <script>
    let input = document.getElementById("myInput");

    input.addEventListener("keydown", function (e) {
    console.log("按下的键:", e.key); // 比如 "a"、"Enter"
    console.log("是否按下 Ctrl:", e.ctrlKey);
    });

    input.addEventListener("input", function (e) {
    console.log("当前输入值:", e.target.value); // 实时内容
    });
    </script>

    常用属性:

    属性含义
    e.target 实际触发事件的元素
    e.key 按下的按键(键盘事件)
    e.ctrlKey / e.shiftKey / e.altKey 是否同时按了修饰键
    e.clientX / e.clientY 鼠标距浏览器窗口的坐标

    7.3 阻止默认行为 preventDefault

    有些标签自带行为:点击 <a> 会跳转、提交表单会刷新页面。不想让它发生时用 preventDefault():

    <a id="aLink" href="https://www.baidu.com">不许跳转</a>

    <script>
    document.getElementById("aLink").addEventListener("click", function (e) {
    e.preventDefault(); // 阻止跳转
    alert("链接被拦住了");
    });
    </script>

    7.4 事件传播与 stopPropagation

    一个元素嵌套另一个元素时,点击内层事件会冒泡到外层(从内向外依次触发)。想阻止向上传播,用 stopPropagation():

    <div id="outer" style="padding: 20px; background: lightblue;">
    外层 div
    <button id="inner">内层按钮</button>
    </div>

    <script>
    document.getElementById("outer").addEventListener("click", function () {
    console.log("外层被触发");
    });

    document.getElementById("inner").addEventListener("click", function (e) {
    e.stopPropagation(); // 阻止冒泡到外层
    console.log("内层被触发");
    });
    </script>

    注释掉 e.stopPropagation() 再点按钮,观察两层都会打印——这就是"冒泡"。

    7.5 事件委托(重要技巧)

    如果有很多个子元素都要绑定点击,一个个绑效率低、还容易漏掉动态新增的。事件委托的思路是:把监听绑在父元素上,用 e.target 判断点的是谁:

    <ul id="list">
    <li>项目一</li>
    <li>项目二</li>
    <li>项目三</li>
    </ul>

    <script>
    document.getElementById("list").addEventListener("click", function (e) {
    if (e.target.tagName === "LI") { // 判断点的是不是 li
    alert("你点了:" + e.target.textContent);
    }
    });
    </script>

    好处:无论多少个 li、哪怕之后动态新增 li,都不用重新绑定。

    7.6 实战:图片悬停放大

    <style>
    .gallery { display: flex; gap: 10px; }
    .gallery img {
    width: 100px;
    transition: transform 0.3s;
    cursor: pointer;
    }
    .gallery img.zoom { transform: scale(1.3); }
    </style>

    <div class="gallery">
    <img src="cat1.jpg" alt="猫1">
    <img src="cat2.jpg" alt="猫2">
    <img src="cat3.jpg" alt="猫3">
    </div>

    <script>
    let imgs = document.querySelectorAll(".gallery img");
    imgs.forEach(function (img) {
    img.addEventListener("mouseenter", function () {
    img.classList.add("zoom");
    });
    img.addEventListener("mouseleave", function () {
    img.classList.remove("zoom");
    });
    });
    </script>


    8. 表单实战

    表单是网页交互的核心场景。下面通过一个"注册表单校验"把表单相关的 DOM 操作串起来。

    8.1 读取表单输入

    let input = document.getElementById("username");
    input.value; // 读取输入值
    input.value = "预设值"; // 设置值

    8.2 实战:注册表单校验

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <title>表单校验</title>
    </head>
    <body>
    <form id="regForm">
    <p>
    用户名
    <input type="text" id="username" placeholder="至少 3 个字符">
    <span id="userTip" style="color: red;"></span>
    </p>
    <p>
    邮箱
    <input type="text" id="email" placeholder="xxx@example.com">
    <span id="emailTip" style="color: red;"></span>
    </p>
    <button type="submit">注册</button>
    </form>

    <script>
    let form = document.getElementById("regForm");

    // 阻止表单默认的页面刷新提交行为,改用我们自己校验
    form.addEventListener("submit", function (e) {
    e.preventDefault();

    let username = document.getElementById("username").value.trim();
    let email = document.getElementById("email").value.trim();
    let userTip = document.getElementById("userTip");
    let emailTip = document.getElementById("emailTip");

    let ok = true;

    if (username.length < 3) {
    userTip.textContent = "用户名太短了";
    ok = false;
    } else {
    userTip.textContent = "";
    }

    let hasAt = email.includes("@") && email.split("@").length === 2;
    let emailValid = hasAt && email.split("@")[1].includes(".");
    if (!emailValid) {
    emailTip.textContent = "邮箱格式不对";
    ok = false;
    } else {
    emailTip.textContent = "";
    }

    if (ok) {
    alert("注册成功!用户名:" + username);
    }
    });
    </script>
    </body>
    </html>

    8.3 实战:两个下拉框联动

    省份选择城市:

    <select id="province">
    <option value="zj">浙江</option>
    <option value="gd">广东</option>
    <option value="sc">四川</option>
    </select>

    <select id="city">
    <option>请先选择省份</option>
    </select>

    <script>
    // 省份对应的城市
    let cityMap = {
    zj: ["杭州", "宁波", "温州"],
    gd: ["广州", "深圳", "珠海"],
    sc: ["成都", "绵阳", "乐山"]
    };

    let province = document.getElementById("province");
    let city = document.getElementById("city");

    province.addEventListener("change", function () {
    let cities = cityMap[province.value] || [];
    // 清空并重新填充城市下拉框
    city.innerHTML = "";
    cities.forEach(function (c) {
    let option = document.createElement("option");
    option.value = c;
    option.textContent = c;
    city.appendChild(option);
    });
    });
    </script>


    9. 定时器实战

    定时器配合 DOM,能做出倒计时、打字机、轮播、秒表等常见效果。

    9.1 setTimeout:延时执行一次

    setTimeout(function () {
    console.log("3 秒后执行一次");
    }, 3000);

    9.2 setInterval:每隔一段时间重复执行

    let timer = setInterval(function () {
    console.log("每 1 秒一次");
    }, 1000);

    // 停止:
    clearInterval(timer);

    9.3 实战:秒表

    <p id="watch">00:00</p>
    <button id="startBtn">开始</button>
    <button id="stopBtn">暂停</button>
    <button id="resetBtn">重置</button>

    <script>
    let seconds = 0;
    let timer = null;

    function render() {
    let min = Math.floor(seconds / 60);
    let sec = seconds % 60;
    let pad = function (n) { return n < 10 ? "0" + n : n; };
    document.getElementById("watch").textContent = pad(min) + ":" + pad(sec);
    }

    document.getElementById("startBtn").addEventListener("click", function () {
    if (timer) return; // 已在计时就不重复开
    timer = setInterval(function () {
    seconds++;
    render();
    }, 1000);
    });

    document.getElementById("stopBtn").addEventListener("click", function () {
    clearInterval(timer);
    timer = null;
    });

    document.getElementById("resetBtn").addEventListener("click", function () {
    clearInterval(timer);
    timer = null;
    seconds = 0;
    render();
    });
    </script>

    9.4 实战:自动轮播(定时器版)

    <style>
    #banner { width: 300px; height: 150px; text-align: center; font-size: 40px; line-height: 150px; color: white; }
    </style>

    <div id="banner"></div>

    <script>
    let colors = ["#e74c3c", "#3498db", "#2ecc71", "#f39c12"];
    let index = 0;
    let banner = document.getElementById("banner");

    banner.textContent = "第 1 张";
    banner.style.backgroundColor = colors[0];

    setInterval(function () {
    index = (index + 1) % colors.length;
    banner.textContent = "第 " + (index + 1) + " 张";
    banner.style.backgroundColor = colors[index];
    }, 1500);
    </script>


    10. 实战一:选项卡切换

    最经典的 DOM 实战:点击不同选项卡,显示对应内容。综合运用"遍历、classList 切换、循环绑定"。

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <title>选项卡</title>
    <style>
    .tabs { display: flex; gap: 0; border-bottom: 2px solid #eee; }
    .tab {
    padding: 10px 20px;
    cursor: pointer;
    border: 1px solid #eee;
    border-bottom: none;
    background: #fafafa;
    }
    .tab.active { background: #ffffff; color: #1a73e8; font-weight: bold; border-top: 2px solid #1a73e8; }
    .panel { display: none; padding: 20px; border: 1px solid #eee; border-top: none; }
    .panel.active { display: block; }
    </style>
    </head>
    <body>
    <div class="tabs">
    <div class="tab active" data-index="0">公告</div>
    <div class="tab" data-index="1">动态</div>
    <div class="tab" data-index="2">设置</div>
    </div>

    <div class="panel active">这里是公告内容。</div>
    <div class="panel">这里是动态内容。</div>
    <div class="panel">这里是设置内容。</div>

    <script>
    let tabs = document.querySelectorAll(".tab");
    let panels = document.querySelectorAll(".panel");

    tabs.forEach(function (tab) {
    tab.addEventListener("click", function () {
    // 1. 所有 tab 去掉 active
    tabs.forEach(function (t) { t.classList.remove("active"); });
    // 2. 当前 tab 加 active
    tab.classList.add("active");
    // 3. 所有 panel 隐藏
    panels.forEach(function (p) { p.classList.remove("active"); });
    // 4. 对应的 panel 显示
    panels[tab.dataset.index].classList.add("active");
    });
    });
    </script>
    </body>
    </html>

    技巧:data-index 是自定义属性,JS 里通过 元素.dataset.index 读取,用来记录"这是第几块面板"。


    11. 实战二:发消息系统

    综合运用"创建元素、插入列表、事件委托、清空输入"。

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <title>消息系统</title>
    <style>
    body { font-family: "微软雅黑"; }
    #chat {
    width: 360px;
    height: 260px;
    border: 1px solid #ddd;
    overflow-y: auto;
    padding: 10px;
    background: #f9f9f9;
    }
    .msg {
    padding: 6px 10px;
    margin: 6px 0;
    width: fit-content;
    max-width: 80%;
    border-radius: 8px;
    }
    .mine { background: #1a73e8; color: white; margin-left: auto; }
    .other { background: #eeeeee; }
    </style>
    </head>
    <body>
    <h2>客服对话</h2>
    <div id="chat"></div>
    <input type="text" id="input" placeholder="输入消息,回车发送">
    <button id="sendBtn">发送</button>

    <script>
    let chat = document.getElementById("chat");
    let input = document.getElementById("input");

    function addMsg(text, type) {
    let div = document.createElement("div");
    div.className = "msg " + type; // mine / other
    div.textContent = text;
    chat.appendChild(div);
    chat.scrollTop = chat.scrollHeight; // 自动滚到底部
    }

    function send() {
    let text = input.value.trim();
    if (!text) return;
    addMsg(text, "mine");
    input.value = "";

    // 模拟对方回复
    setTimeout(function () {
    addMsg("已收到:" + text, "other");
    }, 800);
    }

    document.getElementById("sendBtn").addEventListener("click", send);
    input.addEventListener("keydown", function (e) {
    if (e.key === "Enter") send();
    });
    </script>
    </body>
    </html>

    chat.scrollTop = chat.scrollHeight 是一个实用技巧:让可滚动的消息区自动滚到最新一条。


    12. 实战三:待办清单(升级版)

    最后一个综合实战,把之前的操作全面串起来,并加入 localStorage 持久化:刷新页面数据不丢。

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <title>待办清单</title>
    <style>
    body { font-family: "微软雅黑"; max-width: 420px; margin: 20px auto; }
    .done { text-decoration: line-through; color: #999; }
    li { user-select: none; }
    li span { cursor: pointer; }
    </style>
    </head>
    <body>
    <h1>待办清单</h1>
    <input type="text" id="taskInput" placeholder="输入新任务">
    <button id="addBtn">添加</button>
    <ul id="list"></ul>

    <script>
    let list = document.getElementById("list");
    let input = document.getElementById("taskInput");
    let addBtn = document.getElementById("addBtn");

    // 1. 数据模型:从 localStorage 读取(首次为空数组)
    let tasks = JSON.parse(localStorage.getItem("tasks") || "[]");

    // 2. 保存到本地
    function save() {
    localStorage.setItem("tasks", JSON.stringify(tasks));
    }

    // 3. 渲染整个列表
    function render() {
    list.innerHTML = "";
    tasks.forEach(function (task, index) {
    let li = document.createElement("li");
    li.textContent = task.text;

    if (task.done) li.classList.add("done");

    // 点击文字:切换完成状态
    li.addEventListener("click", function () {
    tasks[index].done = !tasks[index].done;
    save();
    render();
    });

    // 删除按钮
    let del = document.createElement("button");
    del.textContent = "删除";
    del.style.marginLeft = "10px";
    del.addEventListener("click", function (e) {
    e.stopPropagation(); // 别触发 li 的点击
    tasks.splice(index, 1);
    save();
    render();
    });

    li.appendChild(del);
    list.appendChild(li);
    });
    }

    // 4. 添加任务
    function add() {
    let text = input.value.trim();
    if (!text) return;
    tasks.push({ text: text, done: false });
    save();
    input.value = "";
    render();
    }

    addBtn.addEventListener("click", add);
    input.addEventListener("keydown", function (e) {
    if (e.key === "Enter") add();
    });

    // 5. 首次渲染
    render();
    </script>
    </body>
    </html>

    这个例子的核心架构(也是小型前端应用的标准套路):

    数据(tasks 数组) → 渲染(render 函数) → 事件(增/删/改触发 save + render)

    改数据 → 保存 → 重新渲染,页面永远和数据保持一致。


    13. 小结与练习

    本章总结

    分类核心技术
    获取元素 getElementById、querySelector、querySelectorAll
    修改内容 textContent(纯文本)、innerHTML(HTML,慎用)
    修改属性 href、src、setAttribute、getAttribute
    修改样式 style.xxx(驼峰)、classList.add/remove/toggle/contains
    增删元素 createElement、appendChild、insertBefore、remove
    事件 addEventListener、preventDefault、stopPropagation、事件委托
    表单 value、submit、change、input、下拉框联动
    定时器 setTimeout、setInterval、clearInterval
    持久化 localStorage + JSON.stringify/JSON.parse
    开发套路 数据模型 → 渲染函数 → 事件绑定(改数据 → 保存 → 重新渲染)

    课后练习

  • 做一个"点赞"按钮:每点一次数字 +1,再点一下变成取消点赞并 -1(用 classList 换爱心颜色)。
  • 加餐:输入分数,点提交后追加到成绩列表,并统计平均分显示在页面上。
  • 做一个"图片查看器":小图列表 + 中间大图,点小图大图跟着换(提示:src 替换 + querySelectorAll 循环绑定)。
  • 给第 12 节的待办清单加一个"清空全部"按钮和一个"剩余任务数"统计。
  • 用事件委托给一个 50 条数据的列表绑定点击,点击时在控制台打印被点的内容。
  • 常见问题

    问:querySelectorAll 返回的是什么?能用 querySelectorAll(…).style 吗? 答:它返回一个节点集合(类数组),不是单个元素,不能直接 .style。要遍历后逐个操作,比如 el.classList 或 document.querySelectorAll("p")[0]。

    问:动态创建的元素为什么点击没反应? 答:如果你在创建后用 addEventListener 绑定了应该没问题。如果事件绑在了页面加载时还不存在的元素上,就需要改用事件委托(绑到父元素上)。

    问:innerHTML 和 textContent 到底用哪个? 答:插入自己写的、确定安全的 HTML 结构用 innerHTML;插入用户输入的内容一律用 textContent,避免脚本注入风险。

    问:刷新后数据就没了? 答:数据只存在内存里。要跨页面刷新保留,需要 localStorage 持久化(第 12 节的做法)。


    DOM 是前端开发的"地基",练好它,从"会写页面"到"会做应用"就只差临门一脚。继续学下去,你会发现 React、Vue 这些框架,本质都是在帮你更高效地操作 DOM。

    如果觉得教程有帮助,欢迎关注我的博客

    赞(0)
    未经允许不得转载:171主机测评 » DOM实战入门教程
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址