Skip to content

前端Doc - DOM操作与事件处理、CSS技巧

欢迎来到 Week 3!本周,我们将深入探讨如何使用 JavaScript 进行 DOM(文档对象模型) 操作与事件处理,以及如何利用 高级 CSS 技巧打造美观、实用的网页效果。我们的目标是通过理论结合实际,帮助你快速提升前端开发能力。

本周的案例是:凉风青叶想制作一个可以增删任务的 Todo List。她希望界面是在一个 长方形圆角的半透明磨砂卡片 内,整个页面有一个 自动填充的背景图,当 鼠标悬停在添加按钮上时,按钮会变暗。我们将随着青叶的开发之旅,一步步实现这个项目。

深入理解 DOM 操作与事件处理

什么是 DOM?

DOM(Document Object Model) 是一种编程接口,用于将 HTML 文档结构转化成浏览器可以理解和操作的形式。可以将 DOM 想象成一棵“”,每个 HTML 标签都像是树中的一个节点。根节点是整个文档(通常是 <html>​ 标签),从中分出一个个分支,比如 <body>​、<head>​ 等,逐步延伸到每一个元素和文本节点。这种“树结构”使得开发者可以通过脚本语言(如 JavaScript)轻松访问、修改、添加或删除页面的任意部分,实现对网页的动态更新。

TIP

每当你修改 DOM 树中的节点,比如增加一个新元素或更改文本内容,浏览器会即时渲染更新后的页面,使用户可以立即看到变化。这种树状结构将文档的层次和关系清晰地展现出来,使开发者能够灵活地操控网页内容。

常用的 DOM 选择器与高级用法

在操作 DOM 时,选择器是关键。除了基础的选择器,我们还可以掌握一些高级选择器和技巧,以便更高效地操作元素。以下是一个全面的选择器和 DOM 操作教程,包括实例讲解和代码示例。

首先,我们创建一个简单的 HTML 结构,包含了父子关系及不同的类和属性,为我们之后讲解选择器做准备。

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>DOM 选择器示例</title>
</head>
<body>
  <div id="container">
    <h1>任务列表</h1>
    <ul id="todo-list">
      <li class="task completed">完成任务 1</li>
      <li class="task">待完成任务 2</li>
      <li class="task completed">完成任务 3</li>
      <li class="task">待完成任务 4</li>
    </ul>
    <input type="text" name="task" placeholder="输入新任务">
  </div>
</body>
</html>

在这个结构中,我们有一个包含任务列表的 div​ 元素,其中 li​ 元素表示不同的任务项,每个任务项可能有 completed​ 类,且 input​ 输入框具有 name="task"​ 属性。

1. 基础选择器回顾

  • document.getElementById('id')​:通过 id​ 获取元素。例如,获取 id="container"​ 的元素:

    javascript
    let container = document.getElementById('container');
  • document.getElementsByClassName('class')​:获取具有特定 class​ 的元素集合。例如,获取所有 class="task"​ 的元素:

    javascript
    let tasks = document.getElementsByClassName('task');
  • document.getElementsByTagName('tag')​:获取特定标签名的元素集合。例如,获取所有 li​ 元素:

    javascript
    let listItems = document.getElementsByTagName('li');

2. 高级选择器

  • 属性选择器:document.querySelectorAll('[type="text"]')​ 选择所有 type="text"​ 的输入框。

    javascript
    let textInput = document.querySelector('[type="text"]');
  • 层级选择器:document.querySelectorAll('#container > ul > .task')​ 选择 #container​ 中的直接子元素 ul​ 的所有 class="task"​ 的元素。

    javascript
    let directTasks = document.querySelectorAll('#container > ul > .task');
  • 伪类选择器:document.querySelectorAll('li:nth-child(odd)')​ 选择 li​ 列表中的奇数项。

    javascript
    let oddTasks = document.querySelectorAll('li:nth-child(odd)');
  • 组合选择器:document.querySelectorAll('.task.completed')​ 选择所有同时具有 task​ 和 completed​ 类的元素。

    javascript
    let completedTasks = document.querySelectorAll('.task.completed');

3. 巧用选择器提高效率

在大型项目中,合适的选择器能大幅提高代码可读性和效率。示例:

