Skip to content

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>&copy; 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>&lt;aside&gt;</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>&copy; 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应用更加强大和用户友好,为创建丰富的交互式体验提供了坚实的基础。