JavaScript 从零基础到精通系列:DOM 操作与事件驱动编程

摘要: JavaScript 在浏览器中的魔法,几乎全部通过 DOM 实现。本篇将学会如何选取页面元素,动态修改内容和样式,并深刻理解事件模型(事件冒泡、委托)。你将亲手打造图片轮播器和留言板,把前几篇的数据逻辑变成用户看得见、摸得着的交互界面。


一、认识 DOM

DOM(Document Object Model)是浏览器将 HTML 文档解析成的一个树形对象模型。每个标签、属性、文本都是树上的一个节点。JavaScript 可以访问、修改、添加、删除这些节点,从而动态改变页面。根节点是 document,代表整个页面。

二、获取元素

最常用的几个方法:

javascript 复制代码
// 通过 ID
const header = document.getElementById('main-header');
// 通过 CSS 选择器(返回第一个匹配)
const btn = document.querySelector('.btn-primary');
// 通过 CSS 选择器(返回所有匹配,NodeList 静态或动态取决于方法)
const items = document.querySelectorAll('.item');
// 通过类名
const errors = document.getElementsByClassName('error');

querySelectorquerySelectorAll 使用 CSS 选择器语法,灵活强大,是现在的主流选择方式。

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>DOM 选择器测试</title>
    <style>
        /* 给元素加点样式方便看 */
        #main-header { color: blue; font-size: 20px; }
        .btn-primary { background: green; color: white; padding: 5px; }
        .item { margin: 5px; border: 1px solid #ccc; }
        .error { color: red; font-weight: bold; }
    </style>
</head>
<body>

    <!-- 我们要获取的元素都在这里 -->
    <h1 id="main-header">我是 ID 为 main-header 的标题</h1>
    <button class="btn-primary">我是 class 为 btn-primary 的按钮</button>
    <ul>
        <li class="item">列表项 1</li>
        <li class="item">列表项 2</li>
        <li class="item error">列表项 3(带 error)</li>
    </ul>

<script>
// ======================
// 1. 通过 ID 获取
// ======================
const header = document.getElementById('main-header');
console.log("通过 ID 获取:", header); // 看控制台!
header.style.color = "red"; // 把文字变红 → 页面能看到效果!


// ======================
// 2. 通过 CSS 选择器(单个)
// ======================
const btn = document.querySelector('.btn-primary');
console.log("通过选择器获取按钮:", btn);
btn.innerText = "我被修改啦!"; // 修改按钮文字 → 页面能看到!


// ======================
// 3. 获取所有 .item
// ======================
const items = document.querySelectorAll('.item');
console.log("所有列表项:", items);

// 循环修改所有 item
items.forEach((item, index) => {
    item.innerText = `我是第 ${index+1} 个 item`;
});


// ======================
// 4. 通过 class 获取
// ======================
const errors = document.getElementsByClassName('error');
console.log("错误元素:", errors[0]);
errors[0].innerText = "我是错误提示!";

</script>
</body>
</html>

三、修改内容与属性

  • textContent:纯文本,不解析 HTML。

  • innerHTML:会解析 HTML 标签,有安全风险(XSS),插入用户输入时慎用。

  • value:表单元素的值。

javascript 复制代码
const title = document.querySelector('h1');
title.textContent = '新标题';
​
const link = document.querySelector('a');
link.setAttribute('href', 'https://example.com');
link.getAttribute('href');
html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <title>DOM 修改测试</title>
</head>

<body>

    <!-- 必须要有这两个元素,JS 才能找到它们 -->
    <h1>旧标题</h1>
    <a>我是链接</a>

    <script>
        // 1. 修改标题文字
        const title = document.querySelector('h1');
        title.textContent = '新标题'; // 页面会立刻变成"新标题"

        // 2. 修改链接地址
        const link = document.querySelector('a');
        link.setAttribute('href', 'https://example.com');

        // 3. 获取链接地址并打印(这样才能看到效果)
        const hrefValue = link.getAttribute('href');
        console.log('链接地址是:', hrefValue); // 控制台会输出 https://example.com

        // 给链接加点文字,方便看
        link.textContent = "点我去 example.com";
    </script>

</body>

</html>

样式修改:通过 element.style 修改内联样式,或通过 classList 增删类名(推荐)。

javascript 复制代码
const box = document.querySelector('.box');
box.style.backgroundColor = 'lightblue';
​
// classList 方法
box.classList.add('active');
box.classList.remove('hidden');
box.classList.toggle('dark-mode');
box.classList.contains('active'); // true/false
html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>classList + style 测试</title>
    <style>
        /* 必须先定义 .box 样式 */
        .box {
            width: 200px;
            height: 200px;
            margin: 20px;
            border: 1px solid #333;
        }
        /* 下面是 JS 会操作的 class */
        .active {
            border: 4px solid red;
        }
        .hidden {
            display: none;
        }
        .dark-mode {
            background-color: #333;
            color: white;
        }
    </style>
</head>
<body>

    <!-- 这个元素就是 JS 要找的 .box -->
    <div class="box">我是盒子</div>

<script>
    const box = document.querySelector('.box');

    // 1. 直接修改样式:背景变浅蓝色
    box.style.backgroundColor = 'lightblue';

    // 2. 添加 class → 出现红边框
    box.classList.add('active');

    // 3. 移除 class(这里本来没有,所以没变化)
    box.classList.remove('hidden');

    // 4. 切换 class(有就删,没有就加)
    box.classList.toggle('dark-mode');

    // 5. 判断是否包含某个 class → 打印到控制台
    const hasActive = box.classList.contains('active');
    console.log('是否包含 active:', hasActive); // true
</script>

</body>
</html>

四、事件监听:让页面响应用户

事件是用户或浏览器自身发生的动作,如点击、移动、键盘、加载等。

javascript 复制代码
const button = document.getElementById('myBtn');
​
// 推荐方式:addEventListener
button.addEventListener('click', function(event) {
    console.log('按钮被点击了', event);
    // event.target 是触发事件的元素
});
​
// 匿名函数或具名函数都可以
function handleMouseEnter(e) {
    console.log('鼠标进来了');
}
button.addEventListener('mouseenter', handleMouseEnter);
// 移除监听
button.removeEventListener('mouseenter', handleMouseEnter);
html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>事件监听测试</title>
    <style>
        #myBtn {
            padding: 10px 20px;
            font-size: 16px;
            cursor: pointer;
        }
    </style>