javascript
// 获取所有完成的任务项
let completedTasks = document.querySelectorAll('#todo-list li.completed');

// 获取特定输入框内的值
let taskInput = document.querySelector('input[name="task"]');

DOM 操作技巧

学习完选择器后,接下来是动态操作 DOM 的一些常用技巧。

1. 动态创建和插入元素

使用 document.createElement()​ 创建新元素,并使用 appendChild()​ 插入 DOM 中。

javascript
let newTask = document.createElement('li');
newTask.className = 'task';
newTask.textContent = '新的任务';
document.getElementById('todo-list').appendChild(newTask);

2. 修改元素属性和样式

直接修改元素的属性或使用 style​ 对象更改样式:

javascript
// 修改属性
newTask.setAttribute('data-id', '123');

// 修改样式
newTask.style.color = 'blue';

3. 使用模板字符串生成复杂结构

在需要插入复杂 HTML 时,模板字符串可以提升代码清晰度。

javascript
let taskContent = `
  <div class="task-item">
    <span>${taskText}</span>
    <button class="delete-btn">删除</button>
  </div>
`;
newTask.innerHTML = taskContent;

事件处理进阶:简单实例

为了让页面互动更流畅,我们需要掌握一些简单的事件处理技巧。接下来,我们通过简洁的例子,来介绍事件委托、防抖与节流、自定义事件的基本用法。

这里能看懂、理解就可以了

现阶段不要求手写这种功能,后面学框架的时候会细说。


1. 事件委托:一个监听器管理多个按钮

假设我们有一个简单的待办事项列表,每个任务后面都有一个“删除”按钮。我们可以只给父元素绑定一个点击事件,然后通过判断点击的目标元素是否是“删除”按钮,来实现删除功能。

示例 HTML

html
<ul id="todo-list">
  <li>任务 1 <button class="delete-btn">删除</button></li>
  <li>任务 2 <button class="delete-btn">删除</button></li>
</ul>

示例代码

javascript
document.getElementById('todo-list').addEventListener('click', function(event) {
  if (event.target.classList.contains('delete-btn')) {
    event.target.parentElement.remove(); // 删除对应的任务项
  }
});

在这个例子中,点击“删除”按钮后,会找到按钮的父元素 <li>​ 并将其移除,完成删除任务的功能。事件委托的好处是即使将来添加新任务项也不需要额外绑定事件。


2. 防抖:输入框防止频繁搜索

假设你在输入框中输入文字,每次输入都触发搜索请求会导致页面卡顿。我们可以用“防抖”技巧,设置只有在用户停止输入一定时间后,才执行搜索。

示例 HTML

html
<input type="text" id="search-box" placeholder="搜索任务">

示例代码

javascript
function debounce(func, delay) {
  let timeout;
  return function() {
    clearTimeout(timeout);
    timeout = setTimeout(func, delay);
  };
}

document.getElementById('search-box').addEventListener('input', debounce(function() {
  console.log('执行搜索');
}, 300));

在这里,当用户输入完并停止 300 毫秒后,才会打印“执行搜索”。这样避免了频繁搜索,提升了页面性能。


3. 自定义事件:通知新任务添加

自定义事件让不同组件之间可以简单沟通。比如,当我们添加一个新任务时,可以触发自定义事件,让其他功能模块(如任务统计)知道有新任务了。

示例代码

javascript
// 创建自定义事件,携带新任务的信息
let event = new CustomEvent('newTask', { detail: { taskName: '学习事件处理' } });

// 触发自定义事件
document.dispatchEvent(event);

// 监听自定义事件
document.addEventListener('newTask', function(event) {
  console.log('已添加新任务:', event.detail.taskName);
});

当添加新任务时,我们触发了 newTask​ 事件,其他模块可以监听此事件并获取任务名称来更新任务统计或提醒用户。

明白了,针对不太直观的 CSS 布局或复杂的效果,我会添加对应的 HTML 结构例子来帮助理解。以下是更详细的 CSS 技巧说明,包含每个技巧的 CSS 和 HTML 示例。


高级 CSS 技巧与实战

高级选择器与应用

以下是一些高级 CSS 选择器,可以帮助你更精确地控制特定元素。

1. 属性选择器

属性选择器根据元素的特定属性选中元素,适合对具有动态属性的内容进行样式设置。

