引言
HTML5作为现代Web开发的基石,不仅定义了网页的结构,还提供了丰富的API来实现复杂的交互功能。从零基础开始学习HTML5前端开发,需要系统地掌握核心技能,并通过实战项目来巩固知识。本文将为你提供一个全面的学习路径,涵盖从基础语法到高级应用,再到实战项目的完整攻略。
第一部分:HTML5基础语法与结构
1.1 HTML5文档结构
HTML5的文档结构比之前的版本更加简洁。一个标准的HTML5文档如下:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HTML5页面标题</title>
</head>
<body>
<!-- 页面内容 -->
</body>
</html>
关键点说明:
<!DOCTYPE html>:声明文档类型为HTML5,必须放在文档的第一行。<html>:根元素,lang属性指定文档语言。<head>:包含文档的元数据,如字符集、视口设置、标题等。<meta charset="UTF-8">:指定字符编码为UTF-8,支持多语言。<meta name="viewport" content="width=device-width, initial-scale=1.0">:确保页面在移动设备上正确缩放。<title>:定义页面标题,显示在浏览器标签页上。
1.2 常用HTML5标签
HTML5引入了许多新标签,使语义化更加清晰。以下是一些常用标签:
<header>
<nav>
<ul>
<li><a href="#">首页</a></li>
<li><a href="#">关于</a></li>
</ul>
</nav>
</header>
<main>
<article>
<h1>文章标题</h1>
<p>文章内容...</p>
</article>
<aside>
<h2>侧边栏</h2>
<p>相关链接...</p>
</aside>
</main>
<footer>
<p>© 2023 版权所有</p>
</footer>
语义化标签的优势:
- 提高可访问性,屏幕阅读器能更好地理解页面结构。
- 有利于SEO,搜索引擎能更准确地抓取内容。
- 代码更易读和维护。
1.3 表单与输入类型
HTML5增强了表单功能,提供了多种新的输入类型:
<form>
<label for="email">邮箱:</label>
<input type="email" id="email" name="email" required>
<label for="date">日期:</label>
<input type="date" id="date" name="date">
<label for="range">范围:</label>
<input type="range" id="range" name="range" min="0" max="100">
<label for="color">颜色:</label>
<input type="color" id="color" name="color">
<label for="file">文件:</label>
<input type="file" id="file" name="file" multiple>
<button type="submit">提交</button>
</form>
新输入类型的优势:
- 提供更好的用户体验,如日期选择器、颜色选择器。
- 移动设备上会自动调用相应的键盘类型。
- 内置验证功能,如
type="email"会自动验证邮箱格式。
第二部分:CSS3样式与布局
2.1 CSS3选择器
CSS3提供了更强大的选择器,使样式控制更加灵活:
/* 基本选择器 */
#header { /* ID选择器 */ }
.nav { /* 类选择器 */ }
div { /* 元素选择器 */ }
/* 属性选择器 */
input[type="text"] { /* 匹配type属性为text的input */ }
a[href^="https"] { /* 匹配以https开头的href */ }
/* 伪类选择器 */
a:hover { /* 鼠标悬停 */ }
input:focus { /* 输入框获得焦点 */ }
li:nth-child(odd) { /* 奇数行的li */ }
/* 伪元素选择器 */
p::first-letter { /* 段落首字母 */ }
div::before { /* 元素前插入内容 */ }
2.2 Flexbox布局
Flexbox是CSS3中用于布局的强大工具,特别适合一维布局:
.container {
display: flex;
justify-content: space-between; /* 主轴对齐 */
align-items: center; /* 交叉轴对齐 */
flex-wrap: wrap; /* 允许换行 */
gap: 10px; /* 项目间距 */
}
.item {
flex: 1; /* 等分剩余空间 */
min-width: 200px; /* 最小宽度 */
}
Flexbox实战示例:
<div class="container">
<div class="item">项目1</div>
<div class="item">项目2</div>
<div class="item">项目3</div>
</div>
2.3 Grid布局
Grid布局是CSS3中用于二维布局的系统:
.grid-container {
display: grid;
grid-template-columns: repeat(3, 1fr); /* 三列等宽 */
grid-template-rows: 100px 200px; /* 两行固定高度 */
gap: 20px; /* 网格间距 */
}
.grid-item {
grid-column: 1 / 3; /* 跨越1到3列 */
grid-row: 1; /* 第一行 */
}
Grid布局示例:
<div class="grid-container">
<div class="grid-item">头部</div>
<div class="grid-item">侧边栏</div>
<div class="grid-item">主内容</div>
<div class="grid-item">底部</div>
</div>
2.4 CSS3动画与过渡
CSS3提供了强大的动画和过渡效果:
/* 过渡效果 */
.button {
background-color: #3498db;
transition: background-color 0.3s ease;
}
.button:hover {
background-color: #2980b9;
}
/* 关键帧动画 */
@keyframes slideIn {
0% {
transform: translateX(-100%);
opacity: 0;
}
100% {
transform: translateX(0);
opacity: 1;
}
}
.animated-element {
animation: slideIn 0.5s ease-out;
}
第三部分:JavaScript核心编程
3.1 ES6+新特性
现代JavaScript开发需要掌握ES6及以上版本的新特性:
// 箭头函数
const add = (a, b) => a + b;
// 解构赋值
const person = { name: 'Alice', age: 30 };
const { name, age } = person;
// 模板字符串
const greeting = `Hello, ${name}! You are ${age} years old.`;
// 默认参数
function greet(name = 'Guest') {
return `Hello, ${name}!`;
}
// 扩展运算符
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]
// Promise
const fetchData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('数据加载完成');
}, 1000);
});
};
// Async/Await
async function loadData() {
try {
const data = await fetchData();
console.log(data);
} catch (error) {
console.error('错误:', error);
}
}
3.2 DOM操作与事件处理
DOM操作是前端开发的核心技能:
// 获取元素
const header = document.getElementById('header');
const items = document.querySelectorAll('.item');
// 创建和插入元素
const newDiv = document.createElement('div');
newDiv.textContent = '新元素';
document.body.appendChild(newDiv);
// 事件处理
const button = document.querySelector('#myButton');
button.addEventListener('click', function(event) {
console.log('按钮被点击', event.target);
});
// 事件委托
document.querySelector('.list').addEventListener('click', function(e) {
if (e.target.tagName === 'LI') {
console.log('点击了列表项:', e.target.textContent);
}
});
3.3 异步编程与AJAX
现代Web应用离不开异步数据请求:
// 使用fetch API
async function fetchUserData() {
try {
const response = await fetch('https://api.example.com/users');
if (!response.ok) {
throw new Error('网络响应错误');
}
const data = await response.json();
console.log(data);
return data;
} catch (error) {
console.error('获取数据失败:', error);
}
}
// 使用axios(需要引入axios库)
// axios.get('https://api.example.com/users')
// .then(response => console.log(response.data))
// .catch(error => console.error(error));
第四部分:HTML5高级API
4.1 Canvas绘图
Canvas API允许在网页上绘制图形:
<canvas id="myCanvas" width="400" height="300" style="border:1px solid #000;"></canvas>
<script>
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// 绘制矩形
ctx.fillStyle = 'blue';
ctx.fillRect(10, 10, 150, 100);
// 绘制圆形
ctx.beginPath();
ctx.arc(200, 150, 50, 0, Math.PI * 2);
ctx.fillStyle = 'red';
ctx.fill();
// 绘制文字
ctx.font = '20px Arial';
ctx.fillStyle = 'black';
ctx.fillText('Hello Canvas!', 100, 250);
</script>
4.2 Web Storage
HTML5提供了本地存储解决方案:
// localStorage
localStorage.setItem('username', 'Alice');
const username = localStorage.getItem('username');
console.log(username); // 输出: Alice
// sessionStorage
sessionStorage.setItem('sessionData', '临时数据');
const sessionData = sessionStorage.getItem('sessionData');
// 存储对象
const user = { name: 'Bob', age: 25 };
localStorage.setItem('user', JSON.stringify(user));
const storedUser = JSON.parse(localStorage.getItem('user'));
console.log(storedUser.name); // 输出: Bob
// 清除存储
localStorage.removeItem('username');
localStorage.clear();
4.3 Geolocation API
获取用户地理位置:
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
(position) => {
const latitude = position.coords.latitude;
const longitude = position.coords.longitude;
console.log(`纬度: ${latitude}, 经度: ${longitude}`);
},
(error) => {
console.error('获取位置失败:', error.message);
},
{
enableHighAccuracy: true, // 高精度
timeout: 5000, // 超时时间
maximumAge: 0 // 不使用缓存
}
);
} else {
console.log('浏览器不支持地理定位');
}
4.4 Web Workers
Web Workers允许在后台线程中运行JavaScript:
// 主线程代码
const worker = new Worker('worker.js');
worker.postMessage('开始计算');
worker.onmessage = function(event) {
console.log('从Worker接收到:', event.data);
};
// worker.js 文件内容
self.onmessage = function(event) {
// 执行耗时计算
const result = heavyComputation(event.data);
self.postMessage(result);
};
function heavyComputation(data) {
// 模拟耗时计算
let sum = 0;
for (let i = 0; i < 1000000000; i++) {
sum += i;
}
return sum;
}
第五部分:响应式设计与移动端适配
5.1 媒体查询
媒体查询是响应式设计的核心:
/* 移动设备优先 */
body {
font-size: 14px;
line-height: 1.5;
}
/* 平板设备 */
@media (min-width: 768px) {
body {
font-size: 16px;
}
.container {
max-width: 720px;
margin: 0 auto;
}
}
/* 桌面设备 */
@media (min-width: 1024px) {
body {
font-size: 18px;
}
.container {
max-width: 960px;
}
.sidebar {
display: block;
}
}
5.2 视口单位与弹性布局
使用视口单位实现真正的响应式:
/* 视口单位 */
.container {
width: 100vw; /* 视口宽度的100% */
height: 100vh; /* 视口高度的100% */
padding: 2vw; /* 相对于视口宽度的内边距 */
}
/* 弹性字体 */
html {
font-size: 16px;
}
@media (min-width: 768px) {
html {
font-size: 18px;
}
}
/* 使用clamp()函数 */
h1 {
font-size: clamp(1.5rem, 5vw, 3rem); /* 最小值, 优先值, 最大值 */
}
5.3 移动端触摸事件
处理移动端的触摸交互:
// 触摸事件
const element = document.getElementById('touchArea');
element.addEventListener('touchstart', function(event) {
console.log('触摸开始', event.touches[0].clientX);
});
element.addEventListener('touchmove', function(event) {
console.log('触摸移动', event.touches[0].clientX);
});
element.addEventListener('touchend', function(event) {
console.log('触摸结束');
});
// 防止页面滚动
element.addEventListener('touchmove', function(event) {
event.preventDefault(); // 阻止默认行为
}, { passive: false });
第六部分:前端工程化与工具链
6.1 模块化开发
现代前端开发需要模块化:
// ES6模块导出
// utils.js
export const formatDate = (date) => {
return new Date(date).toLocaleDateString();
};
export default class Calculator {
add(a, b) {
return a + b;
}
}
// main.js
import Calculator, { formatDate } from './utils.js';
const calc = new Calculator();
console.log(calc.add(1, 2)); // 3
console.log(formatDate(new Date())); // 格式化日期
6.2 构建工具
使用Webpack进行项目构建:
// webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
},
devServer: {
static: {
directory: path.join(__dirname, 'public'),
},
compress: true,
port: 9000,
}
};
6.3 版本控制
使用Git进行版本管理:
# 初始化仓库
git init
# 添加文件
git add .
# 提交更改
git commit -m "Initial commit"
# 创建分支
git branch feature/new-feature
# 切换分支
git checkout feature/new-feature
# 合并分支
git checkout main
git merge feature/new-feature
# 推送到远程仓库
git remote add origin https://github.com/username/repo.git
git push -u origin main
第七部分:实战项目开发
7.1 项目一:个人博客系统
项目目标: 创建一个静态个人博客,展示文章列表和详情页。
技术栈: HTML5 + CSS3 + JavaScript
核心功能:
- 响应式布局的博客首页
- 文章分类和标签系统
- 文章详情页
- 评论功能(前端模拟)
代码示例:
<!-- 博客首页结构 -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>我的博客</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<header class="blog-header">
<h1>我的技术博客</h1>
<nav>
<ul>
<li><a href="#">首页</a></li>
<li><a href="#">分类</a></li>
<li><a href="#">关于</a></li>
</ul>
</nav>
</header>
<main class="blog-main">
<section class="posts-list">
<article class="post-card">
<h2><a href="post1.html">HTML5新特性详解</a></h2>
<p class="post-meta">2023-10-01 | 前端开发</p>
<p>HTML5带来了许多新特性,包括语义化标签、新的表单输入类型、Canvas绘图等...</p>
<a href="post1.html" class="read-more">阅读更多</a>
</article>
<article class="post-card">
<h2><a href="post2.html">CSS3动画实战</a></h2>
<p class="post-meta">2023-09-28 | CSS</p>
<p>CSS3动画让网页更加生动有趣,通过关键帧动画可以实现复杂的视觉效果...</p>
<a href="post2.html" class="read-more">阅读更多</a>
</article>
</section>
<aside class="sidebar">
<div class="categories">
<h3>分类</h3>
<ul>
<li><a href="#">前端开发 (5)</a></li>
<li><a href="#">JavaScript (3)</a></li>
<li><a href="#">CSS (2)</a></li>
</ul>
</div>
<div class="tags">
<h3>标签云</h3>
<div class="tag-cloud">
<a href="#" style="font-size: 1.2em">HTML5</a>
<a href="#" style="font-size: 1em">CSS3</a>
<a href="#" style="font-size: 1.5em">JavaScript</a>
<a href="#" style="font-size: 0.9em">响应式</a>
</div>
</div>
</aside>
</main>
<footer class="blog-footer">
<p>© 2023 我的博客 | 由HTML5构建</p>
</footer>
<script src="script.js"></script>
</body>
</html>
CSS样式示例:
/* styles.css */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: #333;
background-color: #f5f5f5;
}
.blog-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 2rem 0;
text-align: center;
}
.blog-header h1 {
font-size: 2.5rem;
margin-bottom: 1rem;
}
.blog-header nav ul {
list-style: none;
display: flex;
justify-content: center;
gap: 2rem;
}
.blog-header nav a {
color: white;
text-decoration: none;
font-weight: 500;
transition: opacity 0.3s;
}
.blog-header nav a:hover {
opacity: 0.8;
}
.blog-main {
max-width: 1200px;
margin: 2rem auto;
padding: 0 1rem;
display: grid;
grid-template-columns: 2fr 1fr;
gap: 2rem;
}
.posts-list {
display: flex;
flex-direction: column;
gap: 2rem;
}
.post-card {
background: white;
padding: 1.5rem;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
transition: transform 0.3s, box-shadow 0.3s;
}
.post-card:hover {
transform: translateY(-5px);
box-shadow: 0 5px 20px rgba(0,0,0,0.15);
}
.post-card h2 a {
color: #333;
text-decoration: none;
font-size: 1.5rem;
transition: color 0.3s;
}
.post-card h2 a:hover {
color: #667eea;
}
.post-meta {
color: #666;
font-size: 0.9rem;
margin: 0.5rem 0;
}
.read-more {
display: inline-block;
margin-top: 1rem;
color: #667eea;
text-decoration: none;
font-weight: 600;
transition: color 0.3s;
}
.read-more:hover {
color: #764ba2;
}
.sidebar {
display: flex;
flex-direction: column;
gap: 2rem;
}
.sidebar > div {
background: white;
padding: 1.5rem;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.sidebar h3 {
margin-bottom: 1rem;
color: #333;
border-bottom: 2px solid #667eea;
padding-bottom: 0.5rem;
}
.sidebar ul {
list-style: none;
}
.sidebar ul li {
margin-bottom: 0.5rem;
}
.sidebar a {
color: #555;
text-decoration: none;
transition: color 0.3s;
}
.sidebar a:hover {
color: #667eea;
}
.tag-cloud {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.tag-cloud a {
background: #f0f0f0;
padding: 0.3rem 0.8rem;
border-radius: 20px;
color: #555;
text-decoration: none;
transition: all 0.3s;
}
.tag-cloud a:hover {
background: #667eea;
color: white;
}
.blog-footer {
background: #333;
color: white;
text-align: center;
padding: 1.5rem 0;
margin-top: 2rem;
}
/* 响应式设计 */
@media (max-width: 768px) {
.blog-main {
grid-template-columns: 1fr;
}
.blog-header h1 {
font-size: 2rem;
}
.blog-header nav ul {
flex-direction: column;
gap: 0.5rem;
}
.post-card {
padding: 1rem;
}
}
JavaScript交互示例:
// script.js
document.addEventListener('DOMContentLoaded', function() {
// 1. 文章卡片点击效果
const postCards = document.querySelectorAll('.post-card');
postCards.forEach(card => {
card.addEventListener('click', function(e) {
// 如果点击的是链接,不阻止默认行为
if (e.target.tagName === 'A') return;
// 添加点击动画
this.style.transform = 'scale(0.98)';
setTimeout(() => {
this.style.transform = '';
}, 150);
// 跳转到文章详情页
const link = this.querySelector('h2 a');
if (link) {
window.location.href = link.href;
}
});
});
// 2. 标签云点击统计
const tagLinks = document.querySelectorAll('.tag-cloud a');
tagLinks.forEach(tag => {
tag.addEventListener('click', function(e) {
e.preventDefault();
// 显示点击提示
const toast = document.createElement('div');
toast.textContent = `已选择标签: ${this.textContent}`;
toast.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: #667eea;
color: white;
padding: 10px 20px;
border-radius: 5px;
z-index: 1000;
animation: slideIn 0.3s ease-out;
`;
document.body.appendChild(toast);
// 3秒后移除
setTimeout(() => {
toast.remove();
}, 3000);
// 这里可以添加实际的筛选逻辑
console.log(`筛选标签: ${this.textContent}`);
});
});
// 3. 滚动到顶部按钮
const backToTop = document.createElement('button');
backToTop.textContent = '↑';
backToTop.style.cssText = `
position: fixed;
bottom: 30px;
right: 30px;
width: 50px;
height: 50px;
border-radius: 50%;
background: #667eea;
color: white;
border: none;
font-size: 1.5rem;
cursor: pointer;
opacity: 0;
transition: opacity 0.3s, transform 0.3s;
z-index: 100;
`;
document.body.appendChild(backToTop);
// 监听滚动事件
window.addEventListener('scroll', function() {
if (window.pageYOffset > 300) {
backToTop.style.opacity = '1';
backToTop.style.transform = 'scale(1)';
} else {
backToTop.style.opacity = '0';
backToTop.style.transform = 'scale(0.8)';
}
});
// 点击回到顶部
backToTop.addEventListener('click', function() {
window.scrollTo({
top: 0,
behavior: 'smooth'
});
});
// 4. 模拟评论功能
const commentForm = document.createElement('div');
commentForm.innerHTML = `
<div style="background: white; padding: 1.5rem; border-radius: 8px; margin-top: 2rem; box-shadow: 0 2px 10px rgba(0,0,0,0.1);">
<h3 style="margin-bottom: 1rem; color: #333;">发表评论</h3>
<textarea id="commentText" placeholder="写下你的想法..." style="width: 100%; padding: 0.8rem; border: 1px solid #ddd; border-radius: 4px; resize: vertical; min-height: 80px;"></textarea>
<button id="submitComment" style="margin-top: 1rem; padding: 0.5rem 1.5rem; background: #667eea; color: white; border: none; border-radius: 4px; cursor: pointer;">提交评论</button>
<div id="commentsList" style="margin-top: 1.5rem;"></div>
</div>
`;
document.querySelector('.posts-list').appendChild(commentForm);
// 提交评论
document.getElementById('submitComment').addEventListener('click', function() {
const text = document.getElementById('commentText').value.trim();
if (!text) {
alert('请输入评论内容');
return;
}
const comment = document.createElement('div');
comment.style.cssText = `
background: #f9f9f9;
padding: 1rem;
margin-bottom: 0.5rem;
border-radius: 4px;
border-left: 3px solid #667eea;
animation: slideIn 0.3s ease-out;
`;
const date = new Date().toLocaleString();
comment.innerHTML = `
<p style="margin-bottom: 0.5rem;">${text}</p>
<small style="color: #666;">匿名用户 | ${date}</small>
`;
document.getElementById('commentsList').prepend(comment);
document.getElementById('commentText').value = '';
// 保存到localStorage
const comments = JSON.parse(localStorage.getItem('blogComments') || '[]');
comments.unshift({
text: text,
date: date,
user: '匿名用户'
});
localStorage.setItem('blogComments', JSON.stringify(comments.slice(0, 10))); // 只保留最近10条
});
// 页面加载时恢复评论
const savedComments = JSON.parse(localStorage.getItem('blogComments') || '[]');
savedComments.forEach(commentData => {
const comment = document.createElement('div');
comment.style.cssText = `
background: #f9f9f9;
padding: 1rem;
margin-bottom: 0.5rem;
border-radius: 4px;
border-left: 3px solid #667eea;
`;
comment.innerHTML = `
<p style="margin-bottom: 0.5rem;">${commentData.text}</p>
<small style="color: #666;">${commentData.user} | ${commentData.date}</small>
`;
document.getElementById('commentsList').appendChild(comment);
});
});
7.2 项目二:待办事项应用(Todo List)
项目目标: 创建一个功能完整的待办事项管理应用。
技术栈: HTML5 + CSS3 + JavaScript (ES6+)
核心功能:
- 添加、删除、标记完成任务
- 任务分类和筛选
- 数据持久化(localStorage)
- 拖拽排序(可选)
完整代码示例:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>待办事项管理器</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.todo-container {
background: white;
width: 100%;
max-width: 500px;
border-radius: 12px;
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
overflow: hidden;
}
.todo-header {
background: #667eea;
color: white;
padding: 1.5rem;
text-align: center;
}
.todo-header h1 {
font-size: 1.8rem;
margin-bottom: 0.5rem;
}
.todo-header p {
opacity: 0.9;
font-size: 0.9rem;
}
.input-section {
padding: 1.5rem;
border-bottom: 1px solid #eee;
}
.input-group {
display: flex;
gap: 0.5rem;
}
.input-group input {
flex: 1;
padding: 0.8rem;
border: 2px solid #ddd;
border-radius: 6px;
font-size: 1rem;
transition: border-color 0.3s;
}
.input-group input:focus {
outline: none;
border-color: #667eea;
}
.input-group select {
padding: 0.8rem;
border: 2px solid #ddd;
border-radius: 6px;
background: white;
cursor: pointer;
}
.input-group button {
padding: 0.8rem 1.5rem;
background: #667eea;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: 600;
transition: background 0.3s;
}
.input-group button:hover {
background: #5a6fd8;
}
.filter-section {
padding: 1rem 1.5rem;
background: #f9f9f9;
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
.filter-btn {
padding: 0.5rem 1rem;
border: 1px solid #ddd;
background: white;
border-radius: 20px;
cursor: pointer;
transition: all 0.3s;
font-size: 0.9rem;
}
.filter-btn.active {
background: #667eea;
color: white;
border-color: #667eea;
}
.filter-btn:hover {
border-color: #667eea;
}
.stats {
padding: 0.5rem 1.5rem;
background: #f0f0f0;
font-size: 0.85rem;
color: #666;
display: flex;
justify-content: space-between;
}
.todo-list {
padding: 1rem;
max-height: 400px;
overflow-y: auto;
}
.todo-item {
display: flex;
align-items: center;
padding: 1rem;
background: white;
border-radius: 8px;
margin-bottom: 0.5rem;
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
transition: all 0.3s;
cursor: grab;
}
.todo-item:hover {
transform: translateX(5px);
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
}
.todo-item.dragging {
opacity: 0.5;
transform: scale(0.98);
}
.todo-item.completed {
opacity: 0.6;
background: #f9f9f9;
}
.todo-item.completed .todo-text {
text-decoration: line-through;
color: #999;
}
.todo-checkbox {
width: 20px;
height: 20px;
margin-right: 1rem;
cursor: pointer;
}
.todo-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 0.2rem;
}
.todo-text {
font-size: 1rem;
color: #333;
}
.todo-meta {
font-size: 0.75rem;
color: #888;
display: flex;
gap: 0.5rem;
}
.todo-category {
background: #e0e0e0;
padding: 0.1rem 0.5rem;
border-radius: 10px;
font-size: 0.7rem;
}
.todo-actions {
display: flex;
gap: 0.5rem;
}
.todo-actions button {
background: none;
border: none;
cursor: pointer;
font-size: 1.1rem;
padding: 0.3rem;
border-radius: 4px;
transition: background 0.3s;
}
.todo-actions button:hover {
background: #f0f0f0;
}
.delete-btn:hover {
color: #e74c3c;
background: #fee;
}
.empty-state {
text-align: center;
padding: 3rem 1rem;
color: #999;
}
.empty-state p {
margin-top: 0.5rem;
font-size: 0.9rem;
}
/* 动画 */
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.todo-item {
animation: slideIn 0.3s ease-out;
}
/* 响应式设计 */
@media (max-width: 480px) {
.input-group {
flex-direction: column;
}
.input-group select {
width: 100%;
}
.filter-section {
justify-content: center;
}
.todo-header h1 {
font-size: 1.5rem;
}
}
</style>
</head>
<body>
<div class="todo-container">
<div class="todo-header">
<h1>待办事项管理器</h1>
<p>高效管理你的任务</p>
</div>
<div class="input-section">
<div class="input-group">
<input type="text" id="taskInput" placeholder="输入新任务..." maxlength="100">
<select id="categorySelect">
<option value="work">工作</option>
<option value="personal">个人</option>
<option value="shopping">购物</option>
<option value="other">其他</option>
</select>
<button id="addBtn">添加</button>
</div>
</div>
<div class="filter-section">
<button class="filter-btn active" data-filter="all">全部</button>
<button class="filter-btn" data-filter="active">未完成</button>
<button class="filter-btn" data-filter="completed">已完成</button>
<button class="filter-btn" data-filter="work">工作</button>
<button class="filter-btn" data-filter="personal">个人</button>
</div>
<div class="stats">
<span id="totalTasks">总任务: 0</span>
<span id="completedTasks">已完成: 0</span>
</div>
<div class="todo-list" id="todoList">
<div class="empty-state">
<p>暂无任务</p>
<p>添加第一个任务开始吧!</p>
</div>
</div>
</div>
<script>
class TodoManager {
constructor() {
this.tasks = this.loadTasks();
this.currentFilter = 'all';
this.init();
}
init() {
this.bindEvents();
this.render();
}
bindEvents() {
// 添加任务
document.getElementById('addBtn').addEventListener('click', () => this.addTask());
document.getElementById('taskInput').addEventListener('keypress', (e) => {
if (e.key === 'Enter') this.addTask();
});
// 筛选按钮
document.querySelectorAll('.filter-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
e.target.classList.add('active');
this.currentFilter = e.target.dataset.filter;
this.render();
});
});
// 拖拽功能
this.initDragAndDrop();
}
addTask() {
const input = document.getElementById('taskInput');
const category = document.getElementById('categorySelect').value;
const text = input.value.trim();
if (!text) {
this.showNotification('请输入任务内容');
return;
}
const task = {
id: Date.now(),
text: text,
category: category,
completed: false,
createdAt: new Date().toISOString()
};
this.tasks.unshift(task);
this.saveTasks();
this.render();
input.value = '';
this.showNotification('任务添加成功!');
}
toggleTask(id) {
const task = this.tasks.find(t => t.id === id);
if (task) {
task.completed = !task.completed;
this.saveTasks();
this.render();
}
}
deleteTask(id) {
if (confirm('确定要删除这个任务吗?')) {
this.tasks = this.tasks.filter(t => t.id !== id);
this.saveTasks();
this.render();
this.showNotification('任务已删除');
}
}
getFilteredTasks() {
let filtered = [...this.tasks];
// 按状态筛选
if (this.currentFilter === 'active') {
filtered = filtered.filter(t => !t.completed);
} else if (this.currentFilter === 'completed') {
filtered = filtered.filter(t => t.completed);
} else if (['work', 'personal', 'shopping', 'other'].includes(this.currentFilter)) {
filtered = filtered.filter(t => t.category === this.currentFilter);
}
return filtered;
}
render() {
const todoList = document.getElementById('todoList');
const filteredTasks = this.getFilteredTasks();
// 更新统计
document.getElementById('totalTasks').textContent = `总任务: ${this.tasks.length}`;
document.getElementById('completedTasks').textContent = `已完成: ${this.tasks.filter(t => t.completed).length}`;
if (filteredTasks.length === 0) {
todoList.innerHTML = `
<div class="empty-state">
<p>${this.currentFilter === 'all' ? '暂无任务' : '没有符合条件的任务'}</p>
<p>${this.currentFilter === 'all' ? '添加第一个任务开始吧!' : '试试其他筛选条件'}</p>
</div>
`;
return;
}
todoList.innerHTML = filteredTasks.map(task => `
<div class="todo-item ${task.completed ? 'completed' : ''}"
data-id="${task.id}"
draggable="true">
<input type="checkbox"
class="todo-checkbox"
${task.completed ? 'checked' : ''}
onchange="todoManager.toggleTask(${task.id})">
<div class="todo-content">
<span class="todo-text">${this.escapeHtml(task.text)}</span>
<div class="todo-meta">
<span class="todo-category">${this.getCategoryName(task.category)}</span>
<span>${this.formatDate(task.createdAt)}</span>
</div>
</div>
<div class="todo-actions">
<button class="delete-btn" onclick="todoManager.deleteTask(${task.id})" title="删除">🗑️</button>
</div>
</div>
`).join('');
// 重新绑定拖拽事件
this.initDragAndDrop();
}
initDragAndDrop() {
const items = document.querySelectorAll('.todo-item');
let draggedItem = null;
items.forEach(item => {
item.addEventListener('dragstart', (e) => {
draggedItem = item;
item.classList.add('dragging');
e.dataTransfer.effectAllowed = 'move';
});
item.addEventListener('dragend', () => {
item.classList.remove('dragging');
draggedItem = null;
});
item.addEventListener('dragover', (e) => {
e.preventDefault();
const afterElement = this.getDragAfterElement(e.clientY);
if (afterElement == null) {
item.parentNode.appendChild(draggedItem);
} else {
item.parentNode.insertBefore(draggedItem, afterElement);
}
});
item.addEventListener('drop', (e) => {
e.preventDefault();
this.updateOrder();
});
});
}
getDragAfterElement(y) {
const items = [...document.querySelectorAll('.todo-item:not(.dragging)')];
return items.reduce((closest, child) => {
const box = child.getBoundingClientRect();
const offset = y - box.top - box.height / 2;
if (offset < 0 && offset > closest.offset) {
return { offset: offset, element: child };
} else {
return closest;
}
}, { offset: Number.NEGATIVE_INFINITY }).element;
}
updateOrder() {
const items = document.querySelectorAll('.todo-item');
const newOrder = [];
items.forEach(item => {
const id = parseInt(item.dataset.id);
const task = this.tasks.find(t => t.id === id);
if (task) newOrder.push(task);
});
this.tasks = newOrder;
this.saveTasks();
}
saveTasks() {
localStorage.setItem('todoTasks', JSON.stringify(this.tasks));
}
loadTasks() {
const saved = localStorage.getItem('todoTasks');
return saved ? JSON.parse(saved) : [];
}
getCategoryName(category) {
const names = {
work: '工作',
personal: '个人',
shopping: '购物',
other: '其他'
};
return names[category] || category;
}
formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleDateString('zh-CN', {
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
}
escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
showNotification(message) {
const notification = document.createElement('div');
notification.textContent = message;
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: #4CAF50;
color: white;
padding: 12px 20px;
border-radius: 6px;
z-index: 1000;
animation: slideIn 0.3s ease-out;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
`;
document.body.appendChild(notification);
setTimeout(() => {
notification.style.opacity = '0';
notification.style.transform = 'translateX(100%)';
setTimeout(() => notification.remove(), 300);
}, 2000);
}
}
// 初始化应用
const todoManager = new TodoManager();
</script>
</body>
</html>
7.3 项目三:响应式图片画廊
项目目标: 创建一个支持响应式、懒加载和模态框查看的图片画廊。
技术栈: HTML5 + CSS3 + JavaScript (Intersection Observer API)
核心功能:
- 响应式网格布局
- 图片懒加载
- 点击放大查看
- 键盘导航支持
代码示例:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>响应式图片画廊</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: #f5f5f5;
padding: 20px;
}
.gallery-header {
text-align: center;
margin-bottom: 2rem;
}
.gallery-header h1 {
color: #333;
margin-bottom: 0.5rem;
}
.gallery-header p {
color: #666;
font-size: 1.1rem;
}
.gallery-container {
max-width: 1200px;
margin: 0 auto;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
}
.gallery-item {
position: relative;
aspect-ratio: 4/3;
border-radius: 12px;
overflow: hidden;
cursor: pointer;
background: #e0e0e0;
transition: transform 0.3s, box-shadow 0.3s;
}
.gallery-item:hover {
transform: translateY(-5px);
box-shadow: 0 10px 30px rgba(0,0,0,0.15);
}
.gallery-item img {
width: 100%;
height: 100%;
object-fit: cover;
opacity: 0;
transition: opacity 0.5s;
}
.gallery-item img.loaded {
opacity: 1;
}
.gallery-item .placeholder {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(45deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 200%;
animation: shimmer 1.5s infinite;
}
.gallery-item .overlay {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: linear-gradient(transparent, rgba(0,0,0,0.7));
padding: 1rem;
color: white;
transform: translateY(100%);
transition: transform 0.3s;
}
.gallery-item:hover .overlay {
transform: translateY(0);
}
.overlay h3 {
font-size: 1rem;
margin-bottom: 0.3rem;
}
.overlay p {
font-size: 0.8rem;
opacity: 0.8;
}
/* 模态框 */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.9);
z-index: 1000;
justify-content: center;
align-items: center;
opacity: 0;
transition: opacity 0.3s;
}
.modal.active {
display: flex;
opacity: 1;
}
.modal-content {
position: relative;
max-width: 90%;
max-height: 90%;
animation: zoomIn 0.3s ease-out;
}
.modal-content img {
width: 100%;
height: 100%;
object-fit: contain;
border-radius: 8px;
}
.modal-close {
position: absolute;
top: -40px;
right: 0;
background: none;
border: none;
color: white;
font-size: 2rem;
cursor: pointer;
transition: transform 0.2s;
}
.modal-close:hover {
transform: scale(1.2);
}
.modal-nav {
position: absolute;
top: 50%;
transform: translateY(-50%);
background: rgba(255,255,255,0.2);
border: none;
color: white;
font-size: 2rem;
width: 50px;
height: 50px;
border-radius: 50%;
cursor: pointer;
transition: background 0.3s;
}
.modal-nav:hover {
background: rgba(255,255,255,0.3);
}
.modal-prev {
left: -70px;
}
.modal-next {
right: -70px;
}
.modal-info {
position: absolute;
bottom: -60px;
left: 0;
right: 0;
text-align: center;
color: white;
}
.modal-info h3 {
margin-bottom: 0.3rem;
}
.modal-info p {
opacity: 0.8;
font-size: 0.9rem;
}
/* 动画 */
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
@keyframes zoomIn {
from {
transform: scale(0.8);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
/* 响应式调整 */
@media (max-width: 768px) {
.gallery-container {
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 10px;
}
.modal-nav {
display: none;
}
.modal-close {
top: 20px;
right: 20px;
background: rgba(0,0,0,0.5);
width: 40px;
height: 40px;
border-radius: 50%;
}
.modal-info {
bottom: 10px;
background: rgba(0,0,0,0.5);
padding: 10px;
border-radius: 8px;
}
}
/* 加载更多按钮 */
.load-more {
display: block;
margin: 2rem auto;
padding: 1rem 2rem;
background: #667eea;
color: white;
border: none;
border-radius: 8px;
font-size: 1rem;
cursor: pointer;
transition: background 0.3s;
}
.load-more:hover {
background: #5a6fd8;
}
.load-more:disabled {
background: #ccc;
cursor: not-allowed;
}
</style>
</head>
<body>
<div class="gallery-header">
<h1>响应式图片画廊</h1>
<p>点击图片查看大图,支持键盘导航</p>
</div>
<div class="gallery-container" id="gallery">
<!-- 图片将通过JavaScript动态加载 -->
</div>
<button class="load-more" id="loadMore">加载更多</button>
<!-- 模态框 -->
<div class="modal" id="modal">
<div class="modal-content">
<button class="modal-close" id="modalClose">×</button>
<button class="modal-nav modal-prev" id="modalPrev">‹</button>
<button class="modal-nav modal-next" id="modalNext">›</button>
<img id="modalImage" src="" alt="">
<div class="modal-info">
<h3 id="modalTitle"></h3>
<p id="modalDesc"></p>
</div>
</div>
</div>
<script>
class GalleryManager {
constructor() {
this.images = [];
this.currentIndex = 0;
this.page = 1;
this.isLoading = false;
this.observer = null;
this.init();
}
init() {
this.bindEvents();
this.initIntersectionObserver();
this.loadImages();
}
bindEvents() {
// 加载更多
document.getElementById('loadMore').addEventListener('click', () => {
this.loadImages();
});
// 模态框控制
document.getElementById('modalClose').addEventListener('click', () => this.closeModal());
document.getElementById('modalPrev').addEventListener('click', () => this.navigateModal(-1));
document.getElementById('modalNext').addEventListener('click', () => this.navigateModal(1));
// 点击模态框背景关闭
document.getElementById('modal').addEventListener('click', (e) => {
if (e.target.id === 'modal') this.closeModal();
});
// 键盘导航
document.addEventListener('keydown', (e) => {
if (!document.getElementById('modal').classList.contains('active')) return;
if (e.key === 'Escape') this.closeModal();
if (e.key === 'ArrowLeft') this.navigateModal(-1);
if (e.key === 'ArrowRight') this.navigateModal(1);
});
}
initIntersectionObserver() {
this.observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
const src = img.dataset.src;
if (src) {
img.src = src;
img.onload = () => {
img.classList.add('loaded');
const placeholder = img.previousElementSibling;
if (placeholder) placeholder.remove();
};
this.observer.unobserve(img);
}
}
});
}, {
rootMargin: '50px',
threshold: 0.1
});
}
async loadImages() {
if (this.isLoading) return;
this.isLoading = true;
const loadMoreBtn = document.getElementById('loadMore');
loadMoreBtn.textContent = '加载中...';
loadMoreBtn.disabled = true;
try {
// 模拟API请求
const newImages = await this.fetchImages(this.page);
this.images = [...this.images, ...newImages];
this.renderImages(newImages);
this.page++;
// 如果加载了所有图片,隐藏加载按钮
if (this.page > 5) {
loadMoreBtn.style.display = 'none';
}
} catch (error) {
console.error('加载图片失败:', error);
this.showNotification('加载失败,请重试');
} finally {
this.isLoading = false;
loadMoreBtn.textContent = '加载更多';
loadMoreBtn.disabled = false;
}
}
async fetchImages(page) {
// 模拟API延迟
await new Promise(resolve => setTimeout(resolve, 800));
// 生成模拟数据
const categories = ['自然', '城市', '人物', '抽象', '动物'];
const images = [];
for (let i = 0; i < 12; i++) {
const id = (page - 1) * 12 + i + 1;
const width = 400 + Math.floor(Math.random() * 200);
const height = 300 + Math.floor(Math.random() * 150);
const category = categories[Math.floor(Math.random() * categories.length)];
images.push({
id: id,
title: `图片 ${id}`,
description: `${category} - 第${page}页`,
category: category,
// 使用picsum.photos作为示例图片源
thumbnail: `https://picsum.photos/${width}/${height}?random=${id}`,
full: `https://picsum.photos/1200/800?random=${id}`
});
}
return images;
}
renderImages(newImages) {
const gallery = document.getElementById('gallery');
newImages.forEach((image, index) => {
const item = document.createElement('div');
item.className = 'gallery-item';
item.dataset.index = this.images.length - newImages.length + index;
item.innerHTML = `
<div class="placeholder"></div>
<img data-src="${image.thumbnail}" alt="${image.title}">
<div class="overlay">
<h3>${image.title}</h3>
<p>${image.description}</p>
</div>
`;
gallery.appendChild(item);
// 观察图片实现懒加载
const img = item.querySelector('img');
this.observer.observe(img);
// 点击事件
item.addEventListener('click', () => {
this.openModal(parseInt(item.dataset.index));
});
});
}
openModal(index) {
this.currentIndex = index;
const image = this.images[index];
const modal = document.getElementById('modal');
const modalImage = document.getElementById('modalImage');
const modalTitle = document.getElementById('modalTitle');
const modalDesc = document.getElementById('modalDesc');
modalImage.src = image.full;
modalImage.alt = image.title;
modalTitle.textContent = image.title;
modalDesc.textContent = image.description;
modal.classList.add('active');
document.body.style.overflow = 'hidden';
}
closeModal() {
const modal = document.getElementById('modal');
modal.classList.remove('active');
document.body.style.overflow = '';
}
navigateModal(direction) {
const newIndex = this.currentIndex + direction;
if (newIndex >= 0 && newIndex < this.images.length) {
this.currentIndex = newIndex;
const image = this.images[newIndex];
const modalImage = document.getElementById('modalImage');
const modalTitle = document.getElementById('modalTitle');
const modalDesc = document.getElementById('modalDesc');
// 添加淡入淡出效果
modalImage.style.opacity = '0';
setTimeout(() => {
modalImage.src = image.full;
modalImage.alt = image.title;
modalTitle.textContent = image.title;
modalDesc.textContent = image.description;
modalImage.style.opacity = '1';
}, 150);
}
}
showNotification(message) {
const notification = document.createElement('div');
notification.textContent = message;
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: #e74c3c;
color: white;
padding: 12px 20px;
border-radius: 6px;
z-index: 1001;
animation: slideIn 0.3s ease-out;
`;
document.body.appendChild(notification);
setTimeout(() => notification.remove(), 3000);
}
}
// 初始化画廊
const galleryManager = new GalleryManager();
</script>
</body>
</html>
第八部分:学习资源与进阶路径
8.1 推荐学习资源
在线教程:
- MDN Web Docs(最权威的Web技术文档)
- freeCodeCamp(免费的交互式编程课程)
- W3Schools(基础语法参考)
视频课程:
- Udemy上的”Complete Web Development Bootcamp”
- Coursera的”Web Design for Everybody”
- YouTube频道:Traversy Media, The Net Ninja
书籍推荐:
- 《HTML5与CSS3权威指南》
- 《JavaScript高级程序设计》
- 《深入浅出React和Redux》
8.2 进阶学习路径
框架学习:
- React/Vue/Angular 选择一个深入学习
- 状态管理(Redux/Vuex)
- 路由管理(React Router/Vue Router)
性能优化:
- 代码分割与懒加载
- 图片优化与WebP格式
- 缓存策略与Service Worker
TypeScript:
- 类型系统基础
- 接口与泛型
- 在React/Vue中使用TypeScript
测试:
- 单元测试(Jest/Vitest)
- 端到端测试(Cypress/Playwright)
- 测试驱动开发(TDD)
8.3 实战项目建议
初级项目:
- 个人简历页面
- 产品展示网站
- 简单的计算器
中级项目:
- 电商网站前端
- 社交媒体应用
- 实时聊天应用
高级项目:
- 在线编辑器(如Markdown编辑器)
- 数据可视化仪表盘
- 渐进式Web应用(PWA)
结语
掌握HTML5前端开发核心技能需要系统的学习和大量的实践。从基础语法到高级API,从CSS布局到JavaScript编程,每一步都需要扎实掌握。通过实战项目,你可以将理论知识转化为实际能力,解决真实世界的问题。
记住,前端开发是一个快速发展的领域,持续学习和实践是保持竞争力的关键。希望这份全攻略能为你的学习之路提供清晰的指引,祝你在前端开发的道路上取得成功!
