HTML5新特性
HTML5是HTML的最新版本,引入了许多新特性和API,极大地增强了Web应用的功能和用户体验。掌握这些新特性对于现代Web开发至关重要。
HTML5新增语义标签
HTML5引入了更多语义化标签,使网页结构更加清晰和有意义。
新增的语义化标签
html
<!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>
<header>
<h1>网站标题</h1>
<nav>
<ul>
<li><a href="/">首页</a></li>
<li><a href="/about">关于</a></li>
<li><a href="/contact">联系</a></li>
</ul>
</nav>
</header>
<main>
<article>
<header>
<h2>文章标题</h2>
<p>
作者:<address>张三</address>
发布时间:<time datetime="2023-01-01">2023年1月1日</time>
</p>
</header>
<p>文章内容...</p>
<section>
<h3>章节标题</h3>
<p>章节内容...</p>
</section>
<footer>
<p>标签:<mark>HTML5</mark> <mark>语义化</mark></p>
</footer>
</article>
<aside>
<h3>侧边栏</h3>
<p>相关链接或补充信息...</p>
</aside>
</main>
<footer>
<p>© 2023 版权所有</p>
</footer>
</body>
</html>标签详解
<main>标签
表示文档的主要内容,每个页面应该只有一个<main>标签:
html
<main>
<h1>主要内容</h1>
<p>这是页面的主要内容...</p>
</main><article>标签
表示独立的内容,如文章、博客帖子、新闻等:
html
<article>
<h2>文章标题</h2>
<p>文章内容...</p>
</article><section>标签
表示文档中的一个区段或章节:
html
<section>
<h2>第一章节</h2>
<p>章节内容...</p>
</section><aside>标签
表示与主要内容相关但可以独立存在的内容,如侧边栏、广告等:
html
<aside>
<h3>相关文章</h3>
<ul>
<li><a href="#">相关文章1</a></li>
<li><a href="#">相关文章2</a></li>
</ul>
</aside><figure>和<figcaption>标签
用于包含媒体内容及其说明:
html
<figure>
<img src="image.jpg" alt="描述图片">
<figcaption>图片说明文字</figcaption>
</figure><mark>标签
用于突出显示文本:
html
<p>请特别注意<mark>这部分内容</mark>。</p><time>标签
用于表示日期和时间:
html
<p>会议时间:<time datetime="2023-12-25T10:00">2023年12月25日上午10点</time></p>标签的正确嵌套关系
html
<!-- 推荐的嵌套结构 -->
<body>
<header>...</header>
<main>
<article>
<header>...</header>
<section>...</section>
<footer>...</footer>
</article>
<aside>...</aside>
</main>
<footer>...</footer>
</body>增强型表单
HTML5为表单引入了新的输入类型和属性,提供了更好的用户体验和数据验证。
新增input类型
html
<form>
<!-- 邮箱输入 -->
<label for="email">邮箱:</label>
<input type="email" id="email" name="email" required>
<!-- 网址输入 -->
<label for="website">个人网站:</label>
<input type="url" id="website" name="website">
<!-- 数字输入 -->
<label for="age">年龄:</label>
<input type="number" id="age" name="age" min="1" max="120">
<!-- 范围选择 -->
<label for="satisfaction">满意度:</label>
<input type="range" id="satisfaction" name="satisfaction" min="0" max="100" value="50">
<!-- 颜色选择 -->
<label for="color">喜欢的颜色:</label>
<input type="color" id="color" name="color" value="#ff0000">
<!-- 日期选择 -->
<label for="birthdate">出生日期:</label>
<input type="date" id="birthdate" name="birthdate">
<!-- 时间选择 -->
<label for="meeting-time">会议时间:</label>
<input type="time" id="meeting-time" name="meeting-time">
<!-- 日期时间选择 -->
<label for="appointment">预约时间:</label>
<input type="datetime-local" id="appointment" name="appointment">
<!-- 月份选择 -->
<label for="birth-month">出生月份:</label>
<input type="month" id="birth-month" name="birth-month">
<!-- 星期选择 -->
<label for="week">选择周:</label>
<input type="week" id="week" name="week">
<!-- 搜索框 -->
<label for="search">搜索:</label>
<input type="search" id="search" name="search">
<!-- 电话号码 -->
<label for="phone">电话:</label>
<input type="tel" id="phone" name="phone" pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}">
<!-- 文件上传(支持多文件) -->
<label for="files">选择文件:</label>
<input type="file" id="files" name="files" multiple>
</form>表单新属性
html
<form>
<!-- 占位符文本 -->
<input type="text" name="username" placeholder="请输入用户名">
<!-- 自动完成 -->
<input type="email" name="email" autocomplete="email">
<!-- 自动聚焦 -->
<input type="text" name="search" autofocus>
<!-- 必填字段 -->
<input type="text" name="required-field" required>
<!-- 最小长度 -->
<input type="text" name="min-length" minlength="3">
<!-- 最大长度 -->
<input type="text" name="max-length" maxlength="20">
<!-- 正则表达式验证 -->
<input type="text" name="pattern" pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}"
title="请输入正确的电话号码格式:123-456-7890">
<!-- 只读 -->
<input type="text" name="readonly" value="只读内容" readonly>
<!-- 禁用 -->
<input type="text" name="disabled" value="禁用内容" disabled>
<!-- 多个验证 -->
<input type="email" name="email" required multiple>
</form>表单验证API基础
html
<!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>
.error {
color: red;
font-size: 0.9em;
}
.valid {
border-color: green;
}
.invalid {
border-color: red;
}
</style>
</head>
<body>
<form id="validation-form">
<div>
<label for="username">用户名(3-20个字符):</label>
<input type="text" id="username" name="username" required minlength="3" maxlength="20">
<div id="username-error" class="error"></div>
</div>
<div>
<label for="email">邮箱:</label>
<input type="email" id="email" name="email" required>
<div id="email-error" class="error"></div>
</div>
<div>
<label for="password">密码(至少8个字符):</label>
<input type="password" id="password" name="password" required minlength="8">
<div id="password-error" class="error"></div>
</div>
<div>
<label for="confirm-password">确认密码:</label>
<input type="password" id="confirm-password" name="confirm-password" required>
<div id="confirm-password-error" class="error"></div>
</div>
<button type="submit">注册</button>
</form>
<script>
const form = document.getElementById('validation-form');
const username = document.getElementById('username');
const email = document.getElementById('email');
const password = document.getElementById('password');
const confirmPassword = document.getElementById('confirm-password');
// 实时验证用户名
username.addEventListener('input', function() {
if (this.validity.valid) {
this.classList.remove('invalid');
this.classList.add('valid');
document.getElementById('username-error').textContent = '';
} else {
this.classList.remove('valid');
this.classList.add('invalid');
if (this.validity.tooShort) {
document.getElementById('username-error').textContent = '用户名至少需要3个字符';
} else if (this.validity.tooLong) {
document.getElementById('username-error').textContent = '用户名不能超过20个字符';
}
}
});
// 实时验证密码确认
confirmPassword.addEventListener('input', function() {
if (this.value !== password.value) {
this.classList.remove('valid');
this.classList.add('invalid');
document.getElementById('confirm-password-error').textContent = '密码不匹配';
} else {
this.classList.remove('invalid');
this.classList.add('valid');
document.getElementById('confirm-password-error').textContent = '';
}
});
// 表单提交验证
form.addEventListener('submit', function(e) {
if (!form.checkValidity()) {
e.preventDefault();
alert('请检查表单中的错误');
}
});
</script>
</body>
</html>绘图与图形
HTML5引入了<canvas>元素,为Web提供了强大的2D绘图能力。
Canvas基础用法
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Canvas绘图示例</title>
</head>
<body>
<h1>Canvas绘图示例</h1>
<canvas id="myCanvas" width="400" height="300" style="border: 1px solid #000;">
您的浏览器不支持Canvas元素。
</canvas>
<script>
// 获取canvas元素和绘图上下文
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// 绘制矩形
ctx.fillStyle = 'blue';
ctx.fillRect(10, 10, 100, 50);
// 绘制边框矩形
ctx.strokeStyle = 'red';
ctx.lineWidth = 2;
ctx.strokeRect(130, 10, 100, 50);
// 绘制透明矩形
ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
ctx.fillRect(250, 10, 100, 50);
// 绘制线条
ctx.beginPath();
ctx.moveTo(50, 100);
ctx.lineTo(150, 150);
ctx.lineTo(250, 100);
ctx.strokeStyle = 'purple';
ctx.lineWidth = 3;
ctx.stroke();
// 绘制圆形
ctx.beginPath();
ctx.arc(100, 200, 30, 0, 2 * Math.PI);
ctx.fillStyle = 'yellow';
ctx.fill();
ctx.strokeStyle = 'orange';
ctx.lineWidth = 2;
ctx.stroke();
// 绘制文字
ctx.font = '20px Arial';
ctx.fillStyle = 'black';
ctx.fillText('Hello Canvas!', 200, 200);
// 绘制描边文字
ctx.font = '20px Arial';
ctx.strokeStyle = 'blue';
ctx.lineWidth = 1;
ctx.strokeText('Stroke Text', 200, 230);
</script>
</body>
</html>简单图形绘制
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Canvas图形绘制</title>
</head>
<body>
<h1>Canvas图形绘制</h1>
<canvas id="shapesCanvas" width="500" height="400" style="border: 1px solid #000;"></canvas>
<script>
const canvas = document.getElementById('shapesCanvas');
const ctx = canvas.getContext('2d');
// 绘制三角形
ctx.beginPath();
ctx.moveTo(50, 50);
ctx.lineTo(150, 50);
ctx.lineTo(100, 150);
ctx.closePath();
ctx.fillStyle = 'red';
ctx.fill();
// 绘制多边形
function drawPolygon(x, y, radius, sides) {
ctx.beginPath();
for (let i = 0; i < sides; i++) {
const angle = (i * 2 * Math.PI / sides) - Math.PI / 2;
const px = x + radius * Math.cos(angle);
const py = y + radius * Math.sin(angle);
if (i === 0) {
ctx.moveTo(px, py);
} else {
ctx.lineTo(px, py);
}
}
ctx.closePath();
ctx.stroke();
}
ctx.strokeStyle = 'blue';
drawPolygon(250, 100, 50, 5); // 五边形
drawPolygon(400, 100, 50, 6); // 六边形
// 绘制渐变
const gradient = ctx.createLinearGradient(0, 0, 200, 0);
gradient.addColorStop(0, 'red');
gradient.addColorStop(0.5, 'yellow');
gradient.addColorStop(1, 'blue');
ctx.fillStyle = gradient;
ctx.fillRect(50, 200, 200, 50);
// 绘制径向渐变
const radialGradient = ctx.createRadialGradient(350, 225, 10, 350, 225, 50);
radialGradient.addColorStop(0, 'white');
radialGradient.addColorStop(1, 'black');
ctx.fillStyle = radialGradient;
ctx.fillRect(300, 200, 100, 50);
// 绘制图像
const img = new Image();
img.onload = function() {
ctx.drawImage(img, 50, 300, 100, 100);
};
img.src = 'https://via.placeholder.com/100';
</script>
</body>
</html>存储与离线
HTML5提供了客户端存储解决方案,使Web应用能够在用户浏览器中存储数据。
localStorage与sessionStorage基础
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Web存储示例</title>
</head>
<body>
<h1>Web存储示例</h1>
<div>
<h2>localStorage示例</h2>
<input type="text" id="local-input" placeholder="输入要存储的内容">
<button onclick="saveToLocalStorage()">保存到localStorage</button>
<button onclick="getFromLocalStorage()">从localStorage读取</button>
<button onclick="clearLocalStorage()">清空localStorage</button>
<p id="local-result"></p>
</div>
<div>
<h2>sessionStorage示例</h2>
<input type="text" id="session-input" placeholder="输入要存储的内容">
<button onclick="saveToSessionStorage()">保存到sessionStorage</button>
<button onclick="getFromSessionStorage()">从sessionStorage读取</button>
<button onclick="clearSessionStorage()">清空sessionStorage</button>
<p id="session-result"></p>
</div>
<div>
<h2>存储信息</h2>
<p>localStorage大小:<span id="local-size">0</span> 字节</p>
<p>sessionStorage大小:<span id="session-size">0</span> 字节</p>
<button onclick="showStorageInfo()">显示存储信息</button>
</div>
<script>
// localStorage操作
function saveToLocalStorage() {
const value = document.getElementById('local-input').value;
localStorage.setItem('myKey', value);
alert('数据已保存到localStorage');
}
function getFromLocalStorage() {
const value = localStorage.getItem('myKey');
document.getElementById('local-result').textContent = value || '未找到数据';
}
function clearLocalStorage() {
localStorage.removeItem('myKey');
document.getElementById('local-result').textContent = '数据已清空';
}
// sessionStorage操作
function saveToSessionStorage() {
const value = document.getElementById('session-input').value;
sessionStorage.setItem('myKey', value);
alert('数据已保存到sessionStorage');
}
function getFromSessionStorage() {
const value = sessionStorage.getItem('myKey');
document.getElementById('session-result').textContent = value || '未找到数据';
}
function clearSessionStorage() {
sessionStorage.removeItem('myKey');
document.getElementById('session-result').textContent = '数据已清空';
}
// 计算存储大小
function calculateStorageSize(storage) {
let size = 0;
for (let key in storage) {
if (storage.hasOwnProperty(key)) {
size += key.length + storage[key].length;
}
}
return size;
}
function showStorageInfo() {
document.getElementById('local-size').textContent = calculateStorageSize(localStorage);
document.getElementById('session-size').textContent = calculateStorageSize(sessionStorage);
}
// 页面加载时显示存储信息
window.onload = function() {
showStorageInfo();
};
</script>
</body>
</html>存储对象和数组
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>复杂数据存储</title>
</head>
<body>
<h1>复杂数据存储示例</h1>
<div>
<h2>用户信息管理</h2>
<form id="user-form">
<input type="text" id="user-name" placeholder="姓名" required>
<input type="email" id="user-email" placeholder="邮箱" required>
<input type="number" id="user-age" placeholder="年龄" required>
<button type="submit">添加用户</button>
</form>
<h3>用户列表</h3>
<ul id="user-list"></ul>
<button onclick="clearUsers()">清空所有用户</button>
</div>
<script>
// 用户数据结构
class User {
constructor(name, email, age) {
this.id = Date.now(); // 简单的ID生成
this.name = name;
this.email = email;
this.age = age;
this.createdAt = new Date().toISOString();
}
}
// 用户管理类
class UserManager {
constructor() {
this.users = this.loadUsers();
this.renderUsers();
}
// 从localStorage加载用户
loadUsers() {
const usersData = localStorage.getItem('users');
return usersData ? JSON.parse(usersData) : [];
}
// 保存用户到localStorage
saveUsers() {
localStorage.setItem('users', JSON.stringify(this.users));
}
// 添加用户
addUser(name, email, age) {
const user = new User(name, email, age);
this.users.push(user);
this.saveUsers();
this.renderUsers();
}
// 删除用户
removeUser(id) {
this.users = this.users.filter(user => user.id !== id);
this.saveUsers();
this.renderUsers();
}
// 清空所有用户
clearUsers() {
this.users = [];
this.saveUsers();
this.renderUsers();
}
// 渲染用户列表
renderUsers() {
const userList = document.getElementById('user-list');
userList.innerHTML = '';
this.users.forEach(user => {
const li = document.createElement('li');
li.innerHTML = `
<strong>${user.name}</strong>
(${user.email}, ${user.age}岁)
<small>创建于: ${new Date(user.createdAt).toLocaleString()}</small>
<button onclick="userManager.removeUser(${user.id})">删除</button>
`;
userList.appendChild(li);
});
}
}
// 创建用户管理实例
const userManager = new UserManager();
// 表单提交处理
document.getElementById('user-form').addEventListener('submit', function(e) {
e.preventDefault();
const name = document.getElementById('user-name').value;
const email = document.getElementById('user-email').value;
const age = parseInt(document.getElementById('user-age').value);
userManager.addUser(name, email, age);
// 清空表单
this.reset();
});
// 清空所有用户
function clearUsers() {
if (confirm('确定要清空所有用户吗?')) {
userManager.clearUsers();
}
}
</script>
</body>
</html>其他新特性
地理定位(Geolocation API)基础
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>地理定位示例</title>
</head>
<body>
<h1>地理定位示例</h1>
<button onclick="getLocation()">获取当前位置</button>
<button onclick="watchLocation()">持续监听位置</button>
<button onclick="stopWatching()">停止监听</button>
<div id="location-info"></div>
<div id="map"></div>
<script>
let watchId;
// 检查浏览器是否支持地理定位
if (!navigator.geolocation) {
document.getElementById('location-info').innerHTML = '您的浏览器不支持地理定位';
}
// 获取当前位置
function getLocation() {
const infoDiv = document.getElementById('location-info');
infoDiv.innerHTML = '正在获取位置信息...';
navigator.geolocation.getCurrentPosition(
// 成功回调
function(position) {
const latitude = position.coords.latitude;
const longitude = position.coords.longitude;
const accuracy = position.coords.accuracy;
infoDiv.innerHTML = `
<h3>位置信息</h3>
<p>纬度: ${latitude.toFixed(6)}</p>
<p>经度: ${longitude.toFixed(6)}</p>
<p>精度: ${accuracy} 米</p>
<p>获取时间: ${new Date(position.timestamp).toLocaleString()}</p>
`;
// 显示地图
showMap(latitude, longitude);
},
// 错误回调
function(error) {
switch(error.code) {
case error.PERMISSION_DENIED:
infoDiv.innerHTML = '用户拒绝了地理定位请求';
break;
case error.POSITION_UNAVAILABLE:
infoDiv.innerHTML = '位置信息不可用';
break;
case error.TIMEOUT:
infoDiv.innerHTML = '获取位置信息超时';
break;
case error.UNKNOWN_ERROR:
infoDiv.innerHTML = '获取位置时发生未知错误';
break;
}
},
// 选项
{
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 60000
}
);
}
// 持续监听位置
function watchLocation() {
const infoDiv = document.getElementById('location-info');
infoDiv.innerHTML = '正在监听位置变化...';
watchId = navigator.geolocation.watchPosition(
function(position) {
const latitude = position.coords.latitude;
const longitude = position.coords.longitude;
const accuracy = position.coords.accuracy;
infoDiv.innerHTML = `
<h3>实时位置信息</h3>
<p>纬度: ${latitude.toFixed(6)}</p>
<p>经度: ${longitude.toFixed(6)}</p>
<p>精度: ${accuracy} 米</p>
<p>更新时间: ${new Date(position.timestamp).toLocaleString()}</p>
`;
showMap(latitude, longitude);
},
function(error) {
infoDiv.innerHTML = '监听位置时发生错误: ' + error.message;
},
{
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 60000
}
);
}
// 停止监听位置
function stopWatching() {
if (watchId) {
navigator.geolocation.clearWatch(watchId);
document.getElementById('location-info').innerHTML = '已停止位置监听';
}
}
// 显示地图(使用OpenStreetMap)
function showMap(lat, lng) {
const mapDiv = document.getElementById('map');
mapDiv.innerHTML = `
<h3>位置地图</h3>
<iframe width="400" height="300" frameborder="0"
src="https://www.openstreetmap.org/export/embed.html?bbox=${lng-0.01},${lat-0.01},${lng+0.01},${lat+0.01}&layer=mapnik&marker=${lat},${lng}">
</iframe>
`;
}
</script>
</body>
</html>拖放API(Drag & Drop)基础
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>拖放API示例</title>
<style>
.draggable {
width: 100px;
height: 100px;
background-color: #4CAF50;
margin: 10px;
cursor: move;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
}
.drop-zone {
width: 300px;
height: 200px;
border: 2px dashed #ccc;
margin: 20px 0;
display: flex;
align-items: center;
justify-content: center;
background-color: #f9f9f9;
}
.drop-zone.drag-over {
border-color: #4CAF50;
background-color: #e8f5e8;
}
.dropped {
background-color: #2196F3;
}
</style>
</head>
<body>
<h1>拖放API示例</h1>
<div>
<h2>可拖拽元素</h2>
<div class="draggable" draggable="true" data-item="item1">项目 1</div>
<div class="draggable" draggable="true" data-item="item2">项目 2</div>
<div class="draggable" draggable="true" data-item="item3">项目 3</div>
</div>
<div>
<h2>放置区域</h2>
<div id="drop-zone1" class="drop-zone">放置区域 1</div>
<div id="drop-zone2" class="drop-zone">放置区域 2</div>
</div>
<div>
<h2>拖放事件日志</h2>
<ul id="event-log"></ul>
</div>
<script>
// 添加拖拽事件监听器
document.querySelectorAll('.draggable').forEach(item => {
item.addEventListener('dragstart', dragStart);
item.addEventListener('dragend', dragEnd);
});
// 添加放置区域事件监听器
document.querySelectorAll('.drop-zone').forEach(zone => {
zone.addEventListener('dragover', dragOver);
zone.addEventListener('dragenter', dragEnter);
zone.addEventListener('dragleave', dragLeave);
zone.addEventListener('drop', drop);
});
// 拖拽开始事件
function dragStart(e) {
console.log('拖拽开始:', e.target.dataset.item);
logEvent(`开始拖拽: ${e.target.dataset.item}`);
// 设置拖拽数据
e.dataTransfer.setData('text/plain', e.target.dataset.item);
e.dataTransfer.effectAllowed = 'move';
// 添加视觉反馈
setTimeout(() => {
e.target.classList.add('dragging');
}, 0);
}
// 拖拽结束事件
function dragEnd(e) {
console.log('拖拽结束');
logEvent('拖拽结束');
e.target.classList.remove('dragging');
}
// 拖拽进入放置区域
function dragEnter(e) {
e.preventDefault();
console.log('拖拽进入放置区域');
logEvent('拖拽进入放置区域');
e.target.classList.add('drag-over');
}
// 拖拽在放置区域上
function dragOver(e) {
e.preventDefault();
e.dataTransfer.dropEffect = 'move';
}
// 拖拽离开放置区域
function dragLeave(e) {
console.log('拖拽离开放置区域');
logEvent('拖拽离开放置区域');
e.target.classList.remove('drag-over');
}
// 放置事件
function drop(e) {
e.preventDefault();
e.target.classList.remove('drag-over');
// 获取拖拽的数据
const data = e.dataTransfer.getData('text/plain');
console.log('放置:', data);
logEvent(`放置: ${data} 到 ${e.target.id}`);
// 创建新的可拖拽元素
const newItem = document.createElement('div');
newItem.className = 'draggable dropped';
newItem.draggable = true;
newItem.dataset.item = data;
newItem.textContent = `已放置: ${data}`;
// 添加拖拽事件
newItem.addEventListener('dragstart', dragStart);
newItem.addEventListener('dragend', dragEnd);
// 添加到放置区域
e.target.appendChild(newItem);
}
// 记录事件日志
function logEvent(message) {
const logList = document.getElementById('event-log');
const listItem = document.createElement('li');
listItem.textContent = `${new Date().toLocaleTimeString()}: ${message}`;
logList.appendChild(listItem);
// 保持最多20条记录
if (logList.children.length > 20) {
logList.removeChild(logList.firstChild);
}
}
</script>
</body>
</html>实践项目
开发一个HTML5特性展示页面
html
<!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>
<style>
body {
font-family: Arial, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 2rem;
text-align: center;
border-radius: 10px;
margin-bottom: 2rem;
}
nav {
background-color: #333;
padding: 1rem;
border-radius: 5px;
margin-bottom: 2rem;
}
nav ul {
list-style: none;
display: flex;
justify-content: center;
gap: 2rem;
}
nav a {
color: white;
text-decoration: none;
padding: 0.5rem 1rem;
border-radius: 5px;
transition: background-color 0.3s;
}
nav a:hover {
background-color: #555;
}
section {
background-color: #f9f9f9;
padding: 2rem;
margin-bottom: 2rem;
border-radius: 10px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.feature-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
margin-top: 1rem;
}
.feature-card {
background: white;
padding: 1.5rem;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.feature-card h3 {
color: #333;
margin-top: 0;
}
canvas {
border: 1px solid #ddd;
border-radius: 5px;
}
footer {
text-align: center;
padding: 2rem;
background-color: #333;
color: white;
border-radius: 10px;
}
</style>
</head>
<body>
<header>
<h1>HTML5新特性展示</h1>
<p>探索HTML5带来的强大功能</p>
</header>
<nav>
<ul>
<li><a href="#semantic">语义化标签</a></li>
<li><a href="#forms">增强表单</a></li>
<li><a href="#canvas">Canvas绘图</a></li>
<li><a href="#storage">Web存储</a></li>
<li><a href="#geolocation">地理定位</a></li>
</ul>
</nav>
<main>
<section id="semantic">
<h2>语义化标签</h2>
<p>HTML5引入了更多语义化标签,使网页结构更加清晰。</p>
<div class="feature-grid">
<article class="feature-card">
<header>
<h3>文章标题</h3>
<time datetime="2023-01-01">2023年1月1日</time>
</header>
<p>这是一篇示例文章,展示了<article>标签的使用。</p>
<footer>
<p>作者:<address>张三</address></p>
</footer>
</article>
<aside class="feature-card">
<h3>侧边栏</h3>
<p>这是侧边栏内容,使用<code><aside></code>标签。</p>
<nav>
<ul>
<li><a href="#">相关链接1</a></li>
<li><a href="#">相关链接2</a></li>
</ul>
</nav>
</aside>
</div>
</section>
<section id="forms">
<h2>增强表单</h2>
<p>HTML5为表单提供了新的输入类型和验证功能。</p>
<form class="feature-card">
<div>
<label for="email">邮箱:</label>
<input type="email" id="email" name="email" required>
</div>
<div>
<label for="website">网站:</label>
<input type="url" id="website" name="website" placeholder="https://example.com">
</div>
<div>
<label for="number">数字(1-100):</label>
<input type="number" id="number" name="number" min="1" max="100">
</div>
<div>
<label for="range">范围选择:</label>
<input type="range" id="range" name="range" min="0" max="100" value="50">
</div>
<div>
<label for="color">颜色选择:</label>
<input type="color" id="color" name="color" value="#ff0000">
</div>
<div>
<label for="date">日期选择:</label>
<input type="date" id="date" name="date">
</div>
<button type="submit">提交</button>
</form>
</section>
<section id="canvas">
<h2>Canvas绘图</h2>
<p>使用HTML5 Canvas进行2D图形绘制。</p>
<div class="feature-card">
<canvas id="demoCanvas" width="400" height="200"></canvas>
</div>
</section>
<section id="storage">
<h2>Web存储</h2>
<p>客户端存储数据,无需服务器交互。</p>
<div class="feature-card">
<div>
<input type="text" id="storage-input" placeholder="输入要存储的内容">
<button onclick="saveToStorage()">保存</button>
<button onclick="loadFromStorage()">读取</button>
<button onclick="clearStorage()">清空</button>
</div>
<p id="storage-output">存储内容将显示在这里</p>
</div>
</section>
<section id="geolocation">
<h2>地理定位</h2>
<p>获取用户的地理位置信息。</p>
<div class="feature-card">
<button onclick="getLocation()">获取当前位置</button>
<p id="location-output">位置信息将显示在这里</p>
</div>
</section>
</main>
<footer>
<p>© 2023 HTML5特性展示. 使用HTML5语义化标签构建.</p>
</footer>
<script>
// Canvas绘图
function drawOnCanvas() {
const canvas = document.getElementById('demoCanvas');
const ctx = canvas.getContext('2d');
// 绘制渐变背景
const gradient = ctx.createLinearGradient(0, 0, 400, 0);
gradient.addColorStop(0, '#ff6b6b');
gradient.addColorStop(0.5, '#4ecdc4');
gradient.addColorStop(1, '#45b7d1');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 400, 200);
// 绘制文字
ctx.font = 'bold 24px Arial';
ctx.fillStyle = 'white';
ctx.textAlign = 'center';
ctx.fillText('HTML5 Canvas', 200, 50);
// 绘制几何图形
ctx.beginPath();
ctx.arc(100, 120, 30, 0, 2 * Math.PI);
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
ctx.fill();
ctx.beginPath();
ctx.moveTo(200, 100);
ctx.lineTo(250, 150);
ctx.lineTo(150, 150);
ctx.closePath();
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
ctx.fill();
// 绘制矩形
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
ctx.fillRect(300, 100, 50, 50);
}
// Web存储功能
function saveToStorage() {
const value = document.getElementById('storage-input').value;
localStorage.setItem('demo-storage', value);
alert('数据已保存');
}
function loadFromStorage() {
const value = localStorage.getItem('demo-storage');
document.getElementById('storage-output').textContent = value || '未找到存储的数据';
}
function clearStorage() {
localStorage.removeItem('demo-storage');
document.getElementById('storage-output').textContent = '存储已清空';
}
// 地理定位功能
function getLocation() {
const output = document.getElementById('location-output');
output.textContent = '正在获取位置信息...';
if (!navigator.geolocation) {
output.textContent = '您的浏览器不支持地理定位';
return;
}
navigator.geolocation.getCurrentPosition(
function(position) {
output.innerHTML = `
纬度: ${position.coords.latitude.toFixed(6)}<br>
经度: ${position.coords.longitude.toFixed(6)}<br>
精度: ${position.coords.accuracy} 米
`;
},
function(error) {
output.textContent = '获取位置信息失败: ' + error.message;
}
);
}
// 页面加载完成后执行
window.onload = function() {
drawOnCanvas();
};
</script>
</body>
</html>制作一个带本地存储功能的简单待办事项列表
html
<!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>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 600px;
margin: 0 auto;
background: white;
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
overflow: hidden;
}
header {
background: #333;
color: white;
padding: 1.5rem;
text-align: center;
}
.app-title {
font-size: 1.8rem;
margin-bottom: 0.5rem;
}
.app-subtitle {
font-size: 1rem;
opacity: 0.8;
}
.input-section {
padding: 1.5rem;
border-bottom: 1px solid #eee;
}
.input-group {
display: flex;
gap: 10px;
}
#todo-input {
flex: 1;
padding: 12px;
border: 2px solid #ddd;
border-radius: 8px;
font-size: 1rem;
transition: border-color 0.3s;
}
#todo-input:focus {
outline: none;
border-color: #667eea;
}
#add-btn {
padding: 12px 20px;
background: #667eea;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 1rem;
transition: background 0.3s;
}
#add-btn:hover {
background: #5a6fd8;
}
.stats {
padding: 1rem 1.5rem;
background: #f8f9fa;
border-bottom: 1px solid #eee;
display: flex;
justify-content: space-between;
font-size: 0.9rem;
color: #666;
}
.filter-buttons {
display: flex;
gap: 10px;
padding: 1rem 1.5rem;
background: #f8f9fa;
border-bottom: 1px solid #eee;
}
.filter-btn {
padding: 6px 12px;
background: white;
border: 1px solid #ddd;
border-radius: 15px;
cursor: pointer;
font-size: 0.9rem;
transition: all 0.3s;
}
.filter-btn.active {
background: #667eea;
color: white;
border-color: #667eea;
}
.filter-btn:hover:not(.active) {
background: #e9ecef;
}
.todo-list {
list-style: none;
max-height: 400px;
overflow-y: auto;
}
.todo-item {
display: flex;
align-items: center;
padding: 1rem 1.5rem;
border-bottom: 1px solid #eee;
transition: background 0.3s;
}
.todo-item:hover {
background: #f8f9fa;
}
.todo-item.completed {
opacity: 0.7;
}
.todo-checkbox {
margin-right: 15px;
width: 20px;
height: 20px;
cursor: pointer;
}
.todo-text {
flex: 1;
font-size: 1.1rem;
}
.todo-item.completed .todo-text {
text-decoration: line-through;
}
.todo-date {
font-size: 0.8rem;
color: #999;
margin-left: 10px;
}
.todo-actions {
display: flex;
gap: 10px;
margin-left: 15px;
}
.edit-btn, .delete-btn {
padding: 5px 10px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 0.8rem;
}
.edit-btn {
background: #28a745;
color: white;
}
.delete-btn {
background: #dc3545;
color: white;
}
.edit-btn:hover {
background: #218838;
}
.delete-btn:hover {
background: #c82333;
}
.empty-state {
text-align: center;
padding: 3rem;
color: #999;
}
.empty-state p {
margin-top: 1rem;
}
.clear-completed {
padding: 1rem 1.5rem;
text-align: center;
background: #f8f9fa;
}
#clear-btn {
padding: 8px 16px;
background: #6c757d;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 0.9rem;
}
#clear-btn:hover {
background: #5a6268;
}
.notification {
position: fixed;
top: 20px;
right: 20px;
padding: 15px 20px;
background: #28a745;
color: white;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
transform: translateX(120%);
transition: transform 0.3s ease-out;
z-index: 1000;
}
.notification.show {
transform: translateX(0);
}
</style>
</head>
<body>
<div class="container">
<header>
<h1 class="app-title">📝 待办事项</h1>
<p class="app-subtitle">基于HTML5本地存储的智能待办应用</p>
</header>
<section class="input-section">
<div class="input-group">
<input type="text" id="todo-input" placeholder="添加新的待办事项..." maxlength="100">
<button id="add-btn">添加</button>
</div>
</section>
<div class="stats">
<span id="total-count">总计: 0 项</span>
<span id="completed-count">已完成: 0 项</span>
</div>
<div class="filter-buttons">
<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>
</div>
<ul class="todo-list" id="todo-list">
<li class="empty-state">
<h3>📋 暂无待办事项</h3>
<p>添加您的第一个任务开始使用吧!</p>
</li>
</ul>
<div class="clear-completed">
<button id="clear-btn">清除已完成事项</button>
</div>
</div>
<div class="notification" id="notification"></div>
<script>
// 待办事项类
class Todo {
constructor(text) {
this.id = Date.now() + Math.random(); // 简单ID生成
this.text = text;
this.completed = false;
this.createdAt = new Date().toISOString();
this.completedAt = null;
}
}
// 待办事项管理器
class TodoManager {
constructor() {
this.todos = this.loadTodos();
this.currentFilter = 'all';
this.init();
}
// 初始化应用
init() {
this.render();
this.bindEvents();
this.updateStats();
}
// 绑定事件
bindEvents() {
// 添加待办事项
const input = document.getElementById('todo-input');
const addButton = document.getElementById('add-btn');
addButton.addEventListener('click', () => this.addTodo());
input.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
this.addTodo();
}
});
// 过滤按钮
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();
});
});
// 清除已完成事项
document.getElementById('clear-btn').addEventListener('click', () => {
this.clearCompleted();
});
}
// 从localStorage加载待办事项
loadTodos() {
const todosData = localStorage.getItem('todos');
return todosData ? JSON.parse(todosData) : [];
}
// 保存待办事项到localStorage
saveTodos() {
localStorage.setItem('todos', JSON.stringify(this.todos));
}
// 添加待办事项
addTodo() {
const input = document.getElementById('todo-input');
const text = input.value.trim();
if (text) {
const todo = new Todo(text);
this.todos.unshift(todo); // 添加到列表开头
this.saveTodos();
this.render();
this.updateStats();
input.value = '';
this.showNotification('✅ 待办事项已添加');
}
}
// 切换待办事项完成状态
toggleTodo(id) {
const todo = this.todos.find(t => t.id === id);
if (todo) {
todo.completed = !todo.completed;
if (todo.completed) {
todo.completedAt = new Date().toISOString();
} else {
todo.completedAt = null;
}
this.saveTodos();
this.render();
this.updateStats();
this.showNotification(todo.completed ? '✅ 任务已完成' : '↩️ 任务已恢复');
}
}
// 编辑待办事项
editTodo(id, newText) {
const todo = this.todos.find(t => t.id === id);
if (todo && newText.trim()) {
todo.text = newText.trim();
this.saveTodos();
this.render();
this.showNotification('✏️ 任务已更新');
}
}
// 删除待办事项
deleteTodo(id) {
if (confirm('确定要删除这个待办事项吗?')) {
this.todos = this.todos.filter(t => t.id !== id);
this.saveTodos();
this.render();
this.updateStats();
this.showNotification('🗑️ 任务已删除');
}
}
// 清除已完成事项
clearCompleted() {
const completedCount = this.todos.filter(t => t.completed).length;
if (completedCount > 0) {
if (confirm(`确定要清除 ${completedCount} 个已完成的事项吗?`)) {
this.todos = this.todos.filter(t => !t.completed);
this.saveTodos();
this.render();
this.updateStats();
this.showNotification(`🧹 已清除 ${completedCount} 个已完成事项`);
}
} else {
this.showNotification('📋 没有已完成的事项');
}
}
// 获取过滤后的待办事项
getFilteredTodos() {
switch (this.currentFilter) {
case 'active':
return this.todos.filter(t => !t.completed);
case 'completed':
return this.todos.filter(t => t.completed);
default:
return this.todos;
}
}
// 渲染待办事项列表
render() {
const todoList = document.getElementById('todo-list');
const filteredTodos = this.getFilteredTodos();
if (filteredTodos.length === 0) {
todoList.innerHTML = `
<li class="empty-state">
<h3>${this.currentFilter === 'completed' ? '✅ 暂无已完成事项' :
this.currentFilter === 'active' ? '📝 暂无未完成事项' : '📋 暂无待办事项'}</h3>
<p>${this.currentFilter === 'completed' ? '完成一些任务来查看这里的内容' :
this.currentFilter === 'active' ? '所有任务都已完成!' : '添加您的第一个任务开始使用吧!'}</p>
</li>
`;
return;
}
todoList.innerHTML = filteredTodos.map(todo => `
<li class="todo-item ${todo.completed ? 'completed' : ''}" data-id="${todo.id}">
<input type="checkbox" class="todo-checkbox" ${todo.completed ? 'checked' : ''}>
<span class="todo-text">${this.escapeHtml(todo.text)}</span>
<span class="todo-date">${this.formatDate(todo.createdAt)}</span>
<div class="todo-actions">
<button class="edit-btn">编辑</button>
<button class="delete-btn">删除</button>
</div>
</li>
`).join('');
// 绑定待办事项事件
this.bindTodoEvents();
}
// 绑定待办事项事件
bindTodoEvents() {
// 切换完成状态
document.querySelectorAll('.todo-checkbox').forEach(checkbox => {
checkbox.addEventListener('change', (e) => {
const id = parseFloat(e.target.closest('.todo-item').dataset.id);
this.toggleTodo(id);
});
});
// 编辑待办事项
document.querySelectorAll('.edit-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
const todoItem = e.target.closest('.todo-item');
const id = parseFloat(todoItem.dataset.id);
const todoText = todoItem.querySelector('.todo-text');
const currentText = todoText.textContent;
const newText = prompt('编辑待办事项:', currentText);
if (newText !== null) {
this.editTodo(id, newText);
}
});
});
// 删除待办事项
document.querySelectorAll('.delete-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
const id = parseFloat(e.target.closest('.todo-item').dataset.id);
this.deleteTodo(id);
});
});
}
// 更新统计信息
updateStats() {
const totalCount = this.todos.length;
const completedCount = this.todos.filter(t => t.completed).length;
document.getElementById('total-count').textContent = `总计: ${totalCount} 项`;
document.getElementById('completed-count').textContent = `已完成: ${completedCount} 项`;
}
// 显示通知
showNotification(message) {
const notification = document.getElementById('notification');
notification.textContent = message;
notification.classList.add('show');
setTimeout(() => {
notification.classList.remove('show');
}, 3000);
}
// 格式化日期
formatDate(dateString) {
const date = new Date(dateString);
const now = new Date();
const diffTime = Math.abs(now - date);
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
if (diffDays === 1) {
return '今天';
} else if (diffDays === 2) {
return '昨天';
} else if (diffDays <= 7) {
return `${diffDays - 1}天前`;
} else {
return date.toLocaleDateString('zh-CN');
}
}
// 转义HTML
escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
}
// 创建待办事项管理器实例
const todoManager = new TodoManager();
// 页面可见性API - 页面获得焦点时刷新数据
document.addEventListener('visibilitychange', () => {
if (!document.hidden) {
// 重新加载数据以同步其他标签页的更改
todoManager.todos = todoManager.loadTodos();
todoManager.render();
todoManager.updateStats();
}
});
</script>
</body>
</html>通过学习HTML5的新特性和实践项目,您已经掌握了现代Web开发中的重要技术。这些特性使Web应用更加强大和用户友好,为创建丰富的交互式体验提供了坚实的基础。