css
/* 选择 data-priority 为 high 的任务项,并将其文字变为红色 */
li[data-priority="high"] {
  color: red;
}

示例 HTML

html
<ul>
  <li data-priority="low">任务 1</li>
  <li data-priority="high">任务 2</li>
  <li data-priority="medium">任务 3</li>
</ul>

这里,任务 2​ 的优先级是 “high”,会显示为红色。属性选择器可以根据内容的特性,自动为特定任务增加样式。

2. 伪类选择器

伪类选择器可选择元素的特定状态或位置,通常用于交互样式。

css
/* 选择列表中的第一个任务项,加粗显示 */
li:first-child {
  font-weight: bold;
}

/* 鼠标悬停在按钮上时,改变背景色 */
.button:hover {
  background-color: #2980b9;
}

示例 HTML

html
<ul>
  <li>任务 1</li>
  <li>任务 2</li>
  <li>任务 3</li>
</ul>
<button class="button">点击我</button>

在此例中,第一个 li​ 项会加粗显示,按钮在鼠标悬停时背景色变化,提供交互反馈。

3. 伪元素选择器

伪元素选择器可以为元素添加虚拟内容或装饰,使得列表项更具条理性。

css
/* 在每个任务项前加上一个勾选符号 */
li::before {
  content: '✓ ';
  color: green;
}

示例 HTML

html
<ul>
  <li>任务 1</li>
  <li>任务 2</li>
  <li>任务 3</li>
</ul>

每个任务项前会显示绿色的“✓”符号,用来表示任务状态。


实用且美观的 CSS 属性与技巧

1. Flexbox 布局

Flexbox 是一种简洁而强大的布局工具,能让子元素灵活排列,适应不同屏幕尺寸。

css
.container {
  display: flex;
  flex-direction: column; /* 纵向排列 */
  align-items: center;    /* 居中对齐 */
  gap: 10px;              /* 子元素间距 */
}

示例 HTML

html
<div class="container">
  <div class="box">任务 1</div>
  <div class="box">任务 2</div>
  <div class="box">任务 3</div>
</div>

在这个例子中,.container​ 使用 display: flex​,将每个 div.box​ 纵向排列并居中,每个任务项间距为 10px。通过更改 flex-direction​ 为 row​,也可以将这些元素横向排列。

2. CSS 变量

CSS 变量可以帮助你更好地管理样式,在多个地方复用相同的值,便于维护主题色和常用样式。

css
:root {
  --primary-color: #3498db;
  --button-padding: 10px 20px;
}

.button {
  background-color: var(--primary-color);
  color: white;
  padding: var(--button-padding);
}

示例 HTML

html
<button class="button">按钮</button>

这个按钮的背景颜色和填充样式可以通过 CSS 变量来定义和修改,调整 --primary-color​ 或 --button-padding​ 可以同步改变所有按钮的样式。

3. 过渡与动画

过渡效果让样式变得更具动态感,常用于鼠标悬停、按钮点击等交互效果。

css
.button {
  transition: background-color 0.3s, transform 0.3s;
}

.button:hover {
  background-color: #2980b9;
  transform: scale(1.05); /* 放大按钮 */
}

示例 HTML

html
<button class="button">悬停放大</button>

当用户将鼠标悬停在 .button​ 上时,按钮的背景颜色变化且略微放大,增加了用户的交互体验。

4. 背景混合模式

背景混合模式允许将背景图片与颜色叠加,实现独特的视觉效果。

css
.card {
  background: linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.5)),
              url('background.jpg');
  background-blend-mode: overlay;
}

示例 HTML

html
<div class="card">
  <p>带混合模式的背景</p>
</div>

在这个 .card​ 元素中,背景图片和白色半透明渐变层叠在一起,形成了柔和的视觉效果。

5. 媒体查询与响应式设计(理解)

媒体查询帮助页面适应不同屏幕尺寸,在移动端和桌面端分别展示合适的样式。

css
@media (max-width: 600px) {
  .card {
    width: 90%;
    margin: 20px auto;
  }
}

示例 HTML

html
<div class="card">
  <p>响应式设计示例</p>
</div>

在小于 600px 的屏幕上(如手机屏幕),.card​ 元素的宽度将调整为 90%,并居中显示,保证在不同设备上有更好的视觉效果。