</head>
<body>

    <!-- 必须有这个按钮,JS 才能找到它 -->
    <button id="myBtn">点我测试</button>

<script>
    const button = document.getElementById('myBtn');

    // 1. 点击事件
    button.addEventListener('click', function(event) {
        console.log('✅ 按钮被点击了', event);
        alert('你点击了按钮!');
    });

    // 2. 鼠标移入事件
    function handleMouseEnter(e) {
        console.log('🐭 鼠标进来了');
        button.style.background = "lightgreen";
    }

    // 绑定鼠标移入
    button.addEventListener('mouseenter', handleMouseEnter);

    // 注意:我先注释掉移除,不然刚绑定就删了,没效果!
    // button.removeEventListener('mouseenter', handleMouseEnter);
</script>
</body>
</html>

常见事件类型:

  • 鼠标:click, dblclick, mousedown, mouseup, mousemove, mouseenter, mouseleave

  • 键盘:keydown, keyup, keypress(已废弃,建议 keydown)

  • 表单:submit, change, input, focus, blur

  • 文档/窗口:DOMContentLoaded, load, resize, scroll

五、事件流:捕获与冒泡及事件委托

当一个元素触发事件,它会经历三个阶段:

捕获阶段:从 window 向目标元素向下传播。

目标阶段:事件到达目标元素。

冒泡阶段:事件从目标元素向上冒泡到 window

addEventListener 的第三个参数默认 false(在冒泡阶段处理)。利用冒泡可以实现事件委托 :把监听器绑定在父元素上,通过 event.target 识别实际点击的子元素。这样可以大幅减少监听器数量,且动态添加的子元素也能自动拥有此行为。

javascript 复制代码
// 列表委托
const list = document.querySelector('#myList');
list.addEventListener('click', function(e) {
    if (e.target.tagName === 'LI') {
        console.log('你点击了', e.target.textContent);
    }
});
html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>事件委托测试</title>
    <style>
        li {
            padding: 8px 12px;
            margin: 5px 0;
            background: #f1f1f1;
            cursor: pointer;
        }
        li:hover {
            background: lightblue;
        }
    </style>
