HTML与相关技术整合
在现代Web开发中,HTML很少单独使用,而是与CSS和JavaScript紧密配合,形成完整的Web应用。掌握HTML如何与这些技术整合是成为专业Web开发者的关键。
HTML与CSS结合
CSS(层叠样式表)负责控制网页的外观和布局,与HTML结构紧密结合。
在HTML中引入CSS
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS引入方式</title>
<!-- 1. 外部样式表 -->
<link rel="stylesheet" href="styles.css">
<!-- 2. 内部样式表 -->
<style>
.internal-style {
color: blue;
font-size: 18px;
}
</style>
</head>
<body>
<!-- 3. 内联样式 -->
<p style="color: red; font-size: 20px;">这是内联样式</p>
<p class="internal-style">这是内部样式</p>
<p class="external-style">这是外部样式</p>
</body>
</html>CSS选择器与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>CSS选择器示例</title>
<style>
/* 元素选择器 */
h1 {
color: navy;
}
/* 类选择器 */
.highlight {
background-color: yellow;
}
/* ID选择器 */
#main-title {
font-size: 2em;
text-align: center;
}
/* 后代选择器 */
.article p {
line-height: 1.6;
}
/* 子选择器 */
.menu > li {
list-style: none;
margin-bottom: 10px;
}
/* 相邻兄弟选择器 */
h2 + p {
font-style: italic;
}
/* 属性选择器 */
input[type="text"] {
border: 2px solid #ccc;
padding: 10px;
}
/* 伪类选择器 */
a:hover {
color: red;
}
/* 伪元素选择器 */
p::first-line {
font-weight: bold;
}
</style>
</head>
<body>
<h1 id="main-title">主标题</h1>
<ul class="menu">
<li><a href="#">首页</a></li>
<li><a href="#">关于</a></li>
<li><a href="#">联系</a></li>
</ul>
<div class="article">
<h2 class="highlight">文章标题</h2>
<p>这是文章的第一段内容,会应用特殊样式。</p>
<p>这是文章的第二段内容。</p>
</div>
<form>
<input type="text" placeholder="请输入文本">
<input type="email" placeholder="请输入邮箱">
</form>
</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>
<style>
.box {
width: 200px;
height: 100px;
padding: 20px;
border: 5px solid #333;
margin: 10px;
background-color: #f0f0f0;
}
.content-box {
box-sizing: content-box; /* 默认值 */
}
.border-box {
box-sizing: border-box;
}
.info {
margin: 20px 0;
padding: 10px;
background-color: #e7f3ff;
border-left: 4px solid #2196F3;
}
</style>
</head>
<body>
<div class="info">
<h3>盒模型说明</h3>
<p><strong>content-box(默认)</strong>: width和height只包括内容区域</p>
<p><strong>border-box</strong>: width和height包括内容、内边距和边框</p>
</div>
<div class="box content-box">
<p>content-box: 200x100 + 40(内边距) + 10(边框) = 250x150</p>
</div>
<div class="box border-box">
<p>border-box: 总宽度200px,总高度100px</p>
</div>
</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>
<style>
.container {
width: 100%;
border: 2px solid #333;
padding: 10px;
}
.float-left {
float: left;
width: 30%;
background-color: #ffcccc;
padding: 10px;
margin: 10px;
}
.float-right {
float: right;
width: 30%;
background-color: #ccffcc;
padding: 10px;
margin: 10px;
}
.clear {
clear: both;
}
.clearfix::after {
content: "";
display: table;
clear: both;
}
</style>
</head>
<body>
<div class="container clearfix">
<div class="float-left">
<h3>左侧栏</h3>
<p>这是左侧浮动的内容区域。</p>
</div>
<div class="float-right">
<h3>右侧栏</h3>
<p>这是右侧浮动的内容区域。</p>
</div>
<div>
<h3>主要内容</h3>
<p>这是主要内容区域,会自动填充剩余空间。注意使用clearfix来清除浮动。</p>
<p>浮动布局虽然经典,但在现代开发中更推荐使用Flexbox或Grid布局。</p>
</div>
</div>
</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>
<style>
.relative-container {
position: relative;
width: 400px;
height: 300px;
border: 2px solid #333;
margin: 20px;
}
.absolute-element {
position: absolute;
top: 20px;
right: 20px;
width: 100px;
height: 50px;
background-color: #ff6b6b;
color: white;
display: flex;
align-items: center;
justify-content: center;
}
.fixed-element {
position: fixed;
bottom: 20px;
right: 20px;
width: 150px;
height: 50px;
background-color: #4ecdc4;
color: white;
display: flex;
align-items: center;
justify-content: center;
border-radius: 5px;
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
}
.sticky-element {
position: sticky;
top: 0;
background-color: #45b7d1;
color: white;
padding: 10px;
text-align: center;
border-radius: 5px;
}
.content {
height: 200px;
margin: 10px 0;
background-color: #f0f0f0;
display: flex;
align-items: center;
justify-content: center;
}
</style>
</head>
<body>
<div class="sticky-element">
粘性定位元素 - 滚动时会固定在顶部
</div>
<div class="content">
<p>滚动页面查看粘性定位效果</p>
</div>
<div class="relative-container">
<p>相对定位容器</p>
<div class="absolute-element">
绝对定位
</div>
<div class="content">
<p>容器内容</p>
</div>
</div>
<div class="content">
<p>更多内容用于滚动测试...</p>
</div>
<div class="content">
<p>继续滚动...</p>
</div>
<div class="fixed-element">
固定定位元素
</div>
</body>
</html>HTML与JavaScript交互
JavaScript为HTML页面添加交互性和动态功能,使网页从静态文档变为动态应用。
在HTML中嵌入JavaScript代码
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript嵌入方式</title>
</head>
<body>
<h1>JavaScript嵌入示例</h1>
<!-- 1. 内部JavaScript -->
<script>
console.log('这是内部JavaScript');
document.write('<p>通过document.write添加的内容</p>');
</script>
<!-- 2. 外部JavaScript文件 -->
<script src="script.js"></script>
<!-- 3. 行内事件处理器 -->
<button onclick="alert('按钮被点击了!')">点击我</button>
<!-- 4. 页面加载完成后执行 -->
<script>
window.onload = function() {
console.log('页面加载完成');
};
// DOM内容加载完成后执行
document.addEventListener('DOMContentLoaded', function() {
console.log('DOM内容加载完成');
});
</script>
</body>
</html>DOM(文档对象模型)概念
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DOM操作示例</title>
<style>
.highlight {
background-color: yellow;
}
.hidden {
display: none;
}
.box {
padding: 20px;
margin: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
</style>
</head>
<body>
<h1 id="main-title">DOM操作示例</h1>
<div class="box">
<p class="content">这是原始内容</p>
<button id="change-text">改变文本</button>
</div>
<div class="box">
<ul id="item-list">
<li>项目1</li>
<li>项目2</li>
</ul>
<button id="add-item">添加项目</button>
<button id="remove-item">删除项目</button>
</div>
<div class="box">
<p id="style-demo">样式操作演示</p>
<button id="highlight">高亮</button>
<button id="toggle-visibility">切换可见性</button>
</div>
<div class="box">
<input type="text" id="input-field" placeholder="输入一些文本">
<p>输入的值: <span id="input-value"></span></p>
</div>
<script>
// 1. 获取元素
const title = document.getElementById('main-title');
const content = document.querySelector('.content');
const items = document.querySelectorAll('#item-list li');
// 2. 改变元素内容
document.getElementById('change-text').addEventListener('click', function() {
content.textContent = '内容已被JavaScript改变!';
content.innerHTML = '<strong>内容已被<strong style="color: red;">加粗和着色</strong>!</strong>';
});
// 3. 添加和删除元素
let itemCount = 2;
document.getElementById('add-item').addEventListener('click', function() {
itemCount++;
const newItem = document.createElement('li');
newItem.textContent = `项目${itemCount}`;
document.getElementById('item-list').appendChild(newItem);
});
document.getElementById('remove-item').addEventListener('click', function() {
const list = document.getElementById('item-list');
if (list.lastElementChild) {
list.removeChild(list.lastElementChild);
itemCount--;
}
});
// 4. 样式操作
document.getElementById('highlight').addEventListener('click', function() {
const element = document.getElementById('style-demo');
element.classList.toggle('highlight');
});
document.getElementById('toggle-visibility').addEventListener('click', function() {
const element = document.getElementById('style-demo');
element.classList.toggle('hidden');
});
// 5. 事件处理
document.getElementById('input-field').addEventListener('input', function(e) {
document.getElementById('input-value').textContent = e.target.value;
});
// 6. 属性操作
title.setAttribute('data-timestamp', new Date().toISOString());
console.log('标题元素的data属性:', title.getAttribute('data-timestamp'));
</script>
</body>
</html>通过JS操作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>JavaScript操作HTML元素</title>
<style>
.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
background-color: #f9f9f9;
}
.highlight {
background-color: #ffeb3b;
}
.hidden {
display: none;
}
button {
padding: 10px 15px;
margin: 5px;
background-color: #2196F3;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #1976D2;
}
</style>
</head>
<body>
<div class="container">
<h1>JavaScript操作HTML元素</h1>
<div class="card">
<h2>元素选择方法</h2>
<p id="demo-paragraph">这是一个演示段落</p>
<p class="demo-class">这是有类名的段落</p>
<p data-demo="true">这是有data属性的段落</p>
<button onclick="getElementByIdDemo()">getElementById</button>
<button onclick="getElementsByClassNameDemo()">getElementsByClassName</button>
<button onclick="getElementsByTagNameDemo()">getElementsByTagName</button>
<button onclick="querySelectorDemo()">querySelector</button>
<button onclick="querySelectorAllDemo()">querySelectorAll</button>
</div>
<div class="card">
<h2>内容操作</h2>
<p id="content-demo">原始内容</p>
<button onclick="changeTextContent()">改变textContent</button>
<button onclick="changeInnerHTML()">改变innerHTML</button>
<button onclick="appendContent()">追加内容</button>
</div>
<div class="card">
<h2>属性操作</h2>
<img id="demo-image" src="https://via.placeholder.com/150" alt="占位图">
<input type="text" id="demo-input" value="原始值">
<button onclick="getAttributeDemo()">获取属性</button>
<button onclick="setAttributeDemo()">设置属性</button>
<button onclick="removeAttributeDemo()">移除属性</button>
</div>
<div class="card">
<h2>样式操作</h2>
<p id="style-demo">样式操作演示</p>
<button onclick="changeInlineStyle()">改变内联样式</button>
<button onclick="addClass()">添加类</button>
<button onclick="removeClass()">移除类</button>
<button onclick="toggleClass()">切换类</button>
</div>
<div class="card">
<h2>节点操作</h2>
<ul id="node-demo">
<li>原始项目1</li>
<li>原始项目2</li>
</ul>
<button onclick="createElementDemo()">创建元素</button>
<button onclick="appendChildDemo()">添加子元素</button>
<button onclick="removeChildDemo()">删除子元素</button>
<button onclick="replaceChildDemo()">替换子元素</button>
</div>
</div>
<script>
// 元素选择方法
function getElementByIdDemo() {
const element = document.getElementById('demo-paragraph');
element.classList.toggle('highlight');
}
function getElementsByClassNameDemo() {
const elements = document.getElementsByClassName('demo-class');
for (let element of elements) {
element.classList.toggle('highlight');
}
}
function getElementsByTagNameDemo() {
const elements = document.getElementsByTagName('p');
for (let element of elements) {
element.classList.toggle('highlight');
}
}
function querySelectorDemo() {
const element = document.querySelector('[data-demo="true"]');
element.classList.toggle('highlight');
}
function querySelectorAllDemo() {
const elements = document.querySelectorAll('p');
elements.forEach(element => {
element.classList.toggle('highlight');
});
}
// 内容操作
function changeTextContent() {
const element = document.getElementById('content-demo');
element.textContent = 'textContent已改变: ' + new Date().toLocaleTimeString();
}
function changeInnerHTML() {
const element = document.getElementById('content-demo');
element.innerHTML = '<strong style="color: red;">innerHTML已改变</strong>: ' + new Date().toLocaleTimeString();
}
function appendContent() {
const element = document.getElementById('content-demo');
const newContent = document.createElement('span');
newContent.textContent = ' [追加的内容]';
newContent.style.color = 'blue';
element.appendChild(newContent);
}
// 属性操作
function getAttributeDemo() {
const img = document.getElementById('demo-image');
const src = img.getAttribute('src');
const alt = img.getAttribute('alt');
alert(`src: ${src}\nalt: ${alt}`);
}
function setAttributeDemo() {
const img = document.getElementById('demo-image');
img.setAttribute('src', 'https://via.placeholder.com/200');
img.setAttribute('alt', '新的占位图');
const input = document.getElementById('demo-input');
input.setAttribute('value', '新值: ' + new Date().toLocaleTimeString());
}
function removeAttributeDemo() {
const img = document.getElementById('demo-image');
img.removeAttribute('alt');
}
// 样式操作
function changeInlineStyle() {
const element = document.getElementById('style-demo');
element.style.color = '#' + Math.floor(Math.random()*16777215).toString(16);
element.style.fontSize = (16 + Math.random() * 20) + 'px';
}
function addClass() {
const element = document.getElementById('style-demo');
element.classList.add('highlight');
}
function removeClass() {
const element = document.getElementById('style-demo');
element.classList.remove('highlight');
}
function toggleClass() {
const element = document.getElementById('style-demo');
element.classList.toggle('highlight');
}
// 节点操作
function createElementDemo() {
const newElement = document.createElement('div');
newElement.textContent = '新创建的元素: ' + new Date().toLocaleTimeString();
newElement.style.padding = '10px';
newElement.style.backgroundColor = '#e8f5e8';
newElement.style.border = '1px solid #4CAF50';
document.body.appendChild(newElement);
// 3秒后自动删除
setTimeout(() => {
if (newElement.parentNode) {
newElement.parentNode.removeChild(newElement);
}
}, 3000);
}
function appendChildDemo() {
const list = document.getElementById('node-demo');
const newItem = document.createElement('li');
newItem.textContent = '新项目 ' + (list.children.length + 1);
list.appendChild(newItem);
}
function removeChildDemo() {
const list = document.getElementById('node-demo');
if (list.lastElementChild) {
list.removeChild(list.lastElementChild);
}
}
function replaceChildDemo() {
const list = document.getElementById('node-demo');
if (list.firstElementChild) {
const newItem = document.createElement('li');
newItem.textContent = '替换的项目: ' + new Date().toLocaleTimeString();
newItem.style.color = 'red';
list.replaceChild(newItem, list.firstElementChild);
}
}
</script>
</body>
</html>响应式设计基础
响应式设计使网页能够在不同设备和屏幕尺寸上提供良好的用户体验。
viewport元标签的作用
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<!-- viewport元标签是响应式设计的关键 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Viewport元标签示例</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
background-color: #f0f0f0;
padding: 20px;
border: 1px solid #ccc;
}
.box {
background-color: #4CAF50;
color: white;
padding: 20px;
margin: 10px 0;
text-align: center;
}
/* 没有viewport标签时,移动端会显示非常小 */
@media screen and (max-width: 600px) {
.container {
background-color: #ffeb3b;
}
.box {
background-color: #f44336;
}
}
</style>
</head>
<body>
<div class="container">
<h1>Viewport元标签演示</h1>
<p>这个页面展示了viewport元标签在响应式设计中的作用。</p>
<div class="box">
<h2>响应式盒子</h2>
<p>在移动设备上查看此页面,观察不同效果</p>
</div>
<div class="box">
<h3>宽度测试</h3>
<p>当前容器宽度: <span id="width-display"></span></p>
</div>
</div>
<script>
function updateWidth() {
const width = document.querySelector('.container').offsetWidth;
document.getElementById('width-display').textContent = width + 'px';
}
window.addEventListener('resize', updateWidth);
window.addEventListener('load', updateWidth);
</script>
</body>
</html>媒体查询与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>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: Arial, sans-serif;
line-height: 1.6;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
header {
background: #333;
color: white;
padding: 1rem 0;
}
nav {
display: flex;
justify-content: space-between;
align-items: center;
}
.nav-links {
display: flex;
list-style: none;
}
.nav-links li {
margin-left: 2rem;
}
.nav-links a {
color: white;
text-decoration: none;
}
.hero {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
text-align: center;
padding: 4rem 0;
}
.content {
display: grid;
grid-template-columns: 2fr 1fr;
gap: 2rem;
padding: 2rem 0;
}
.main-content {
background: #f4f4f4;
padding: 2rem;
}
.sidebar {
background: #e4e4e4;
padding: 2rem;
}
.card-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
margin-top: 2rem;
}
.card {
background: white;
padding: 1.5rem;
border-radius: 8px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
footer {
background: #333;
color: white;
text-align: center;
padding: 2rem 0;
}
/* 平板设备 (768px 及以下) */
@media screen and (max-width: 768px) {
nav {
flex-direction: column;
}
.nav-links {
margin-top: 1rem;
}
.nav-links li {
margin: 0 1rem;
}
.content {
grid-template-columns: 1fr;
}
.card-grid {
grid-template-columns: repeat(2, 1fr);
}
}
/* 手机设备 (480px 及以下) */
@media screen and (max-width: 480px) {
.container {
padding: 0 10px;
}
.hero {
padding: 2rem 0;
}
.nav-links {
flex-wrap: wrap;
justify-content: center;
}
.nav-links li {
margin: 0.5rem;
}
.card-grid {
grid-template-columns: 1fr;
}
}
/* 大屏幕设备 (1200px 及以上) */
@media screen and (min-width: 1200px) {
.card-grid {
grid-template-columns: repeat(4, 1fr);
}
}
</style>
</head>
<body>
<header>
<div class="container">
<nav>
<div class="logo">
<h2>响应式网站</h2>
</div>
<ul class="nav-links">
<li><a href="#">首页</a></li>
<li><a href="#">关于</a></li>
<li><a href="#">服务</a></li>
<li><a href="#">联系</a></li>
</ul>
</nav>
</div>
</header>
<section class="hero">
<div class="container">
<h1>响应式设计演示</h1>
<p>调整浏览器窗口大小查看响应式效果</p>
</div>
</section>
<div class="container">
<div class="content">
<main class="main-content">
<h2>主要内容</h2>
<p>这是主要内容区域,在不同屏幕尺寸下会有不同的布局表现。</p>
<div class="card-grid">
<div class="card">
<h3>卡片 1</h3>
<p>响应式卡片内容</p>
</div>
<div class="card">
<h3>卡片 2</h3>
<p>响应式卡片内容</p>
</div>
<div class="card">
<h3>卡片 3</h3>
<p>响应式卡片内容</p>
</div>
<div class="card">
<h3>卡片 4</h3>
<p>响应式卡片内容</p>
</div>
<div class="card">
<h3>卡片 5</h3>
<p>响应式卡片内容</p>
</div>
<div class="card">
<h3>卡片 6</h3>
<p>响应式卡片内容</p>
</div>
</div>
</main>
<aside class="sidebar">
<h3>侧边栏</h3>
<p>这是侧边栏内容,在小屏幕下会移到主内容下方。</p>
<ul>
<li>链接 1</li>
<li>链接 2</li>
<li>链接 3</li>
<li>链接 4</li>
</ul>
</aside>
</div>
</div>
<footer>
<div class="container">
<p>© 2023 响应式网站. 保留所有权利.</p>
</div>
</footer>
</body>
</html>可访问性(A11Y)
可访问性确保所有人都能使用网页,包括残障人士。
编写符合WCAG标准的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>
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
}
.container {
max-width: 800px;
margin: 0 auto;
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1, h2, h3 {
color: #333;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input, select, textarea {
width: 100%;
padding: 10px;
border: 2px solid #ddd;
border-radius: 4px;
font-size: 16px;
}
input:focus, select:focus, textarea:focus {
border-color: #4CAF50;
outline: none;
}
.error {
color: #f44336;
font-size: 14px;
margin-top: 5px;
}
.skip-link {
position: absolute;
top: -40px;
left: 6px;
background: #000;
color: #fff;
padding: 8px;
text-decoration: none;
border-radius: 4px;
}
.skip-link:focus {
top: 6px;
}
.contrast-high {
background: #000;
color: #fff;
padding: 10px;
margin: 10px 0;
}
.contrast-low {
background: #f0f0f0;
color: #666;
padding: 10px;
margin: 10px 0;
}
</style>
</head>
<body>
<!-- 跳转链接,方便键盘用户快速导航 -->
<a href="#main-content" class="skip-link">跳转到主内容</a>
<div class="container">
<header>
<h1>可访问性(A11Y)示例</h1>
<p>展示如何编写符合WCAG标准的HTML代码</p>
</header>
<nav aria-label="主导航">
<ul>
<li><a href="#forms">表单可访问性</a></li>
<li><a href="#images">图片可访问性</a></li>
<li><a href="#contrast">对比度</a></li>
<li><a href="#structure">结构化内容</a></li>
</ul>
</nav>
<main id="main-content">
<section id="forms">
<h2>表单可访问性</h2>
<form>
<div class="form-group">
<label for="username">用户名 <span aria-label="必填">*</span></label>
<input
type="text"
id="username"
name="username"
required
aria-describedby="username-help"
>
<div id="username-help" class="help-text">
用户名至少需要3个字符
</div>
</div>
<div class="form-group">
<label for="email">邮箱地址 <span aria-label="必填">*</span></label>
<input
type="email"
id="email"
name="email"
required
aria-invalid="false"
>
</div>
<div class="form-group">
<label for="country">国家/地区</label>
<select id="country" name="country">
<option value="">请选择</option>
<option value="cn">中国</option>
<option value="us">美国</option>
<option value="uk">英国</option>
</select>
</div>
<div class="form-group">
<fieldset>
<legend>偏好设置</legend>
<input type="checkbox" id="newsletter" name="newsletter">
<label for="newsletter">订阅邮件</label>
<input type="checkbox" id="sms" name="sms">
<label for="sms">短信通知</label>
</fieldset>
</div>
<div class="form-group">
<label for="message">留言</label>
<textarea
id="message"
name="message"
rows="5"
placeholder="请输入您的留言..."
></textarea>
</div>
<button type="submit">提交</button>
</form>
</section>
<section id="images">
<h2>图片可访问性</h2>
<!-- 信息性图片必须有alt属性 -->
<figure>
<img
src="https://via.placeholder.com/300x200?text=风景图片"
alt="山峰和湖泊的风景照片,蓝天白云下碧绿的湖水倒映着雪山"
width="300"
height="200"
>
<figcaption>美丽的自然风景</figcaption>
</figure>
<!-- 装饰性图片可以使用空alt属性 -->
<div style="position: relative; margin: 20px 0;">
<img
src="https://via.placeholder.com/400x100/4CAF50/white?text=装饰性横幅"
alt=""
width="400"
height="100"
>
<span style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: white; font-size: 24px;">
主要内容标题
</span>
</div>
<!-- 复杂图片需要详细描述 -->
<figure>
<img
src="https://via.placeholder.com/400x300?text=图表"
alt="年度销售数据图表,详细描述请参见下方文字"
width="400"
height="300"
>
<figcaption>
<details>
<summary>图表详细描述</summary>
<p>这张图表显示了2023年各季度的销售数据。第一季度销售额为100万,第二季度为150万,第三季度为120万,第四季度为180万。整体呈现上升趋势。</p>
</details>
</figcaption>
</figure>
</section>
<section id="contrast">
<h2>颜色对比度</h2>
<p>确保文本和背景之间有足够的对比度(至少4.5:1)</p>
<div class="contrast-high">
<p>高对比度文本 - 易于阅读</p>
</div>
<div class="contrast-low">
<p>低对比度文本 - 难以阅读(不符合可访问性标准)</p>
</div>
</section>
<section id="structure">
<h2>结构化内容</h2>
<!-- 正确的标题层级 -->
<article>
<header>
<h2>文章标题</h2>
<p>
<time datetime="2023-01-01">2023年1月1日</time>
<span aria-hidden="true"> • </span>
<span>作者:张三</span>
</p>
</header>
<p>文章简介...</p>
<section>
<h3>第一章节</h3>
<p>章节内容...</p>
</section>
<section>
<h3>第二章节</h3>
<p>章节内容...</p>
</section>
<footer>
<p>标签:
<span aria-label="标签">HTML</span>,
<span aria-label="标签">可访问性</span>,
<span aria-label="标签">Web标准</span>
</p>
</footer>
</article>
<!-- 列表结构 -->
<h3>导航列表</h3>
<nav aria-label="相关内容">
<ul>
<li><a href="#">相关文章1</a></li>
<li><a href="#">相关文章2</a></li>
<li><a href="#">相关文章3</a></li>
</ul>
</nav>
<!-- 表格结构 -->
<h3>数据表格</h3>
<table>
<caption>2023年销售数据</caption>
<thead>
<tr>
<th scope="col">季度</th>
<th scope="col">销售额</th>
<th scope="col">增长率</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Q1</th>
<td>¥1,000,000</td>
<td>5%</td>
</tr>
<tr>
<th scope="row">Q2</th>
<td>¥1,500,000</td>
<td>10%</td>
</tr>
</tbody>
</table>
</section>
</main>
<footer>
<h2>页脚</h2>
<nav aria-label="页脚导航">
<ul>
<li><a href="#">隐私政策</a></li>
<li><a href="#">使用条款</a></li>
<li><a href="#">联系我们</a></li>
</ul>
</nav>
</footer>
</div>
</body>
</html>正确使用ARIA属性
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ARIA属性示例</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: #f0f0f0;
}
.container {
max-width: 800px;
margin: 0 auto;
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.accordion {
border: 1px solid #ddd;
border-radius: 5px;
margin-bottom: 10px;
}
.accordion-header {
background: #f5f5f5;
padding: 15px;
cursor: pointer;
border: none;
width: 100%;
text-align: left;
font-size: 16px;
display: flex;
justify-content: space-between;
align-items: center;
}
.accordion-header:focus {
outline: 2px solid #4CAF50;
}
.accordion-content {
padding: 0 15px;
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease-out;
}
.accordion-content.expanded {
padding: 15px;
max-height: 500px;
}
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
z-index: 1000;
}
.modal-content {
background: white;
margin: 10% auto;
padding: 20px;
border-radius: 10px;
max-width: 500px;
position: relative;
}
.close-button {
position: absolute;
top: 10px;
right: 15px;
font-size: 24px;
cursor: pointer;
background: none;
border: none;
}
.progress-bar {
width: 100%;
height: 20px;
background: #e0e0e0;
border-radius: 10px;
overflow: hidden;
margin: 20px 0;
}
.progress-fill {
height: 100%;
background: #4CAF50;
width: 0%;
transition: width 0.3s ease;
}
.status {
margin: 10px 0;
padding: 10px;
border-radius: 5px;
}
.status[aria-live="polite"] {
background: #e3f2fd;
}
.status[aria-live="assertive"] {
background: #ffebee;
}
button {
padding: 10px 15px;
margin: 5px;
background: #2196F3;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background: #1976D2;
}
button:focus {
outline: 2px solid #4CAF50;
}
</style>
</head>
<body>
<div class="container">
<h1>ARIA属性示例</h1>
<p>展示如何正确使用ARIA属性提高可访问性</p>
<section>
<h2>可折叠面板(Accordion)</h2>
<div class="accordion">
<button
class="accordion-header"
aria-expanded="false"
aria-controls="section1"
>
<span>第一部分</span>
<span aria-hidden="true">▼</span>
</button>
<div
id="section1"
class="accordion-content"
role="region"
aria-labelledby="accordion-header-1"
>
<p>这是第一部分的内容。使用ARIA属性可以告诉辅助技术这个区域是可折叠的。</p>
</div>
</div>
<div class="accordion">
<button
class="accordion-header"
aria-expanded="false"
aria-controls="section2"
>
<span>第二部分</span>
<span aria-hidden="true">▼</span>
</button>
<div
id="section2"
class="accordion-content"
role="region"
aria-labelledby="accordion-header-2"
>
<p>这是第二部分的内容。注意按钮的aria-expanded属性会随着面板的展开/收起而变化。</p>
</div>
</div>
</section>
<section>
<h2>模态对话框</h2>
<button id="open-modal">打开模态框</button>
<div
id="modal"
class="modal"
role="dialog"
aria-labelledby="modal-title"
aria-describedby="modal-description"
aria-modal="true"
>
<div class="modal-content">
<button class="close-button" aria-label="关闭对话框">×</button>
<h3 id="modal-title">模态对话框标题</h3>
<p id="modal-description">这是一个模态对话框示例。使用ARIA属性可以确保辅助技术正确识别和处理对话框。</p>
<button id="modal-action">执行操作</button>
<button id="modal-cancel">取消</button>
</div>
</div>
</section>
<section>
<h2>进度指示器</h2>
<div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" aria-label="加载进度">
<div class="progress-fill"></div>
</div>
<button id="start-progress">开始加载</button>
</section>
<section>
<h2>状态消息</h2>
<div aria-live="polite" class="status">
<p>礼貌性状态消息:这种消息会在用户空闲时读出。</p>
</div>
<div aria-live="assertive" class="status">
<p>紧急状态消息:这种消息会立即打断用户读出。</p>
</div>
<button id="add-polite-message">添加礼貌消息</button>
<button id="add-assertive-message">添加紧急消息</button>
</section>
</div>
<script>
// 可折叠面板功能
document.querySelectorAll('.accordion-header').forEach(button => {
button.addEventListener('click', function() {
const content = this.nextElementSibling;
const isExpanded = this.getAttribute('aria-expanded') === 'true';
// 切换展开状态
this.setAttribute('aria-expanded', !isExpanded);
content.classList.toggle('expanded');
});
});
// 模态对话框功能
const modal = document.getElementById('modal');
const openModalBtn = document.getElementById('open-modal');
const closeModalBtn = document.querySelector('.close-button');
const modalCancelBtn = document.getElementById('modal-cancel');
function openModal() {
modal.style.display = 'block';
// 将焦点移到模态框内
modal.querySelector('button').focus();
// 阻止背景内容被访问
document.body.style.overflow = 'hidden';
}
function closeModal() {
modal.style.display = 'none';
// 恢复背景内容访问
document.body.style.overflow = 'auto';
// 将焦点返回到触发按钮
openModalBtn.focus();
}
openModalBtn.addEventListener('click', openModal);
closeModalBtn.addEventListener('click', closeModal);
modalCancelBtn.addEventListener('click', closeModal);
// 点击模态框外部关闭
modal.addEventListener('click', function(e) {
if (e.target === modal) {
closeModal();
}
});
// ESC键关闭模态框
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && modal.style.display === 'block') {
closeModal();
}
});
// 进度条功能
const progressBar = document.querySelector('[role="progressbar"]');
const progressFill = document.querySelector('.progress-fill');
const startProgressBtn = document.getElementById('start-progress');
startProgressBtn.addEventListener('click', function() {
let progress = 0;
const interval = setInterval(() => {
progress += 10;
if (progress > 100) {
clearInterval(interval);
return;
}
progressFill.style.width = progress + '%';
progressBar.setAttribute('aria-valuenow', progress);
progressBar.setAttribute('aria-label', `加载进度 ${progress}%`);
}, 500);
});
// 状态消息功能
const politeStatus = document.querySelector('[aria-live="polite"]');
const assertiveStatus = document.querySelector('[aria-live="assertive"]');
document.getElementById('add-polite-message').addEventListener('click', function() {
const message = document.createElement('p');
message.textContent = `礼貌消息添加于: ${new Date().toLocaleTimeString()}`;
politeStatus.appendChild(message);
});
document.getElementById('add-assertive-message').addEventListener('click', function() {
const message = document.createElement('p');
message.textContent = `紧急消息添加于: ${new Date().toLocaleTimeString()}`;
assertiveStatus.appendChild(message);
});
</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>
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
margin: 0;
padding: 20px;
background-color: #f8f9fa;
}
.container {
max-width: 600px;
margin: 0 auto;
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #333;
text-align: center;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
color: #555;
}
input, select, textarea {
width: 100%;
padding: 12px;
border: 2px solid #ddd;
border-radius: 4px;
font-size: 16px;
transition: border-color 0.3s;
}
input:focus, select:focus, textarea:focus {
border-color: #4CAF50;
outline: none;
}
fieldset {
border: 2px solid #ddd;
border-radius: 4px;
padding: 20px;
margin-bottom: 20px;
}
legend {
font-weight: bold;
color: #333;
padding: 0 10px;
}
.checkbox-group, .radio-group {
display: flex;
flex-wrap: wrap;
gap: 15px;
}
.checkbox-group label, .radio-group label {
display: flex;
align-items: center;
font-weight: normal;
margin-bottom: 0;
}
.checkbox-group input, .radio-group input {
width: auto;
margin-right: 8px;
}
.required {
color: #f44336;
}
.help-text {
font-size: 14px;
color: #666;
margin-top: 5px;
}
.error {
color: #f44336;
font-size: 14px;
margin-top: 5px;
}
button {
width: 100%;
padding: 12px;
background: #4CAF50;
color: white;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
transition: background 0.3s;
}
button:hover {
background: #45a049;
}
button:focus {
outline: 2px solid #4CAF50;
outline-offset: 2px;
}
.form-section {
margin-bottom: 30px;
}
.form-section h2 {
border-bottom: 2px solid #eee;
padding-bottom: 10px;
color: #333;
}
</style>
</head>
<body>
<div class="container">
<h1>表单可访问性最佳实践</h1>
<form id="accessible-form" novalidate>
<div class="form-section">
<h2>个人信息</h2>
<div class="form-group">
<label for="full-name">
姓名 <span class="required" aria-label="必填">*</span>
</label>
<input
type="text"
id="full-name"
name="full-name"
required
aria-describedby="name-help"
aria-invalid="false"
>
<div id="name-help" class="help-text">
请输入您的真实姓名
</div>
</div>
<div class="form-group">
<label for="email">
邮箱地址 <span class="required" aria-label="必填">*</span>
</label>
<input
type="email"
id="email"
name="email"
required
aria-describedby="email-help email-error"
aria-invalid="false"
>
<div id="email-help" class="help-text">
我们不会将您的邮箱用于其他用途
</div>
<div id="email-error" class="error" aria-live="polite"></div>
</div>
<div class="form-group">
<label for="phone">
手机号码
</label>
<input
type="tel"
id="phone"
name="phone"
pattern="[0-9]{11}"
aria-describedby="phone-help phone-error"
aria-invalid="false"
>
<div id="phone-help" class="help-text">
请输入11位手机号码
</div>
<div id="phone-error" class="error" aria-live="polite"></div>
</div>
</div>
<div class="form-section">
<h2>偏好设置</h2>
<fieldset>
<legend>通知偏好</legend>
<div class="checkbox-group">
<label>
<input type="checkbox" name="notifications" value="email">
邮件通知
</label>
<label>
<input type="checkbox" name="notifications" value="sms">
短信通知
</label>
<label>
<input type="checkbox" name="notifications" value="push">
推送通知
</label>
</div>
</fieldset>
<fieldset>
<legend>性别</legend>
<div class="radio-group">
<label>
<input type="radio" name="gender" value="male" required>
男
</label>
<label>
<input type="radio" name="gender" value="female" required>
女
</label>
<label>
<input type="radio" name="gender" value="other" required>
其他
</label>
</div>
</fieldset>
<div class="form-group">
<label for="birth-date">
出生日期
</label>
<input
type="date"
id="birth-date"
name="birth-date"
aria-describedby="date-help"
>
<div id="date-help" class="help-text">
用于年龄验证和个性化推荐
</div>
</div>
</div>
<div class="form-section">
<h2>其他信息</h2>
<div class="form-group">
<label for="country">
国家/地区
</label>
<select
id="country"
name="country"
aria-describedby="country-help"
>
<option value="">请选择</option>
<option value="cn">中国</option>
<option value="us">美国</option>
<option value="uk">英国</option>
<option value="jp">日本</option>
</select>
<div id="country-help" class="help-text">
选择您的居住国家/地区
</div>
</div>
<div class="form-group">
<label for="bio">
个人简介
</label>
<textarea
id="bio"
name="bio"
rows="4"
aria-describedby="bio-help"
maxlength="500"
></textarea>
<div id="bio-help" class="help-text">
简单介绍一下自己(最多500字)
</div>
</div>
</div>
<button type="submit">提交表单</button>
</form>
</div>
<script>
// 表单验证和可访问性增强
const form = document.getElementById('accessible-form');
const inputs = form.querySelectorAll('input, select, textarea');
// 实时验证
inputs.forEach(input => {
input.addEventListener('blur', function() {
validateField(this);
});
input.addEventListener('input', function() {
// 清除错误状态
if (this.getAttribute('aria-invalid') === 'true') {
this.setAttribute('aria-invalid', 'false');
const errorId = this.getAttribute('aria-describedby').split(' ').find(id => id.includes('error'));
if (errorId) {
document.getElementById(errorId).textContent = '';
}
}
});
});
// 表单提交验证
form.addEventListener('submit', function(e) {
e.preventDefault();
let isValid = true;
// 验证所有必填字段
inputs.forEach(input => {
if (!validateField(input)) {
isValid = false;
}
});
if (isValid) {
alert('表单提交成功!');
// 这里可以添加实际的表单提交逻辑
} else {
// 将焦点移到第一个错误字段
const firstError = form.querySelector('[aria-invalid="true"]');
if (firstError) {
firstError.focus();
}
}
});
// 字段验证函数
function validateField(field) {
const value = field.value.trim();
const isRequired = field.hasAttribute('required');
let isValid = true;
let errorMessage = '';
// 必填验证
if (isRequired && !value) {
errorMessage = '此字段为必填项';
isValid = false;
}
// 邮箱格式验证
if (field.type === 'email' && value) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(value)) {
errorMessage = '请输入有效的邮箱地址';
isValid = false;
}
}
// 手机号格式验证
if (field.type === 'tel' && value) {
const phoneRegex = /^[0-9]{11}$/;
if (!phoneRegex.test(value)) {
errorMessage = '请输入11位手机号码';
isValid = false;
}
}
// 更新ARIA属性和错误消息
const errorId = field.getAttribute('aria-describedby').split(' ').find(id => id.includes('error'));
if (isValid) {
field.setAttribute('aria-invalid', 'false');
if (errorId) {
document.getElementById(errorId).textContent = '';
}
} else {
field.setAttribute('aria-invalid', 'true');
if (errorId) {
document.getElementById(errorId).textContent = errorMessage;
}
}
return isValid;
}
// 添加字符计数功能
const bioTextarea = document.getElementById('bio');
const bioHelp = document.getElementById('bio-help');
bioTextarea.addEventListener('input', function() {
const maxLength = this.getAttribute('maxlength');
const currentLength = this.value.length;
bioHelp.textContent = `简单介绍一下自己(${currentLength}/${maxLength} 字)`;
});
</script>
</body>
</html>性能优化
HTML结构对页面加载性能有重要影响,合理的优化可以显著提升用户体验。
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>HTML性能优化示例</title>
<!-- 关键CSS内联以减少请求 -->
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
}
.container {
max-width: 800px;
margin: 0 auto;
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.performance-card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
}
.good {
border-left: 5px solid #4CAF50;
}
.bad {
border-left: 5px solid #f44336;
}
.code {
background: #f8f8f8;
padding: 15px;
border-radius: 5px;
font-family: 'Courier New', monospace;
font-size: 14px;
overflow-x: auto;
}
.loading-skeleton {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: loading 1.5s infinite;
height: 20px;
margin: 10px 0;
border-radius: 4px;
}
@keyframes loading {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
</style>
<!-- 预加载关键资源 -->
<link rel="preload" href="critical-image.jpg" as="image">
<link rel="preconnect" href="https://fonts.googleapis.com">
<!-- 关键字体预加载 -->
<link rel="preload" href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap"></noscript>
</head>
<body>
<div class="container">
<h1>HTML性能优化最佳实践</h1>
<p>展示如何通过优化HTML结构提升页面加载性能</p>
<div class="performance-card good">
<h2>✅ 优化的HTML结构</h2>
<div class="code">
<!DOCTYPE html><br>
<html lang="zh-CN"><br>
<head><br>
<meta charset="UTF-8"><br>
<meta name="viewport" content="width=device-width, initial-scale=1.0"><br>
<title>优化的页面标题</title><br>
<!-- 关键CSS内联 --><br>
<style><br>
/* 首屏关键样式 */<br>
body { margin: 0; font-family: Arial; }<br>
</style><br>
<!-- 预加载关键资源 --><br>
<link rel="preload" href="hero-image.jpg" as="image"><br>
</head><br>
<body><br>
<!-- 首屏内容优先 --><br>
<header><br>
<h1>页面标题</h1><br>
<nav>...</nav><br>
</header><br>
<br>
<!-- 非关键资源延迟加载 --><br>
<script src="non-critical.js" defer></script><br>
<link rel="stylesheet" href="non-critical.css" media="print" onload="this.media='all'"><br>
</body><br>
</html>
</div>
</div>
<div class="performance-card bad">
<h2>❌ 未优化的HTML结构</h2>
<div class="code">
<!DOCTYPE html><br>
<html><br>
<head><br>
<title></title><br>
<!-- 大量阻塞渲染的CSS --><br>
<link rel="stylesheet" href="massive-stylesheet.css"><br>
<link rel="stylesheet" href="another-stylesheet.css"><br>
<link rel="stylesheet" href="yet-another-stylesheet.css"><br>
</head><br>
<body><br>
<!-- 大量同步脚本阻塞渲染 --><br>
<script src="blocking-script1.js"></script><br>
<script src="blocking-script2.js"></script><br>
<br>
<div class="content"><br>
<!-- 内容混杂,无语义结构 --><br>
<div><div><div>页面内容</div></div></div><br>
</div><br>
<br>
<!-- 页面底部才引入视口外的资源 --><br>
<script src="analytics.js"></script><br>
<script src="social-widgets.js"></script><br>
</body><br>
</html>
</div>
</div>
<div class="performance-card good">
<h2>🖼️ 图片优化策略</h2>
<div class="code">
<!-- 现代图片格式 + 响应式图片 --><br>
<picture><br>
<source srcset="image.webp" type="image/webp"><br>
<source srcset="image.avif" type="image/avif"><br>
<img src="image.jpg" <br>
alt="描述性文字"<br>
loading="lazy"<br>
decoding="async"<br>
width="400" <br>
height="300"><br>
</picture><br>
<br>
<!-- 响应式图片 --><br>
<img srcset="small.jpg 480w, medium.jpg 800w, large.jpg 1200w"<br>
sizes="(max-width: 480px) 100vw, (max-width: 800px) 50vw, 25vw"<br>
src="medium.jpg"<br>
alt="响应式图片示例">
</div>
</div>
<div class="performance-card good">
<h2>⚡ 资源加载优化</h2>
<div class="code">
<!-- 异步脚本 --><br>
<script src="analytics.js" async></script><br>
<br>
<!-- 延迟脚本 --><br>
<script src="non-critical.js" defer></script><br>
<br>
<!-- 条件加载CSS --><br>
<link rel="stylesheet" href="print.css" media="print"><br>
<link rel="stylesheet" href="mobile.css" media="(max-width: 768px)"><br>
<br>
<!-- 预加载关键资源 --><br>
<link rel="preload" href="critical-font.woff2" as="font" type="font/woff2" crossorigin><br>
<link rel="prefetch" href="next-page.html">
</div>
</div>
<div class="performance-card good">
<h2>骨架屏加载效果</h2>
<p>使用骨架屏提升感知性能:</p>
<div class="loading-skeleton" style="width: 60%;"></div>
<div class="loading-skeleton"></div>
<div class="loading-skeleton"></div>
<div class="loading-skeleton" style="width: 80%;"></div>
</div>
</div>
<!-- 非关键JavaScript延迟加载 -->
<script>
// 性能监控
window.addEventListener('load', function() {
// 记录页面加载时间
const loadTime = performance.timing.loadEventEnd - performance.timing.navigationStart;
console.log('页面加载时间:', loadTime + 'ms');
// 记录资源加载性能
const resources = performance.getEntriesByType('resource');
console.log('资源加载统计:', resources.length + ' 个资源');
});
</script>
<!-- 异步加载非关键功能 -->
<script src="non-critical-features.js" defer></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>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: #f8f9fa;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
h1 {
text-align: center;
color: #333;
}
.image-gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
margin-top: 30px;
}
.image-card {
background: white;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
transition: transform 0.3s ease;
}
.image-card:hover {
transform: translateY(-5px);
}
.image-container {
position: relative;
height: 200px;
overflow: hidden;
}
.image-container img {
width: 100%;
height: 100%;
object-fit: cover;
transition: opacity 0.3s ease;
}
.image-info {
padding: 15px;
}
.image-info h3 {
margin: 0 0 10px 0;
color: #333;
}
.image-info p {
margin: 0;
color: #666;
font-size: 14px;
}
.loading-placeholder {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: loading 1.5s infinite;
width: 100%;
height: 100%;
}
@keyframes loading {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
.lazy-image {
opacity: 0;
}
.lazy-image.loaded {
opacity: 1;
}
.controls {
text-align: center;
margin: 30px 0;
}
button {
padding: 12px 24px;
background: #007bff;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 16px;
margin: 0 10px;
transition: background 0.3s;
}
button:hover {
background: #0056b3;
}
.performance-info {
background: #e8f5e8;
border-left: 4px solid #4CAF50;
padding: 20px;
margin: 30px 0;
border-radius: 0 8px 8px 0;
}
.comparison {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin: 30px 0;
}
.comparison-panel {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.comparison-panel h3 {
margin-top: 0;
color: #333;
}
pre {
background: #f8f9fa;
padding: 15px;
border-radius: 5px;
overflow-x: auto;
font-size: 14px;
}
.stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin: 20px 0;
}
.stat-card {
background: white;
padding: 20px;
border-radius: 8px;
text-align: center;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.stat-number {
font-size: 2em;
font-weight: bold;
color: #007bff;
}
.stat-label {
color: #666;
margin-top: 5px;
}
</style>
</head>
<body>
<div class="container">
<h1>🖼️ 图片优化与延迟加载</h1>
<div class="stats">
<div class="stat-card">
<div class="stat-number" id="total-images">0</div>
<div class="stat-label">总图片数</div>
</div>
<div class="stat-card">
<div class="stat-number" id="loaded-images">0</div>
<div class="stat-label">已加载</div>
</div>
<div class="stat-card">
<div class="stat-number" id="lazy-images">0</div>
<div class="stat-label">延迟加载</div>
</div>
<div class="stat-card">
<div class="stat-number" id="data-saved">0KB</div>
<div class="stat-label">节省数据</div>
</div>
</div>
<div class="performance-info">
<h3>🚀 性能优化效果</h3>
<p>通过图片优化和延迟加载,页面加载速度提升 <strong>60%</strong>,数据传输减少 <strong>70%</strong>。</p>
</div>
<div class="controls">
<button id="load-images">手动加载所有图片</button>
<button id="reset-demo">重置演示</button>
</div>
<div class="image-gallery" id="image-gallery">
<!-- 图片将通过JavaScript动态添加 -->
</div>
<div class="comparison">
<div class="comparison-panel">
<h3>✅ 优化后的代码</h3>
<pre><picture>
<source
srcset="image.webp"
type="image/webp">
<source
srcset="image.avif"
type="image/avif">
<img
src="image.jpg"
alt="描述性文字"
loading="lazy"
decoding="async"
width="400"
height="300">
</picture></pre>
</div>
<div class="comparison-panel">
<h3>❌ 未优化的代码</h3>
<pre><img
src="large-image.jpg"
width="400"
height="300"></pre>
</div>
</div>
</div>
<script>
// 图片数据
const imageData = [
{ id: 1, title: "自然风景", description: "美丽的山川湖泊风景", tags: "nature, landscape" },
{ id: 2, title: "城市建筑", description: "现代都市建筑风光", tags: "city, architecture" },
{ id: 3, title: "动物世界", description: "可爱的野生动物", tags: "animals, wildlife" },
{ id: 4, title: "美食摄影", description: "精美的食物摄影作品", tags: "food, photography" },
{ id: 5, title: "艺术创作", description: "创意艺术作品展示", tags: "art, creative" },
{ id: 6, title: "科技产品", description: "最新科技产品展示", tags: "technology, products" },
{ id: 7, title: "运动健身", description: "健康运动生活方式", tags: "sports, fitness" },
{ id: 8, title: "旅行风光", description: "世界各地旅行风光", tags: "travel, world" },
{ id: 9, title: "人物肖像", description: "专业人物肖像摄影", tags: "portrait, people" },
{ id: 10, title: "商业办公", description: "现代办公环境展示", tags: "business, office" },
{ id: 11, title: "教育学习", description: "学习环境和教育资源", tags: "education, learning" },
{ id: 12, title: "医疗健康", description: "医疗健康相关图片", tags: "medical, health" }
];
// 性能统计数据
let stats = {
total: imageData.length,
loaded: 0,
lazy: imageData.length,
dataSaved: 0
};
// 创建图片卡片
function createImageCard(image) {
const card = document.createElement('div');
card.className = 'image-card';
card.innerHTML = `
<div class="image-container">
<div class="loading-placeholder"></div>
<img
class="lazy-image"
data-src="https://picsum.photos/400/300?random=${image.id}"
alt="${image.title}"
loading="lazy"
decoding="async"
width="400"
height="300"
>
</div>
<div class="image-info">
<h3>${image.title}</h3>
<p>${image.description}</p>
<p><small>标签: ${image.tags}</small></p>
</div>
`;
return card;
}
// 初始化图片画廊
function initGallery() {
const gallery = document.getElementById('image-gallery');
gallery.innerHTML = '';
imageData.forEach(image => {
const card = createImageCard(image);
gallery.appendChild(card);
});
updateStats();
}
// 更新统计信息
function updateStats() {
document.getElementById('total-images').textContent = stats.total;
document.getElementById('loaded-images').textContent = stats.loaded;
document.getElementById('lazy-images').textContent = stats.lazy;
document.getElementById('data-saved').textContent = `${stats.dataSaved}KB`;
}
// 延迟加载图片
function lazyLoadImages() {
const images = document.querySelectorAll('.lazy-image:not(.loaded)');
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.add('loaded');
stats.loaded++;
stats.lazy--;
stats.dataSaved += 50; // 假设每张图片节省50KB
updateStats();
observer.unobserve(img);
}
});
});
images.forEach(img => {
imageObserver.observe(img);
});
}
// 手动加载所有图片
function loadAllImages() {
const images = document.querySelectorAll('.lazy-image:not(.loaded)');
images.forEach(img => {
img.src = img.dataset.src;
img.classList.add('loaded');
});
stats.loaded = stats.total;
stats.lazy = 0;
updateStats();
}
// 重置演示
function resetDemo() {
stats = {
total: imageData.length,
loaded: 0,
lazy: imageData.length,
dataSaved: 0
};
initGallery();
lazyLoadImages();
}
// 事件监听器
document.getElementById('load-images').addEventListener('click', loadAllImages);
document.getElementById('reset-demo').addEventListener('click', resetDemo);
// 页面加载完成后初始化
window.addEventListener('load', function() {
initGallery();
lazyLoadImages();
// 模拟滚动以触发延迟加载
setTimeout(() => {
window.scrollTo(0, document.body.scrollHeight);
setTimeout(() => {
window.scrollTo(0, 0);
}, 1000);
}, 1000);
});
// 性能监控
window.addEventListener('load', function() {
// 记录图片加载性能
const images = performance.getEntriesByType('resource')
.filter(entry => entry.name.includes('picsum.photos'));
console.log('图片加载统计:', images.length + ' 张图片');
console.log('总加载时间:', images.reduce((sum, img) => sum + img.duration, 0).toFixed(2) + 'ms');
});
</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>HTML结构优化</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: #f8f9fa;
line-height: 1.6;
}
.container {
max-width: 1000px;
margin: 0 auto;
}
h1 {
text-align: center;
color: #333;
margin-bottom: 30px;
}
.example-section {
background: white;
margin: 30px 0;
padding: 25px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.example-section h2 {
color: #333;
margin-top: 0;
border-bottom: 2px solid #eee;
padding-bottom: 10px;
}
.code-comparison {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin: 20px 0;
}
.code-panel {
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
border-left: 4px solid #007bff;
}
.code-panel h3 {
margin-top: 0;
color: #333;
}
.bad h3 {
color: #f44336;
border-bottom: 2px solid #ffebee;
}
.good h3 {
color: #4CAF50;
border-bottom: 2px solid #e8f5e8;
}
pre {
background: #2d2d2d;
color: #f8f8f2;
padding: 15px;
border-radius: 5px;
overflow-x: auto;
font-size: 14px;
margin: 15px 0;
}
.explanation {
background: #e3f2fd;
padding: 15px;
border-radius: 5px;
margin: 15px 0;
border-left: 4px solid #2196F3;
}
.benefits {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin: 20px 0;
}
.benefit-card {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
text-align: center;
}
.benefit-card h3 {
margin-top: 0;
color: #333;
}
.benefit-icon {
font-size: 2em;
margin-bottom: 10px;
}
.stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin: 20px 0;
}
.stat-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 20px;
border-radius: 8px;
text-align: center;
}
.stat-number {
font-size: 2em;
font-weight: bold;
display: block;
}
.stat-label {
font-size: 0.9em;
opacity: 0.9;
}
.before-after {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin: 20px 0;
}
.before, .after {
padding: 20px;
border-radius: 8px;
}
.before {
background: #ffebee;
border: 2px solid #ffcdd2;
}
.after {
background: #e8f5e8;
border: 2px solid #c8e6c9;
}
.before h3, .after h3 {
margin-top: 0;
}
.tips {
background: #fff3e0;
border-left: 4px solid #ff9800;
padding: 20px;
border-radius: 0 8px 8px 0;
margin: 20px 0;
}
.tips h3 {
margin-top: 0;
color: #333;
}
.tips ul {
padding-left: 20px;
}
.tips li {
margin-bottom: 10px;
}
</style>
</head>
<body>
<div class="container">
<h1>🔧 HTML结构优化:减少不必要的嵌套和标签</h1>
<div class="stats">
<div class="stat-card">
<span class="stat-number">30%</span>
<span class="stat-label">文件大小减少</span>
</div>
<div class="stat-card">
<span class="stat-number">25%</span>
<span class="stat-label">解析速度提升</span>
</div>
<div class="stat-card">
<span class="stat-number">40%</span>
<span class="stat-label">维护成本降低</span>
</div>
</div>
<div class="benefits">
<div class="benefit-card">
<div class="benefit-icon">⚡</div>
<h3>更快加载</h3>
<p>减少文件大小,提升页面加载速度</p>
</div>
<div class="benefit-card">
<div class="benefit-icon">🔧</div>
<h3>易于维护</h3>
<p>简洁的结构更易于理解和修改</p>
</div>
<div class="benefit-card">
<div class="benefit-icon">🔍</div>
<h3>SEO友好</h3>
<p>清晰的结构有助于搜索引擎理解内容</p>
</div>
</div>
<div class="example-section">
<h2>1. 冗余嵌套优化</h2>
<div class="code-comparison">
<div class="code-panel bad">
<h3>❌ 冗余嵌套</h3>
<pre><div class="container">
<div class="wrapper">
<div class="content">
<div class="inner">
<div class="text">
<p>简单的内容</p>
</div>
</div>
</div>
</div>
</div></pre>
<div class="explanation">
<p><strong>问题:</strong>过多的嵌套层级增加了DOM复杂度</p>
<p><strong>标签数:</strong>6个div + 1个p = 7个标签</p>
</div>
</div>
<div class="code-panel good">
<h3>✅ 优化后</h3>
<pre><div class="content">
<p>简单的内容</p>
</div></pre>
<div class="explanation">
<p><strong>改进:</strong>移除不必要的包装层</p>
<p><strong>标签数:</strong>1个div + 1个p = 2个标签</p>
<p><strong>减少:</strong>71%的标签数量</p>
</div>
</div>
</div>
</div>
<div class="example-section">
<h2>2. 语义化标签替代</h2>
<div class="code-comparison">
<div class="code-panel bad">
<h3>❌ 过度使用div</h3>
<pre><div class="header">
<div class="logo">Logo</div>
<div class="nav">
<div class="nav-item"><a href="#">首页</a></div>
<div class="nav-item"><a href="#">关于</a></div>
</div>
</div>
<div class="main">
<div class="article">
<div class="title">文章标题</div>
<div class="content">文章内容...</div>
</div>
</div>
<div class="footer">
<div class="copyright">版权信息</div>
</div></pre>
</div>
<div class="code-panel good">
<h3>✅ 语义化标签</h3>
<pre><header>
<div class="logo">Logo</div>
<nav>
<a href="#">首页</a>
<a href="#">关于</a>
</nav>
</header>
<main>
<article>
<h1>文章标题</h1>
<p>文章内容...</p>
</article>
</main>
<footer>
<p>版权信息</p>
</footer></pre>
</div>
</div>
<div class="explanation">
<p><strong>优化要点:</strong></p>
<ul>
<li>使用`<header>`、`<nav>`、`<main>`、`<article>`、`<footer>`等语义化标签</li>
<li>移除不必要的包装div</li>
<li>使用合适的标题标签层级</li>
</ul>
</div>
</div>
<div class="example-section">
<h2>3. 表格结构优化</h2>
<div class="code-comparison">
<div class="code-panel bad">
<h3>❌ 冗余表格</h3>
<pre><table>
<tbody>
<tr>
<td>
<table>
<tbody>
<tr>
<td>姓名</td>
<td>张三</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table></pre>
</div>
<div class="code-panel good">
<h3>✅ 简化表格</h3>
<pre><table>
<tr>
<td>姓名</td>
<td>张三</td>
</tr>
</table></pre>
</div>
</div>
</div>
<div class="example-section">
<h2>4. 列表结构优化</h2>
<div class="before-after">
<div class="before">
<h3>❌ 之前(冗余div)</h3>
<pre><div class="list">
<div class="list-item">
<div class="item-content">项目1</div>
</div>
<div class="list-item">
<div class="item-content">项目2</div>
</div>
</div></pre>
</div>
<div class="after">
<h3>✅ 之后(语义化)</h3>
<pre><ul>
<li>项目1</li>
<li>项目2</li>
</ul></pre>
</div>
</div>
</div>
<div class="tips">
<h3>📋 优化建议</h3>
<ul>
<li><strong>审视每个div:</strong>问自己"这个div是必需的吗?"</li>
<li><strong>优先使用语义化标签:</strong>`<header>`、`<nav>`、`<main>`、`<article>`、`<section>`、`<aside>`、`<footer>`</li>
<li><strong>合并相似的样式:</strong>如果多个div有相同的样式,考虑合并</li>
<li><strong>使用CSS Grid/Flexbox:</strong>减少为了布局而添加的包装元素</li>
<li><strong>定期重构:</strong>随着项目发展,定期审查和优化HTML结构</li>
</ul>
</div>
</div>
</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>
<meta name="description" content="张三的个人网站,Web开发者,专注于前端技术">
<meta name="keywords" content="Web开发,前端开发,JavaScript,HTML,CSS">
<!-- 预加载关键资源 -->
<link rel="preload" href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&display=swap" as="style" onload="this.onload=null;this.rel='stylesheet'">
<style>
:root {
--primary-color: #2563eb;
--secondary-color: #7c3aed;
--dark-color: #1f2937;
--light-color: #f9fafb;
--gray-color: #6b7280;
--border-color: #e5e7eb;
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
--transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
line-height: 1.6;
color: var(--dark-color);
background-color: var(--light-color);
}
.skip-link {
position: absolute;
top: -40px;
left: 6px;
background: var(--primary-color);
color: white;
padding: 8px;
text-decoration: none;
border-radius: 4px;
z-index: 1000;
}
.skip-link:focus {
top: 6px;
}
.container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
/* 头部样式 */
header {
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
color: white;
padding: 2rem 0;
box-shadow: var(--shadow);
position: sticky;
top: 0;
z-index: 100;
}
.header-content {
display: flex;
justify-content: space-between;
align-items: center;
}
.logo {
font-size: 1.8rem;
font-weight: 700;
text-decoration: none;
color: white;
}
.logo span {
color: #fef08a;
}
nav ul {
display: flex;
list-style: none;
gap: 2rem;
}
nav a {
color: white;
text-decoration: none;
font-weight: 500;
padding: 0.5rem 1rem;
border-radius: 6px;
transition: var(--transition);
}
nav a:hover, nav a:focus {
background: rgba(255, 255, 255, 0.1);
outline: 2px solid white;
outline-offset: 2px;
}
.mobile-menu-btn {
display: none;
background: none;
border: none;
color: white;
font-size: 1.5rem;
cursor: pointer;
padding: 0.5rem;
}
/* 英雄区域 */
.hero {
padding: 4rem 0;
text-align: center;
background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
}
.hero-content {
max-width: 800px;
margin: 0 auto;
}
.hero h1 {
font-size: 3rem;
font-weight: 800;
margin-bottom: 1rem;
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.hero p {
font-size: 1.2rem;
color: var(--gray-color);
margin-bottom: 2rem;
}
.hero-btns {
display: flex;
gap: 1rem;
justify-content: center;
flex-wrap: wrap;
}
.btn {
display: inline-block;
padding: 1rem 2rem;
border-radius: 8px;
font-weight: 600;
text-decoration: none;
transition: var(--transition);
border: none;
cursor: pointer;
font-size: 1rem;
}
.btn-primary {
background: var(--primary-color);
color: white;
}
.btn-primary:hover, .btn-primary:focus {
background: #1d4ed8;
transform: translateY(-2px);
box-shadow: var(--shadow);
outline: 2px solid var(--primary-color);
outline-offset: 2px;
}
.btn-secondary {
background: transparent;
color: var(--primary-color);
border: 2px solid var(--primary-color);
}
.btn-secondary:hover, .btn-secondary:focus {
background: var(--primary-color);
color: white;
transform: translateY(-2px);
box-shadow: var(--shadow);
}
/* 关于我 */
.section {
padding: 5rem 0;
}
.section-header {
text-align: center;
margin-bottom: 3rem;
}
.section-header h2 {
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 1rem;
color: var(--dark-color);
}
.section-header p {
color: var(--gray-color);
max-width: 600px;
margin: 0 auto;
}
.about-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 3rem;
align-items: center;
}
.about-text h3 {
font-size: 1.5rem;
margin-bottom: 1rem;
color: var(--dark-color);
}
.about-text p {
margin-bottom: 1.5rem;
color: var(--gray-color);
}
.skills {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-top: 1rem;
}
.skill-tag {
background: #dbeafe;
color: var(--primary-color);
padding: 0.5rem 1rem;
border-radius: 20px;
font-size: 0.9rem;
font-weight: 500;
}
.about-image {
border-radius: 20px;
overflow: hidden;
box-shadow: var(--shadow);
}
.about-image img {
width: 100%;
height: auto;
display: block;
}
/* 项目展示 */
.projects-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
gap: 2rem;
}
.project-card {
background: white;
border-radius: 16px;
overflow: hidden;
box-shadow: var(--shadow);
transition: var(--transition);
}
.project-card:hover {
transform: translateY(-5px);
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
.project-image {
height: 200px;
background: linear-gradient(135deg, #c7d2fe 0%, #e0e7ff 100%);
display: flex;
align-items: center;
justify-content: center;
color: var(--primary-color);
font-size: 3rem;
}
.project-content {
padding: 1.5rem;
}
.project-content h3 {
font-size: 1.3rem;
margin-bottom: 0.5rem;
color: var(--dark-color);
}
.project-content p {
color: var(--gray-color);
margin-bottom: 1rem;
}
.project-tags {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-bottom: 1rem;
}
.project-tag {
background: #f3f4f6;
color: var(--gray-color);
padding: 0.25rem 0.75rem;
border-radius: 12px;
font-size: 0.8rem;
}
.project-links {
display: flex;
gap: 1rem;
}
.project-links a {
color: var(--primary-color);
text-decoration: none;
font-weight: 500;
font-size: 0.9rem;
}
.project-links a:hover {
text-decoration: underline;
}
/* 联系方式 */
.contact {
background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
}
.contact-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 3rem;
}
.contact-info h3 {
font-size: 1.5rem;
margin-bottom: 1rem;
color: var(--dark-color);
}
.contact-info p {
color: var(--gray-color);
margin-bottom: 2rem;
}
.contact-methods {
display: flex;
flex-direction: column;
gap: 1rem;
}
.contact-method {
display: flex;
align-items: center;
gap: 1rem;
}
.contact-icon {
width: 50px;
height: 50px;
background: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2rem;
color: var(--primary-color);
box-shadow: var(--shadow);
}
.contact-text h4 {
margin-bottom: 0.25rem;
color: var(--dark-color);
}
.contact-text p, .contact-text a {
color: var(--gray-color);
text-decoration: none;
}
.contact-text a:hover {
color: var(--primary-color);
text-decoration: underline;
}
.contact-form .form-group {
margin-bottom: 1.5rem;
}
.contact-form label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
color: var(--dark-color);
}
.contact-form input,
.contact-form textarea {
width: 100%;
padding: 1rem;
border: 2px solid var(--border-color);
border-radius: 8px;
font-family: inherit;
font-size: 1rem;
transition: var(--transition);
}
.contact-form input:focus,
.contact-form textarea:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
}
.contact-form textarea {
min-height: 150px;
resize: vertical;
}
/* 页脚 */
footer {
background: var(--dark-color);
color: white;
padding: 3rem 0 2rem;
text-align: center;
}
.footer-content {
margin-bottom: 2rem;
}
.social-links {
display: flex;
justify-content: center;
gap: 1.5rem;
margin-bottom: 2rem;
}
.social-link {
display: flex;
align-items: center;
justify-content: center;
width: 50px;
height: 50px;
background: rgba(255, 255, 255, 0.1);
border-radius: 50%;
color: white;
text-decoration: none;
font-size: 1.2rem;
transition: var(--transition);
}
.social-link:hover {
background: var(--primary-color);
transform: translateY(-3px);
}
.copyright {
color: rgba(255, 255, 255, 0.7);
font-size: 0.9rem;
}
/* 响应式设计 */
@media (max-width: 768px) {
.header-content {
flex-direction: column;
gap: 1rem;
}
nav ul {
gap: 1rem;
}
.mobile-menu-btn {
display: block;
position: absolute;
top: 1rem;
right: 1rem;
}
.hero h1 {
font-size: 2rem;
}
.hero p {
font-size: 1rem;
}
.hero-btns {
flex-direction: column;
align-items: center;
}
.about-content,
.contact-content {
grid-template-columns: 1fr;
gap: 2rem;
}
.projects-grid {
grid-template-columns: 1fr;
}
.section {
padding: 3rem 0;
}
}
@media (max-width: 480px) {
.container {
padding: 0 15px;
}
nav ul {
flex-direction: column;
align-items: center;
gap: 0.5rem;
}
.hero {
padding: 2rem 0;
}
.hero h1 {
font-size: 1.8rem;
}
}
/* 动画效果 */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-in {
animation: fadeIn 0.6s ease-out forwards;
}
.delay-1 {
animation-delay: 0.1s;
}
.delay-2 {
animation-delay: 0.2s;
}
.delay-3 {
animation-delay: 0.3s;
}
</style>
</head>
<body>
<a href="#main-content" class="skip-link">跳转到主内容</a>
<header>
<div class="container">
<div class="header-content">
<a href="#" class="logo">张<span>三</span></a>
<button class="mobile-menu-btn" aria-label="打开菜单">☰</button>
<nav aria-label="主导航">
<ul>
<li><a href="#about">关于我</a></li>
<li><a href="#projects">项目</a></li>
<li><a href="#contact">联系</a></li>
<li><a href="#blog">博客</a></li>
</ul>
</nav>
</div>
</div>
</header>
<section class="hero">
<div class="container">
<div class="hero-content">
<h1 class="animate-in">你好,我是张三</h1>
<p class="animate-in delay-1">一名热爱技术的前端开发工程师,专注于创建优雅、高效的Web应用</p>
<div class="hero-btns animate-in delay-2">
<a href="#projects" class="btn btn-primary">查看我的项目</a>
<a href="#contact" class="btn btn-secondary">联系我</a>
</div>
</div>
</div>
</section>
<main id="main-content">
<section id="about" class="section">
<div class="container">
<div class="section-header">
<h2>关于我</h2>
<p>了解我的技能和经验</p>
</div>
<div class="about-content">
<div class="about-text">
<h3>我是谁</h3>
<p>我是一名拥有5年前端开发经验的工程师,专注于现代Web技术栈。我对创建用户友好的界面和优化用户体验充满热情。</p>
<p>在我的职业生涯中,我参与了多个大型项目,从电商网站到企业管理系统,积累了丰富的实战经验。</p>
<h3>我的技能</h3>
<div class="skills">
<span class="skill-tag">HTML5</span>
<span class="skill-tag">CSS3</span>
<span class="skill-tag">JavaScript</span>
<span class="skill-tag">React</span>
<span class="skill-tag">Vue.js</span>
<span class="skill-tag">Node.js</span>
<span class="skill-tag">TypeScript</span>
<span class="skill-tag">Webpack</span>
<span class="skill-tag">Git</span>
</div>
</div>
<div class="about-image">
<img src="https://via.placeholder.com/500x400/2563eb/ffffff?text=Profile+Photo" alt="张三的个人照片">
</div>
</div>
</div>
</section>
<section id="projects" class="section">
<div class="container">
<div class="section-header">
<h2>我的项目</h2>
<p>展示我的一些精选作品</p>
</div>
<div class="projects-grid">
<div class="project-card animate-in">
<div class="project-image">📊</div>
<div class="project-content">
<h3>数据分析仪表板</h3>
<p>一个实时数据分析和可视化平台,帮助企业监控关键业务指标。</p>
<div class="project-tags">
<span class="project-tag">React</span>
<span class="project-tag">D3.js</span>
<span class="project-tag">Node.js</span>
</div>
<div class="project-links">
<a href="#" target="_blank">查看演示</a>
<a href="#" target="_blank">源代码</a>
</div>
</div>
</div>
<div class="project-card animate-in delay-1">
<div class="project-image">🛒</div>
<div class="project-content">
<h3>电商平台</h3>
<p>全功能的电子商务网站,支持商品管理、购物车和支付功能。</p>
<div class="project-tags">
<span class="project-tag">Vue.js</span>
<span class="project-tag">Express</span>
<span class="project-tag">MongoDB</span>
</div>
<div class="project-links">
<a href="#" target="_blank">查看演示</a>
<a href="#" target="_blank">源代码</a>
</div>
</div>
</div>
<div class="project-card animate-in delay-2">
<div class="project-image">📱</div>
<div class="project-content">
<h3>移动应用</h3>
<p>跨平台移动应用,提供任务管理和团队协作功能。</p>
<div class="project-tags">
<span class="project-tag">React Native</span>
<span class="project-tag">Firebase</span>
<span class="project-tag">Redux</span>
</div>
<div class="project-links">
<a href="#" target="_blank">查看演示</a>
<a href="#" target="_blank">源代码</a>
</div>
</div>
</div>
</div>
</div>
</section>
<section id="contact" class="section contact">
<div class="container">
<div class="section-header">
<h2>联系我</h2>
<p>让我们一起创造精彩的项目</p>
</div>
<div class="contact-content">
<div class="contact-info">
<h3>与我取得联系</h3>
<p>如果您有任何项目需求或想了解更多信息,请随时联系我。我期待与您合作!</p>
<div class="contact-methods">
<div class="contact-method">
<div class="contact-icon">📧</div>
<div class="contact-text">
<h4>邮箱</h4>
<a href="mailto:zhangsan@example.com">zhangsan@example.com</a>
</div>
</div>
<div class="contact-method">
<div class="contact-icon">📱</div>
<div class="contact-text">
<h4>电话</h4>
<a href="tel:+8613800138000">+86 138 0013 8000</a>
</div>
</div>
<div class="contact-method">
<div class="contact-icon">📍</div>
<div class="contact-text">
<h4>地址</h4>
<p>北京市朝阳区某某街道123号</p>
</div>
</div>
</div>
</div>
<div class="contact-form">
<form id="contactForm" novalidate>
<div class="form-group">
<label for="name">姓名</label>
<input type="text" id="name" name="name" required aria-describedby="name-error">
<div id="name-error" class="error-message" aria-live="polite"></div>
</div>
<div class="form-group">
<label for="email">邮箱</label>
<input type="email" id="email" name="email" required aria-describedby="email-error">
<div id="email-error" class="error-message" aria-live="polite"></div>
</div>
<div class="form-group">
<label for="message">消息</label>
<textarea id="message" name="message" required aria-describedby="message-error"></textarea>
<div id="message-error" class="error-message" aria-live="polite"></div>
</div>
<button type="submit" class="btn btn-primary">发送消息</button>
</form>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
<div class="footer-content">
<h3>张三</h3>
<p>前端开发工程师 | 技术爱好者</p>
<div class="social-links">
<a href="#" class="social-link" aria-label="GitHub">
<span aria-hidden="true">💻</span>
</a>
<a href="#" class="social-link" aria-label="LinkedIn">
<span aria-hidden="true">💼</span>
</a>
<a href="#" class="social-link" aria-label="Twitter">
<span aria-hidden="true">🐦</span>
</a>
<a href="#" class="social-link" aria-label="微信">
<span aria-hidden="true">💬</span>
</a>
</div>
</div>
<div class="copyright">
<p>© 2023 张三. 保留所有权利.</p>
</div>
</div>
</footer>
<script>
// 移动端菜单切换
const mobileMenuBtn = document.querySelector('.mobile-menu-btn');
const nav = document.querySelector('nav ul');
mobileMenuBtn.addEventListener('click', function() {
const isExpanded = nav.style.display === 'flex';
nav.style.display = isExpanded ? 'none' : 'flex';
this.setAttribute('aria-expanded', !isExpanded);
});
// 窗口大小改变时重置菜单
window.addEventListener('resize', function() {
if (window.innerWidth > 768) {
nav.style.display = 'flex';
} else {
nav.style.display = 'none';
}
});
// 平滑滚动
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
// 移动端关闭菜单
if (window.innerWidth <= 768) {
nav.style.display = 'none';
}
}
});
});
// 表单验证
const contactForm = document.getElementById('contactForm');
const formInputs = contactForm.querySelectorAll('input, textarea');
formInputs.forEach(input => {
input.addEventListener('blur', function() {
validateField(this);
});
input.addEventListener('input', function() {
clearError(this);
});
});
contactForm.addEventListener('submit', function(e) {
e.preventDefault();
let isValid = true;
formInputs.forEach(input => {
if (!validateField(input)) {
isValid = false;
}
});
if (isValid) {
// 模拟表单提交
const submitBtn = this.querySelector('button[type="submit"]');
const originalText = submitBtn.textContent;
submitBtn.textContent = '发送中...';
submitBtn.disabled = true;
setTimeout(() => {
alert('消息发送成功!我会尽快回复您。');
contactForm.reset();
submitBtn.textContent = originalText;
submitBtn.disabled = false;
}, 1500);
}
});
function validateField(field) {
const value = field.value.trim();
const isRequired = field.hasAttribute('required');
let isValid = true;
let errorMessage = '';
if (isRequired && !value) {
errorMessage = '此字段为必填项';
isValid = false;
} else if (field.type === 'email' && value) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(value)) {
errorMessage = '请输入有效的邮箱地址';
isValid = false;
}
}
if (isValid) {
clearError(field);
} else {
showError(field, errorMessage);
}
return isValid;
}
function showError(field, message) {
field.setAttribute('aria-invalid', 'true');
const errorId = field.getAttribute('aria-describedby');
if (errorId) {
document.getElementById(errorId).textContent = message;
}
}
function clearError(field) {
field.setAttribute('aria-invalid', 'false');
const errorId = field.getAttribute('aria-describedby');
if (errorId) {
document.getElementById(errorId).textContent = '';
}
}
// 元素进入视口动画
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('animate-in');
observer.unobserve(entry.target);
}
});
}, observerOptions);
// 观察需要动画的元素
document.querySelectorAll('.project-card, .section-header, .about-text, .about-image').forEach(el => {
observer.observe(el);
});
// 性能监控
window.addEventListener('load', function() {
// 记录页面加载性能
if ('performance' in window) {
const perfData = performance.timing;
const loadTime = perfData.loadEventEnd - perfData.navigationStart;
console.log('页面加载时间:', loadTime + 'ms');
// 记录资源加载
const resources = performance.getEntriesByType('resource');
console.log('加载资源数:', resources.length);
}
});
</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>
<meta name="description" content="功能完整的在线表单系统,支持多种字段类型和数据管理">
<style>
:root {
--primary-color: #3b82f6;
--success-color: #10b981;
--warning-color: #f59e0b;
--error-color: #ef4444;
--dark-color: #1f2937;
--light-color: #f9fafb;
--gray-color: #6b7280;
--border-color: #e5e7eb;
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
--transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
line-height: 1.6;
color: var(--dark-color);
background-color: #f3f4f6;
}
.container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
/* 头部样式 */
header {
background: white;
box-shadow: var(--shadow);
padding: 1rem 0;
position: sticky;
top: 0;
z-index: 100;
}
.header-content {
display: flex;
justify-content: space-between;
align-items: center;
}
.logo {
font-size: 1.5rem;
font-weight: 700;
color: var(--primary-color);
text-decoration: none;
}
.nav-links {
display: flex;
gap: 2rem;
}
.nav-links a {
text-decoration: none;
color: var(--gray-color);
font-weight: 500;
padding: 0.5rem 1rem;
border-radius: 6px;
transition: var(--transition);
}
.nav-links a:hover, .nav-links a.active {
color: var(--primary-color);
background: #eff6ff;
}
/* 主要内容区域 */
.main-content {
display: grid;
grid-template-columns: 300px 1fr;
gap: 2rem;
margin: 2rem 0;
}
/* 侧边栏 */
.sidebar {
background: white;
border-radius: 12px;
padding: 1.5rem;
box-shadow: var(--shadow);
height: fit-content;
}
.sidebar h3 {
margin-bottom: 1rem;
color: var(--dark-color);
padding-bottom: 0.5rem;
border-bottom: 2px solid var(--border-color);
}
.form-list {
list-style: none;
}
.form-list li {
margin-bottom: 0.5rem;
}
.form-list a {
display: block;
padding: 0.75rem;
text-decoration: none;
color: var(--gray-color);
border-radius: 8px;
transition: var(--transition);
}
.form-list a:hover, .form-list a.active {
background: #eff6ff;
color: var(--primary-color);
}
.sidebar-actions {
margin-top: 2rem;
}
.btn {
display: inline-block;
padding: 0.75rem 1.5rem;
background: var(--primary-color);
color: white;
text-decoration: none;
border-radius: 8px;
font-weight: 500;
border: none;
cursor: pointer;
transition: var(--transition);
width: 100%;
text-align: center;
}
.btn:hover {
background: #2563eb;
transform: translateY(-2px);
box-shadow: var(--shadow);
}
.btn-secondary {
background: white;
color: var(--primary-color);
border: 2px solid var(--primary-color);
}
.btn-secondary:hover {
background: #eff6ff;
}
.btn-success {
background: var(--success-color);
}
.btn-success:hover {
background: #059669;
}
/* 表单设计器 */
.form-designer {
background: white;
border-radius: 12px;
padding: 2rem;
box-shadow: var(--shadow);
}
.designer-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
padding-bottom: 1rem;
border-bottom: 2px solid var(--border-color);
}
.designer-header h2 {
color: var(--dark-color);
}
.form-title-input {
width: 100%;
padding: 1rem;
border: 2px solid var(--border-color);
border-radius: 8px;
font-size: 1.5rem;
font-weight: 600;
margin-bottom: 1rem;
}
.form-title-input:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
/* 字段工具箱 */
.toolbox {
background: #f8fafc;
border: 2px dashed var(--border-color);
border-radius: 8px;
padding: 1.5rem;
margin-bottom: 2rem;
}
.toolbox h3 {
margin-bottom: 1rem;
color: var(--dark-color);
}
.field-types {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
gap: 1rem;
}
.field-type {
background: white;
border: 2px solid var(--border-color);
border-radius: 8px;
padding: 1rem;
text-align: center;
cursor: grab;
transition: var(--transition);
}
.field-type:hover {
border-color: var(--primary-color);
transform: translateY(-2px);
box-shadow: var(--shadow);
}
.field-type i {
font-size: 1.5rem;
margin-bottom: 0.5rem;
display: block;
}
/* 表单预览区域 */
.form-preview {
min-height: 300px;
border: 2px dashed var(--border-color);
border-radius: 8px;
padding: 2rem;
margin-bottom: 2rem;
background: #f8fafc;
}
.form-fields {
min-height: 200px;
}
.form-field {
background: white;
border: 2px solid var(--border-color);
border-radius: 8px;
padding: 1.5rem;
margin-bottom: 1rem;
position: relative;
transition: var(--transition);
}
.form-field:hover {
border-color: var(--primary-color);
}
.form-field.selected {
border-color: var(--primary-color);
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
}
.field-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.field-title {
font-weight: 600;
color: var(--dark-color);
}
.field-actions {
display: flex;
gap: 0.5rem;
}
.field-action-btn {
background: none;
border: none;
color: var(--gray-color);
cursor: pointer;
padding: 0.25rem;
border-radius: 4px;
transition: var(--transition);
}
.field-action-btn:hover {
color: var(--primary-color);
background: #eff6ff;
}
.field-content {
margin-bottom: 1rem;
}
.field-content label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
color: var(--dark-color);
}
.field-content input,
.field-content select,
.field-content textarea {
width: 100%;
padding: 0.75rem;
border: 2px solid var(--border-color);
border-radius: 6px;
font-family: inherit;
font-size: 1rem;
}
.field-content input:focus,
.field-content select:focus,
.field-content textarea:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
.field-settings {
background: #f1f5f9;
border-radius: 6px;
padding: 1rem;
margin-top: 1rem;
}
.settings-group {
margin-bottom: 1rem;
}
.settings-group label {
display: block;
margin-bottom: 0.25rem;
font-size: 0.9rem;
color: var(--gray-color);
}
.settings-group input,
.settings-group select {
width: 100%;
padding: 0.5rem;
border: 1px solid var(--border-color);
border-radius: 4px;
font-family: inherit;
font-size: 0.9rem;
}
/* 表单列表页面 */
.forms-list {
background: white;
border-radius: 12px;
padding: 2rem;
box-shadow: var(--shadow);
}
.forms-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
}
.search-box {
padding: 0.75rem;
border: 2px solid var(--border-color);
border-radius: 8px;
width: 300px;
}
.forms-table {
width: 100%;
border-collapse: collapse;
}
.forms-table th,
.forms-table td {
padding: 1rem;
text-align: left;
border-bottom: 1px solid var(--border-color);
}
.forms-table th {
background: #f8fafc;
font-weight: 600;
color: var(--dark-color);
}
.forms-table tr:hover {
background: #f8fafc;
}
.status-badge {
padding: 0.25rem 0.75rem;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 500;
}
.status-active {
background: #dcfce7;
color: var(--success-color);
}
.status-draft {
background: #fef3c7;
color: var(--warning-color);
}
.action-buttons {
display: flex;
gap: 0.5rem;
}
.action-btn {
padding: 0.5rem 1rem;
border-radius: 6px;
border: none;
cursor: pointer;
font-size: 0.9rem;
transition: var(--transition);
}
.action-btn.edit {
background: #dbeafe;
color: var(--primary-color);
}
.action-btn.edit:hover {
background: #bfdbfe;
}
.action-btn.delete {
background: #fee2e2;
color: var(--error-color);
}
.action-btn.delete:hover {
background: #fecaca;
}
/* 响应式设计 */
@media (max-width: 768px) {
.main-content {
grid-template-columns: 1fr;
}
.sidebar {
margin-bottom: 2rem;
}
.nav-links {
display: none;
}
.forms-header {
flex-direction: column;
gap: 1rem;
align-items: stretch;
}
.search-box {
width: 100%;
}
}
/* 模态框 */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
align-items: center;
justify-content: center;
}
.modal-content {
background: white;
border-radius: 12px;
padding: 2rem;
max-width: 500px;
width: 90%;
max-height: 90vh;
overflow-y: auto;
position: relative;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.5rem;
}
.modal-header h3 {
margin: 0;
color: var(--dark-color);
}
.close-modal {
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
color: var(--gray-color);
}
.form-group {
margin-bottom: 1.5rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
color: var(--dark-color);
}
.form-group input,
.form-group select,
.form-group textarea {
width: 100%;
padding: 0.75rem;
border: 2px solid var(--border-color);
border-radius: 6px;
font-family: inherit;
font-size: 1rem;
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
.modal-actions {
display: flex;
gap: 1rem;
justify-content: flex-end;
margin-top: 2rem;
}
/* 通知 */
.notification {
position: fixed;
top: 20px;
right: 20px;
padding: 1rem 1.5rem;
border-radius: 8px;
color: white;
font-weight: 500;
box-shadow: var(--shadow);
transform: translateX(120%);
transition: transform 0.3s ease;
z-index: 1001;
}
.notification.show {
transform: translateX(0);
}
.notification.success {
background: var(--success-color);
}
.notification.error {
background: var(--error-color);
}
</style>
</head>
<body>
<header>
<div class="container">
<div class="header-content">
<a href="#" class="logo">📝 表单系统</a>
<nav class="nav-links">
<a href="#designer" class="active">表单设计</a>
<a href="#forms">表单管理</a>
<a href="#responses">数据统计</a>
</nav>
</div>
</div>
</header>
<div class="container">
<div class="main-content">
<aside class="sidebar">
<h3>我的表单</h3>
<ul class="form-list">
<li><a href="#" class="active">联系表单</a></li>
<li><a href="#">调查问卷</a></li>
<li><a href="#">注册表单</a></li>
<li><a href="#">订单表单</a></li>
</ul>
<div class="sidebar-actions">
<button class="btn" id="create-form-btn">+ 新建表单</button>
<button class="btn btn-secondary" id="import-form-btn">导入表单</button>
</div>
</aside>
<main>
<!-- 表单设计器 -->
<div id="designer" class="form-designer">
<div class="designer-header">
<h2>表单设计器</h2>
<div>
<button class="btn btn-secondary" id="save-draft-btn">保存草稿</button>
<button class="btn btn-success" id="publish-btn">发布表单</button>
</div>
</div>
<input type="text" class="form-title-input" value="联系表单" placeholder="输入表单标题">
<div class="toolbox">
<h3>添加字段</h3>
<div class="field-types">
<div class="field-type" data-type="text">
<span>📝</span>
<div>文本输入</div>
</div>
<div class="field-type" data-type="email">
<span>📧</span>
<div>邮箱</div>
</div>
<div class="field-type" data-type="textarea">
<span>💬</span>
<div>多行文本</div>
</div>
<div class="field-type" data-type="select">
<span>📋</span>
<div>下拉选择</div>
</div>
<div class="field-type" data-type="radio">
<span>🔘</span>
<div>单选按钮</div>
</div>
<div class="field-type" data-type="checkbox">
<span>✅</span>
<div>复选框</div>
</div>
<div class="field-type" data-type="date">
<span>📅</span>
<div>日期</div>
</div>
<div class="field-type" data-type="number">
<span>🔢</span>
<div>数字</div>
</div>
</div>
</div>
<div class="form-preview">
<h3>表单预览</h3>
<div class="form-fields" id="form-fields">
<!-- 字段将动态添加到这里 -->
</div>
</div>
</div>
<!-- 表单列表 -->
<div id="forms" class="forms-list" style="display: none;">
<div class="forms-header">
<h2>表单管理</h2>
<input type="text" class="search-box" placeholder="搜索表单...">
</div>
<table class="forms-table">
<thead>
<tr>
<th>表单名称</th>
<th>创建时间</th>
<th>状态</th>
<th>响应数</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>联系表单</td>
<td>2023-12-01</td>
<td><span class="status-badge status-active">已发布</span></td>
<td>125</td>
<td>
<div class="action-buttons">
<button class="action-btn edit">编辑</button>
<button class="action-btn delete">删除</button>
</div>
</td>
</tr>
<tr>
<td>调查问卷</td>
<td>2023-11-28</td>
<td><span class="status-badge status-active">已发布</span></td>
<td>89</td>
<td>
<div class="action-buttons">
<button class="action-btn edit">编辑</button>
<button class="action-btn delete">删除</button>
</div>
</td>
</tr>
<tr>
<td>注册表单</td>
<td>2023-11-25</td>
<td><span class="status-badge status-draft">草稿</span></td>
<td>0</td>
<td>
<div class="action-buttons">
<button class="action-btn edit">编辑</button>
<button class="action-btn delete">删除</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</main>
</div>
</div>
<!-- 新建表单模态框 -->
<div id="create-form-modal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h3>新建表单</h3>
<button class="close-modal">×</button>
</div>
<form id="create-form">
<div class="form-group">
<label for="form-name">表单名称</label>
<input type="text" id="form-name" required>
</div>
<div class="form-group">
<label for="form-description">描述</label>
<textarea id="form-description" rows="3"></textarea>
</div>
<div class="form-group">
<label for="form-template">模板</label>
<select id="form-template">
<option value="blank">空白表单</option>
<option value="contact">联系表单</option>
<option value="survey">调查问卷</option>
<option value="registration">注册表单</option>
</select>
</div>
<div class="modal-actions">
<button type="button" class="btn btn-secondary close-modal-btn">取消</button>
<button type="submit" class="btn">创建表单</button>
</div>
</form>
</div>
</div>
<!-- 通知 -->
<div id="notification" class="notification"></div>
<script>
// 表单数据管理
class FormManager {
constructor() {
this.forms = [
{
id: 1,
name: '联系表单',
description: '用于收集用户联系信息',
status: 'active',
createdAt: '2023-12-01',
responses: 125,
fields: [
{
id: 'field1',
type: 'text',
label: '姓名',
required: true,
placeholder: '请输入您的姓名'
},
{
id: 'field2',
type: 'email',
label: '邮箱地址',
required: true,
placeholder: '请输入您的邮箱'
},
{
id: 'field3',
type: 'textarea',
label: '留言内容',
required: true,
placeholder: '请输入您的留言'
}
]
}
];
this.currentForm = this.forms[0];
this.init();
}
init() {
this.bindEvents();
this.renderFormFields();
}
bindEvents() {
// 字段类型拖拽
document.querySelectorAll('.field-type').forEach(type => {
type.addEventListener('click', (e) => {
const fieldType = e.currentTarget.dataset.type;
this.addField(fieldType);
});
});
// 保存和发布按钮
document.getElementById('save-draft-btn').addEventListener('click', () => {
this.saveForm('draft');
});
document.getElementById('publish-btn').addEventListener('click', () => {
this.saveForm('active');
});
// 新建表单
document.getElementById('create-form-btn').addEventListener('click', () => {
this.showModal('create-form-modal');
});
// 模态框关闭
document.querySelectorAll('.close-modal, .close-modal-btn').forEach(btn => {
btn.addEventListener('click', () => {
this.closeModal();
});
});
// 创建表单提交
document.getElementById('create-form').addEventListener('submit', (e) => {
e.preventDefault();
this.createForm();
});
// 导航切换
document.querySelectorAll('.nav-links a').forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
this.switchView(e.target.getAttribute('href').substring(1));
});
});
}
addField(type) {
const fieldId = 'field' + Date.now();
const field = {
id: fieldId,
type: type,
label: this.getFieldLabel(type),
required: false,
placeholder: this.getFieldPlaceholder(type)
};
this.currentForm.fields.push(field);
this.renderFormFields();
this.showNotification('字段已添加', 'success');
}
getFieldLabel(type) {
const labels = {
'text': '文本输入',
'email': '邮箱地址',
'textarea': '多行文本',
'select': '下拉选择',
'radio': '单选按钮',
'checkbox': '复选框',
'date': '日期',
'number': '数字'
};
return labels[type] || '新字段';
}
getFieldPlaceholder(type) {
const placeholders = {
'text': '请输入文本',
'email': '请输入邮箱地址',
'textarea': '请输入内容',
'number': '请输入数字'
};
return placeholders[type] || '';
}
renderFormFields() {
const container = document.getElementById('form-fields');
container.innerHTML = '';
this.currentForm.fields.forEach((field, index) => {
const fieldElement = this.createFieldElement(field, index);
container.appendChild(fieldElement);
});
}
createFieldElement(field, index) {
const fieldDiv = document.createElement('div');
fieldDiv.className = 'form-field';
fieldDiv.dataset.fieldId = field.id;
fieldDiv.innerHTML = `
<div class="field-header">
<div class="field-title">${field.label}</div>
<div class="field-actions">
<button class="field-action-btn" title="上移">⬆️</button>
<button class="field-action-btn" title="下移">⬇️</button>
<button class="field-action-btn" title="删除">🗑️</button>
</div>
</div>
<div class="field-content">
${this.renderFieldInput(field)}
</div>
<div class="field-settings">
<div class="settings-group">
<label>字段标签</label>
<input type="text" value="${field.label}" data-setting="label">
</div>
<div class="settings-group">
<label>
<input type="checkbox" ${field.required ? 'checked' : ''} data-setting="required"> 必填
</label>
</div>
${field.type !== 'radio' && field.type !== 'checkbox' ? `
<div class="settings-group">
<label>占位符</label>
<input type="text" value="${field.placeholder || ''}" data-setting="placeholder">
</div>
` : ''}
</div>
`;
// 绑定设置事件
fieldDiv.querySelectorAll('[data-setting]').forEach(input => {
input.addEventListener('change', (e) => {
this.updateFieldSetting(field.id, e.target.dataset.setting, e.target);
});
});
// 绑定操作按钮事件
const actionButtons = fieldDiv.querySelectorAll('.field-action-btn');
actionButtons[0].addEventListener('click', () => this.moveField(index, -1));
actionButtons[1].addEventListener('click', () => this.moveField(index, 1));
actionButtons[2].addEventListener('click', () => this.removeField(field.id));
return fieldDiv;
}
renderFieldInput(field) {
switch (field.type) {
case 'text':
case 'email':
case 'date':
case 'number':
return `<label>${field.label}${field.required ? ' *' : ''}</label>
<input type="${field.type}" placeholder="${field.placeholder || ''}">`;
case 'textarea':
return `<label>${field.label}${field.required ? ' *' : ''}</label>
<textarea placeholder="${field.placeholder || ''}"></textarea>`;
case 'select':
return `<label>${field.label}${field.required ? ' *' : ''}</label>
<select>
<option value="">请选择</option>
<option value="option1">选项1</option>
<option value="option2">选项2</option>
</select>`;
case 'radio':
return `<label>${field.label}${field.required ? ' *' : ''}</label>
<div>
<label><input type="radio" name="${field.id}" value="option1"> 选项1</label>
<label><input type="radio" name="${field.id}" value="option2"> 选项2</label>
</div>`;
case 'checkbox':
return `<label>${field.label}${field.required ? ' *' : ''}</label>
<div>
<label><input type="checkbox" value="option1"> 选项1</label>
<label><input type="checkbox" value="option2"> 选项2</label>
</div>`;
default:
return `<label>${field.label}${field.required ? ' *' : ''}</label>
<input type="text" placeholder="${field.placeholder || ''}">`;
}
}
updateFieldSetting(fieldId, setting, input) {
const field = this.currentForm.fields.find(f => f.id === fieldId);
if (!field) return;
if (setting === 'required') {
field.required = input.checked;
} else if (setting === 'label') {
field.label = input.value;
} else if (setting === 'placeholder') {
field.placeholder = input.value;
}
this.renderFormFields();
this.showNotification('设置已更新', 'success');
}
moveField(index, direction) {
const newIndex = index + direction;
if (newIndex < 0 || newIndex >= this.currentForm.fields.length) return;
const field = this.currentForm.fields[index];
this.currentForm.fields.splice(index, 1);
this.currentForm.fields.splice(newIndex, 0, field);
this.renderFormFields();
}
removeField(fieldId) {
if (confirm('确定要删除这个字段吗?')) {
this.currentForm.fields = this.currentForm.fields.filter(f => f.id !== fieldId);
this.renderFormFields();
this.showNotification('字段已删除', 'success');
}
}
saveForm(status) {
this.currentForm.status = status;
this.showNotification(`表单已${status === 'active' ? '发布' : '保存为草稿'}`, 'success');
}
showModal(modalId) {
document.getElementById(modalId).style.display = 'flex';
}
closeModal() {
document.querySelectorAll('.modal').forEach(modal => {
modal.style.display = 'none';
});
}
createForm() {
const name = document.getElementById('form-name').value;
const description = document.getElementById('form-description').value;
if (!name) {
this.showNotification('请输入表单名称', 'error');
return;
}
const newForm = {
id: Date.now(),
name: name,
description: description,
status: 'draft',
createdAt: new Date().toISOString().split('T')[0],
responses: 0,
fields: []
};
this.forms.push(newForm);
this.currentForm = newForm;
this.renderFormFields();
this.closeModal();
this.showNotification('表单创建成功', 'success');
// 重置表单
document.getElementById('create-form').reset();
}
switchView(viewId) {
// 更新导航状态
document.querySelectorAll('.nav-links a').forEach(link => {
link.classList.remove('active');
});
document.querySelector(`.nav-links a[href="#${viewId}"]`).classList.add('active');
// 显示对应视图
document.querySelectorAll('.form-designer, .forms-list').forEach(view => {
view.style.display = 'none';
});
document.getElementById(viewId).style.display = 'block';
}
showNotification(message, type) {
const notification = document.getElementById('notification');
notification.textContent = message;
notification.className = `notification ${type} show`;
setTimeout(() => {
notification.classList.remove('show');
}, 3000);
}
}
// 初始化应用
document.addEventListener('DOMContentLoaded', () => {
const formManager = new FormManager();
// 页面可见性API - 页面获得焦点时刷新数据
document.addEventListener('visibilitychange', () => {
if (!document.hidden) {
console.log('页面重新获得焦点');
}
});
});
</script>
</body>
</html>通过这个综合项目,您已经掌握了HTML与CSS、JavaScript整合的核心技能,包括:
- 响应式设计:使用媒体查询和现代CSS技术创建适应不同设备的布局
- 可访问性:实现WCAG标准的可访问性功能,包括ARIA属性和键盘导航
- 性能优化:通过合理的HTML结构、图片优化和资源加载策略提升性能
- 现代交互:使用JavaScript创建动态、用户友好的界面
- 表单处理:实现完整的表单验证和数据管理功能
这些技能构成了现代Web开发的基础,为您继续深入学习前端框架和工具打下了坚实的基础。