案例故事:凉风青叶的 Todo List 开发之旅

1. 项目背景与需求分析

背景:凉风青叶是一名在游戏公司“鹰飞跃动”担任前端开发的新员工。她热爱编程,对设计也有独到的见解。为了提高自己的技术水平,她决定利用业余时间制作一个精美的 Todo List 应用。

需求:

  • 能够 添加 和 删除 任务。
  • 界面在一个 长方形圆角的半透明磨砂卡片 内。
  • 整个页面有一个 自动填充的背景图。
  • 当 鼠标悬停在添加按钮上时,按钮会变暗。

2. 设计思路与技术选型

设计思路:

  • 功能性:实现任务的添加和删除,确保基本的交互流程顺畅。
  • 美观性:采用现代化的设计风格,利用 CSS3 的新特性打造精美的视觉效果。
  • 用户体验:通过动画和交互反馈,提升用户的操作体验。

技术选型:

  • 前端框架:纯原生 JavaScript、HTML 和 CSS,巩固基础知识。
  • CSS 技巧:使用 Flexbox、CSS 变量、过渡与动画等高级技巧。
  • DOM 操作:熟练使用 DOM API,实现动态的元素增删和事件绑定。

3. 开发过程详解

第一步:搭建基本结构

青叶首先创建了基本的 HTML 结构:

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>青叶的 Todo List</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div class="card">
    <h1>我的待办事项</h1>
    <ul id="todo-list">
      <!-- 动态生成的任务项 -->
    </ul>
    <div class="input-group">
      <input type="text" id="task-input" placeholder="请输入新任务">
      <button id="add-button" class="button">添加</button>
    </div>
  </div>
  <script src="script.js"></script>
</body>
</html>

思考过程:

  • 将主要内容放在一个 div​,赋予 card​ 类名,方便样式控制。
  • 使用 ul​ 列表来展示任务项,便于动态添加和删除。
  • 在输入框和添加按钮外层加一个 div​,便于布局。

第二步:设置全局样式与背景

青叶希望整个页面有一个自动填充的背景图,于是她在 style.css​ 中设置了以下样式:

css
body {
  margin: 0;
  padding: 0;
  background-image: url('background.jpg');
  background-size: cover;
  background-position: center;
  font-family: '微软雅黑', sans-serif;
}

思考过程:

  • 使用 background-size: cover​ 确保背景图能够覆盖整个页面。
  • 选择 background-position: center​ 使背景图居中显示。
  • 设置 font-family​,统一页面的字体风格。

第三步:设计卡片样式

为了实现半透明磨砂效果,青叶使用了 rgba​ 和 backdrop-filter​:

css
.card {
  background: rgba(255, 255, 255, 0.3);
  backdrop-filter: blur(10px);
  border-radius: 20px;
  width: 400px;
  margin: 100px auto;
  padding: 20px;
  box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
  text-align: center;
}

思考过程:

  • 使用 rgba(255, 255, 255, 0.3)​ 设置半透明的白色背景。
  • backdrop-filter: blur(10px)​ 实现磨砂玻璃效果。
  • border-radius: 20px​ 让卡片有圆角,提升美感。
  • 添加 box-shadow​ 增加层次感。

第四步:布局任务列表和输入区域

青叶希望任务列表和输入区域能够整齐排列,便于用户查看和操作。

css
#todo-list {
  list-style: none;
  padding: 0;
  margin: 20px 0;
}

#todo-list li {
  background: rgba(255, 255, 255, 0.5);
  margin: 10px 0;
  padding: 10px 15px;
  border-radius: 10px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.input-group {
  display: flex;
  justify-content: center;
}

#task-input {
  width: 70%;
  padding: 10px;
  border: none;
  border-radius: 10px 0 0 10px;
  outline: none;
}

.button {
  width: 30%;
  background-color: var(--primary-color);
  color: white;
  border: none;
  border-radius: 0 10px 10px 0;
  cursor: pointer;
  transition: background-color 0.3s;
}

思考过程:

  • 使用 Flexbox 布局,使任务项内部的文本和删除按钮水平排列。
  • 为输入框和按钮设置 border-radius​,使其连成一体,增加美观度。
  • 使用 CSS 变量 --primary-color​,方便全局修改主题色。