</head>
<body>

    <!-- 必须有这个列表,JS 才能生效 -->
    <ul id="myList">
        <li>我是第 1 项</li>
        <li>我是第 2 项</li>
        <li>我是第 3 项</li>
        <li>我是第 4 项</li>
    </ul>

<script>
    // 事件委托:只给父 ul 绑定事件
    const list = document.querySelector('#myList');
    
    list.addEventListener('click', function(e) {
        // 判断点击的是不是 LI 元素
        if (e.target.tagName === 'LI') {
            console.log('你点击了:', e.target.textContent);
            
            // 可选:页面弹出提示,更直观
            alert('你点击了:' + e.target.textContent);
        }
    });
</script>
</body>

六、动态创建与删除元素

javascript 复制代码
// 创建一个新 div
const newDiv = document.createElement('div');
newDiv.textContent = '我是新来的';
newDiv.classList.add('box');
// 追加到父元素末尾
document.body.appendChild(newDiv);
​
// 在前面插入
const referenceNode = document.querySelector('#ref');
document.body.insertBefore(newDiv, referenceNode);
​
// 删除元素
newDiv.remove();

七、表单操作与定时器

获取表单数据:

javascript 复制代码
const form = document.querySelector('#userForm');
form.addEventListener('submit', function(e) {
    e.preventDefault(); // 阻止默认提交刷新页面
    const username = document.querySelector('#username').value;
    console.log('提交的用户名:', username);
});
html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>表单提交测试</title>
    <style>
        form {
            margin: 20px;
            padding: 15px;
            border: 1px solid #ccc;
            display: inline-block;
        }
        input {
            padding: 8px;
            margin-right: 5px;
        }
        button {
            padding: 8px 12px;
            cursor: pointer;
        }
    </style>
</head>
<body>

    <!-- 必须有这个表单,JS 才能生效 -->
    <form id="userForm">
        <input type="text" id="username" placeholder="请输入用户名">
        <button type="submit">提交</button>
    </form>

<script>
const form = document.querySelector('#userForm');

form.addEventListener('submit', function(e) {
    // 阻止页面自动刷新(最关键!)
    e.preventDefault();
    
    // 获取输入框的值
    const username = document.querySelector('#username').value;
    
    // 控制台打印 + 页面提示
    console.log('提交的用户名:', username);
    alert('你提交了:' + username);
});
</script>
</body>
</html>

定时器:

  • setTimeout(fn, delay):延迟执行一次。

  • setInterval(fn, interval):每隔一段时间重复执行。

  • clearTimeout / clearInterval 清除。

javascript 复制代码
// 简单计数器
let count = 0;
const timer = setInterval(() => {
    count++;
    console.log(count);
    if (count >= 5) clearInterval(timer);
}, 1000);

八、综合实战一:图片轮播器

实现一个简单的自动轮播,并带手动切换按钮。

HTML 结构:

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <title>轮播图效果</title>
</head>

<body>

    <div class="slider">
        <div class="slides">
            <img src="https://picsum.photos/400/300?random=4" class="active" />
            <img src="https://picsum.photos/400/300?random=5" />
            <img src="https://picsum.photos/400/300?random=6" />
        </div>
        <button id="prev">←</button>
        <button id="next">→</button>
    </div>

</body>

</html>

CSS:

css 复制代码
.slider {
    position: relative;
    width: 400px;
    height: 300px;
    overflow: hidden;
    border: 2px solid #ccc;
}

.slides {
    width: 100%;
    height: 100%;
}

.slides img {
    position: absolute;
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: none;
    /* 默认隐藏 */
}

.slides img.active {
    display: block;
    /* 只显示当前激活的 */
}
/* 按钮样式 */

#prev,
#next {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    background: rgba(0, 0, 0, 0.5);
    color: white;
    border: none;
    padding: 10px 15px;
    cursor: pointer;
    font-size: 18px;
    z-index: 10;
}

#prev {
    left: 10px;
}

#next {
    right: 10px;
}

JS 逻辑:

javascript 复制代码
(function() {
    const slides = document.querySelectorAll('.slides img');
    const prevBtn = document.getElementById('prev');
    const nextBtn = document.getElementById('next');
    let currentIndex = 0;

    function showSlide(index) {
        slides.forEach((slide, i) => {
            slide.classList.toggle('active', i === index);
        });
    }

    function nextSlide() {
        currentIndex = (currentIndex + 1) % slides.length;
        showSlide(currentIndex);
    }

    function prevSlide() {
        currentIndex = (currentIndex - 1 + slides.length) % slides.length;
        showSlide(currentIndex);
    }

    nextBtn.addEventListener('click', nextSlide);
    prevBtn.addEventListener('click', prevSlide);

    // 自动播放
    setInterval(nextSlide, 3000);
})();

九、综合实战二:简单留言板

结合数组、DOM 操作和事件。用户输入留言,点击发布后添加到列表,可删除。

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>消息列表 - 新增/删除</title>
    <style>
        /* 简单美化,让效果更直观 */
        .container {
            width: 400px;
            margin: 30px auto;
        }
        #messageInput {
            width: 280px;
            padding: 8px;
        }
        #addBtn {
            padding: 8px 12px;
            cursor: pointer;
        }
        #messageList {
            margin-top: 15px;
            list-style: none;
            padding: 0;
        }
        #messageList li {
            padding: 10px;
            background: #f5f5f5;
            margin: 5px 0;
            display: flex;
            justify-content: space-between;
        }
        .delete {
            color: white;
            background: red;
            border: none;
            padding: 3px 6px;
            cursor: pointer;
        }
    </style>
</head>
<body>

    <!-- 配套 HTML 结构 -->
    <div class="container">
        <input type="text" id="messageInput" placeholder="请输入内容">
        <button id="addBtn">添加</button>
        <ul id="messageList"></ul>
    </div>

<script>
const input = document.querySelector('#messageInput');
const addBtn = document.querySelector('#addBtn');
const list = document.querySelector('#messageList');

// 添加消息
addBtn.addEventListener('click', function() {
    const text = input.value.trim();
    if (!text) return alert('内容不能为空');
    
    const li = document.createElement('li');
    li.innerHTML = `
        <span>${text}</span>
        <button class="delete">删除</button>
    `;
    list.appendChild(li);
    input.value = '';
    input.focus();
});

// 事件委托 - 删除
list.addEventListener('click', function(e) {
    if (e.target.classList.contains('delete')) {
        e.target.parentElement.remove();
    }
});
</script>
</body>
</html>

总结: DOM 是连接 JavaScript 和用户界面的桥梁。我们掌握了元素选取、内容样式修改、事件监听及事件委托,这些是任何前端项目 80% 的日常工作。通过轮播器和留言板,我们练习了动态节点创建、定时器和表单处理。


如果这篇文章帮你解决了实操上的困惑,别忘记点击点赞、分享 ,也可以留言告诉我你遇到的其它问题,我会尽快回复。动手练习是掌握编程最快的方法,请务必亲手敲一遍本文的所有示例代码,并截图保存你的成果。你的关注是我坚持原创和细节共享的力量来源,谢谢大家。

相关推荐
橙子家23 分钟前
浏览器缓存之【结构化数据库与缓存】: IndexedDB、Cache storage 和 Storage buckets
前端
user205855615181328 分钟前
X6 中边悬浮置顶,规避 `mouseleave` 事件丢失问题
前端
李明卫杭州30 分钟前
CSS aspect-ratio 属性完全指南
前端
Pedantic2 小时前
SwiftUI 手势层级(Gesture Hierarchy)详解
前端
飘尘3 小时前
前端转型全栈(Java后端)的快速上手指引
前端·后端·全栈
一颗烂土豆3 小时前
Meshopt 压缩深度解析,为什么它比 Draco 更快
前端·javascript·webgl
浏览器工程师4 小时前
AI Agent 接浏览器任务,先别让它一路点到底
前端·后端
雨季mo浅忆4 小时前
VSCode自动格式化三要素
前端
爱勇宝5 小时前
深扒 Anthropic 1680 位工程师简历:应届生几乎没机会,AI 公司最缺的不是博士
前端·后端·程序员