第五步:添加鼠标悬停效果

为了让按钮在鼠标悬停时变暗,青叶添加了以下样式:

css
.button:hover {
  background-color: darken(var(--primary-color), 10%);
}

注意:由于 CSS 不支持直接在 background-color​ 中使用 darken​ 函数,青叶改用手动计算颜色值,或者借助预处理器(如 Sass)。

css
.button:hover {
  background-color: #2c80b4; /* 比原始颜色略深 */
}

思考过程:

  • 通过修改 background-color​,实现按钮变暗的效果。
  • 使用 transition​ 属性,使颜色过渡更加平滑。

第六步:编写 JavaScript 实现功能

青叶开始编写 script.js​,实现添加和删除任务的功能。

javascript
// 获取元素
const addButton = document.getElementById('add-button');
const taskInput = document.getElementById('task-input');
const todoList = document.getElementById('todo-list');

// 绑定事件
addButton.addEventListener('click', addTask);

// 添加任务函数
function addTask() {
  const taskText = taskInput.value.trim();
  if (taskText === '') {
    alert('请输入任务内容!');
    return;
  }

  // 创建任务项
  const listItem = document.createElement('li');

  // 创建任务文本
  const taskSpan = document.createElement('span');
  taskSpan.textContent = taskText;

  // 创建删除按钮
  const deleteButton = document.createElement('button');
  deleteButton.textContent = '删除';
  deleteButton.classList.add('delete-btn');

  // 组装任务项
  listItem.appendChild(taskSpan);
  listItem.appendChild(deleteButton);
  todoList.appendChild(listItem);

  // 清空输入框
  taskInput.value = '';

  // 绑定删除事件
  deleteButton.addEventListener('click', function() {
    todoList.removeChild(listItem);
  });
}

思考过程:

  • 输入验证:在添加任务前,检查输入框是否为空,避免添加空任务。
  • 动态创建元素:使用 createElement​ 创建新的 DOM 元素,组装任务项。
  • 事件绑定:为删除按钮绑定点击事件,点击时删除对应的任务项。
  • 清空输入框:添加任务后,清空输入框,方便继续输入。

第七步:优化用户体验

青叶希望在用户按下回车键时也能添加任务,提高操作的便捷性。

javascript
// 为输入框绑定键盘事件
taskInput.addEventListener('keyup', function(event) {
  if (event.key === 'Enter') {
    addTask();
  }
});

思考过程:

  • 监听输入框的 keyup​ 事件,当按下 Enter​ 键时,调用 addTask​ 函数。
  • 提升用户体验,使操作更加流畅。

第八步:美化删除按钮

青叶为删除按钮添加样式,使其与整体风格一致。

css
.delete-btn {
  background-color: transparent;
  border: none;
  color: #e74c3c;
  cursor: pointer;
  font-size: 14px;
  transition: color 0.3s;
}

.delete-btn:hover {
  color: #c0392b;
}

思考过程:

  • 使删除按钮背景透明,与任务项背景融合。
  • 使用红色系的文字颜色,提示用户这是删除操作。
  • 添加悬停效果,增强交互性。

4. 项目完成与总结

经过一番努力,青叶成功地完成了她的 Todo List 应用。她不仅实现了所有的功能需求,还通过学习和应用高级的 CSS 和 JavaScript 技巧,提升了网页的美观性和用户体验。

image

项目收获:

  • 掌握了高级的 DOM 操作技巧,如事件委托和动态元素创建。
  • 深入理解了 CSS 高级选择器和实用属性,能够更灵活地控制样式。
  • 学会了如何从设计思路出发,逐步实现项目,提升了整体的开发能力。

青叶的感悟:

“通过这个项目,我不仅巩固了前端的基础知识,还学会了很多实用的技巧。原来,编程和设计一样,都需要细心和创造力。我相信这些经验会对我今后的工作大有帮助!”

总结

在本周的学习中,我们:

  • 深入学习了 DOM 操作与事件处理,包括高级选择器、事件委托和防抖节流等技巧。
  • 掌握了高级 CSS 技巧,如 Flexbox 布局、CSS 变量、过渡与动画、背景混合模式等。
  • 通过案例实践,从设计思路到实现过程,完整地体验了项目开发的流程。