HTML 全局属性完整指南
概述
HTML 全局属性是可以应用于所有 HTML 元素的通用属性,无论元素的类型如何。这些属性提供元素标识、样式控制、可访问性、国际化、交互行为等基础能力。理解并合理使用这些属性,可以显著提升页面的可维护性与用户体验。
核心特性
- 通用性:适用于几乎所有 HTML 元素
- 语义化:增强元素的含义和结构
- 可访问性:支持屏幕阅读器和键盘导航
- 国际化:支持多语言和多方向文本
- 交互性:提供拖拽、编辑、焦点控制等功能
适用场景
| 场景 | 推荐属性 | 说明 |
|---|---|---|
| 元素标识 | id, class | CSS 选择、JavaScript 操作、锚点 |
| 样式控制 | style, class | 内联样式、样式分组 |
| 可访问性 | tabindex, accesskey, lang, title | 键盘导航、屏幕阅读器支持 |
| 国际化 | lang, dir, translate | 多语言支持、RTL 文本 |
| 交互控制 | contenteditable, draggable, hidden | 编辑、拖拽、显示隐藏 |
| 数据存储 | data-* | 自定义数据、状态管理 |
| 移动端优化 | inputmode, enterkeyhint, autocapitalize | 虚拟键盘优化 |
| Web Components | slot, part, is | 组件开发、Shadow DOM |
文档导航
按功能分类
- 核心属性:
id,class,style,title - 国际化属性:
lang,dir,translate - 可访问性属性:
tabindex,accesskey - 交互控制属性:
hidden,contenteditable,spellcheck,draggable - 数据属性:
data-* - 移动端优化属性:
inputmode,enterkeyhint,autocapitalize,autofocus - Web Components 属性:
slot,part,exportparts,is - 高级特性属性:
inert,popover,nonce - 微数据属性:
itemscope,itemprop,itemtype,itemid,itemref
快速跳转
术语说明
| 术语 | 说明 |
|---|---|
| 全局属性 | 可以出现在几乎所有 HTML 标签上的属性,用于控制通用行为 |
| 布尔属性 | 存在即为 true,缺失为 false 的属性,例如 hidden、autofocus |
| 可访问性(a11y) | 让使用屏幕阅读器、仅键盘操作等用户也能顺利使用页面的相关技术与规范 |
| 焦点(focus) | 当前接收键盘输入的元素,一般会有明显的焦点样式轮廓 |
| 虚拟键盘 | 移动端或触屏设备上的软件键盘,会受 inputmode、enterkeyhint 等属性影响 |
| 国际化(i18n) | 让页面可以在多语言、多地区场景下正确显示和输入内容 |
| Shadow DOM | Web Components 的一部分,用于创建封装的 DOM 子树 |
| CSP | Content Security Policy,内容安全策略,用于防止 XSS 攻击 |
全局属性速查表
下表总结了 HTML 规范中常见的全局属性,你可以把它当作"速查表"。后续章节会对重要属性展开说明和示例。
| 属性 | 类型 | 说明 | 常见用途 | 分类 |
|---|---|---|---|---|
id | 字符串 | 文档中唯一标识元素 | CSS/JS 选择、锚点、表单关联 | 核心 |
class | 空格分隔字符串 | 为元素分配一个或多个类名 | 样式分组、状态标记 | 核心 |
style | CSS 声明字符串 | 定义元素内联样式 | 小范围覆盖、快速试验 | 核心 |
title | 文本 | 悬停时显示的提示文本 | 补充说明、缩写解释 | 核心 |
lang | BCP 47 语言代码 | 指定元素内容语言 | 多语言支持、拼写检查、SEO | 国际化 |
dir | ltr/rtl/auto | 指定文本方向 | RTL 语言支持 | 国际化 |
tabindex | 整数 | 控制键盘焦点顺序 | 键盘导航、可访问性 | 可访问性 |
accesskey | 字符串 | 为元素分配访问快捷键 | 高级用户键盘操作 | 可访问性 |
hidden | 布尔 | 元素当前不应显示 | 条件显示、占位内容 | 交互控制 |
contenteditable | 布尔/关键字 | 元素内容是否可编辑 | 富文本编辑器、可编辑区域 | 交互控制 |
spellcheck | 布尔 | 是否启用拼写检查 | 表单、评论区、富文本编辑 | 交互控制 |
draggable | 布尔/auto | 元素是否可拖拽 | 拖放界面、文件拖放上传 | 交互控制 |
data-* | 任意字符串 | 自定义数据属性 | JS 交互、状态存储 | 数据 |
translate | yes/no | 是否允许自动翻译内容 | 品牌名、代码片段保护 | 国际化 |
autocapitalize | 关键字 | 控制输入时自动大小写 | 移动端输入体验优化 | 移动端 |
autofocus | 布尔 | 页面加载时自动聚焦 | 表单初始输入框 | 移动端 |
inputmode | 关键字 | 建议虚拟键盘类型 | 数字/邮箱/搜索输入优化 | 移动端 |
enterkeyhint | 关键字 | 指定回车键上的提示文案 | 指示"搜索""发送"等操作 | 移动端 |
is | 字符串 | 绑定自定义元素构造器 | 扩展内建元素(Web Components) | Web Components |
nonce | 随机字符串 | 内容安全策略(CSP)使用的随机值 | 安全地允许特定内联脚本/样式 | 安全 |
inert | 布尔 | 将一棵子树标记为不可交互 | 模态框背景禁用、加载状态 | 高级特性 |
popover | 关键字/布尔 | 声明原生弹出层 | Tooltip、弹出菜单、信息卡片 | 高级特性 |
slot | 字符串 | 指定插槽名称 | Web Components 内容分发 | Web Components |
part | 空格分隔字符串 | 标记组件内部可样式化部件 | Shadow DOM 内部样式挂钩 | Web Components |
exportparts | 映射列表 | 将内部 part 暴露给外部 | 组合组件样式透传 | Web Components |
itemid 等 | 多个属性 | 定义微数据结构化信息 | SEO、搜索引擎富摘要 | 微数据 |
<a id="核心属性"></a>
<h4>041-global-attributes-reference.html</h4><!-- 来源:7-全局属性.md - 全局属性速查表与交互演示 -->
<!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>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
line-height: 1.6; color: #333; background: #f5f7fa; padding: 30px 20px;
}
.container { max-width: 1100px; margin: 0 auto; }
h1 { text-align: center; color: #222; margin-bottom: 8px; font-size: 30px; }
.subtitle { text-align: center; color: #666; margin-bottom: 28px; font-size: 14px; }
/* 分类标签 */
.category-tabs { display: flex; gap: 8px; margin-bottom: 24px; flex-wrap: wrap; justify-content: center; }
.cat-tab {
padding: 8px 18px; border-radius: 20px; border: none; cursor: pointer;
font-size: 13px; font-weight: 600; transition: all 0.2s; background: white; color: #666;
}
.cat-tab.active { background: #0066cc; color: white; }
.cat-tab:hover:not(.active) { background: #e8f4fd; }
/* 属性卡片网格 */
.attr-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 16px; }
.attr-card {
background: white; border-radius: 10px; padding: 20px;
box-shadow: 0 2px 8px rgba(0,0,0,0.06); border-left: 4px solid #0066cc;
transition: transform 0.2s;
}
.attr-card:hover { transform: translateY(-3px); box-shadow: 0 6px 20px rgba(0,0,0,0.1); }
.attr-name {
display: inline-block; background: #0066cc; color: white;
padding: 3px 10px; border-radius: 4px; font-family: 'Monaco', monospace;
font-size: 13px; font-weight: 600; margin-bottom: 8px;
}
.attr-type { float: right; font-size: 11px; color: #888; padding-top: 5px; }
.attr-desc { color: #555; font-size: 13px; line-height: 1.7; margin-bottom: 12px; }
.attr-example {
background: #f8f9fa; padding: 10px 14px; border-radius: 6px;
font-family: 'Monaco', monospace; font-size: 12px; overflow-x: auto;
color: #333; border-left: 2px solid #ddd;
}
/* 互动演示区 */
.demo-section {
background: white; border-radius: 12px; padding: 28px;
margin-top: 28px; box-shadow: 0 2px 12px rgba(0,0,0,0.06);
}
.demo-section h3 { color: #222; margin-bottom: 18px; font-size: 18px; }
/* contenteditable 演示 */
.editable-area {
min-height: 100px; border: 2px dashed #ccc; border-radius: 8px;
padding: 16px; outline: none; transition: border-color 0.2s;
font-size: 15px; line-height: 1.7;
}
.editable-area:focus { border-color: #0066cc; background: #f0f7ff; }
/* hidden 演示 */
.hidden-toggle { margin: 10px 0; }
[data-hidden] { opacity: 0.3; text-decoration: line-through; pointer-events: none; }
/* draggable 演示 */
.drag-list { list-style: none; display: flex; gap: 12px; flex-wrap: wrap; }
.drag-item {
padding: 12px 20px; background: linear-gradient(135deg, #667eea, #764ba2);
color: white; border-radius: 8px; cursor: grab; user-select: none;
transition: all 0.2s;
}
.drag-item:active { cursor: grabbing; transform: scale(1.05); }
.drop-zone {
min-height: 80px; border: 3px dashed #ccc; border-radius: 10px;
display: flex; align-items: center; justify-content: center;
margin-top: 16px; transition: all 0.2s; color: #888;
}
.drop-zone.drag-over { border-color: #28a745; background: #d4edda; color: #155724; }
/* spellcheck 演示 */
.spellcheck-area { width: 100%; height: 80px; padding: 12px; border: 1px solid #ddd; border-radius: 6px; resize: vertical; font-size: 14px; }
</style>
</head>
<body>
<div class="container">
<h1>🌐 HTML 全局属性</h1>
<p class="subtitle">适用于所有 HTML 元素的通用属性 — 速查表 + 交互演示</p>
<!-- 分类切换 -->
<div class="category-tabs">
<button class="cat-tab active" onclick="filterCards('all')">全部</button>
<button class="cat-tab" onclick="filterCards('core')">核心属性</button>
<button class="cat-tab" onclick="filterCards('i18n')">国际化</button>
<button class="cat-tab" onclick="filterCards('a11y')">可访问性</button>
<button class="cat-tab" onclick="filterCards('interaction')">交互控制</button>
<button class="cat-tab" onclick="filterCards('mobile')">移动端</button>
</div>
<!-- 属性卡片 -->
<div class="attr-grid" id="attrGrid">
<!-- id -->
<div class="attr-card" data-category="core">
<span class="attr-name">id</span><span class="attr-type">字符串</span>
<p class="attr-desc">文档内唯一标识符,用于 CSS 选择、JS 操作、锚点跳转和表单关联。</p>
<div class="attr-example"><header id="site-header"></header></div>
</div>
<!-- class -->
<div class="attr-card" data-category="core">
<span class="attr-name">class</span><span class="attr-type">空格分隔字符串</span>
<p class="attr-desc">分配一个或多个类名,用于 CSS 样式分组和 JS 批量操作。</p>
<div class="attr-example"><button class="btn btn-primary"></button></div>
</div>
<!-- style -->
<div class="attr-card" data-category="core">
<span class="attr-name">style</span><span class="attr-type">CSS 声明</span>
<p class="attr-desc">内联样式定义,优先级最高。建议仅在动态修改或覆盖时使用。</p>
<div class="attr-example"><div style="color:red;"></div></div>
</div>
<!-- title -->
<div class="attr-card" data-category="core">
<span class="attr-name">title</span><span class="attr-type">文本</span>
<p class="attr-desc">鼠标悬停时显示的提示文本,用于补充说明或缩写解释。</p>
<div class="attr-example"><abbr title="HyperText Markup Language">HTML</abbr></div>
</div>
<!-- lang -->
<div class="attr-card" data-category="i18n">
<span class="attr-name">lang</span><span class="attr-type">BCP 47 语言代码</span>
<p class="attr-desc">指定元素内容语言,影响拼写检查、搜索引擎、屏幕阅读器发音。</p>
<div class="attr-example"><html lang="zh-CN"><p lang="en">Hello</p></html></div>
</div>
<!-- dir -->
<div class="attr-card" data-category="i18n">
<span class="attr-name">dir</span><span class="attr-type">ltr/rtl/auto</span>
<p class="attr-desc">指定文本方向,支持从右到左(RTL)的语言如阿拉伯语、希伯来语。</p>
<div class="attr-example"><p dir="rtl">مرحبا بالعالم</p></div>
</div>
<!-- translate -->
<div class="attr-card" data-category="i18n">
<span class="attr-name">translate</span><span class="attr-type">yes/no</span>
<p class="attr-desc">控制是否允许浏览器自动翻译该元素内容。品牌名/代码应设为 no。</p>
<div class="attr-example"><code translate="no">&nbsp;</code></div>
</div>
<!-- tabindex -->
<div class="attr-card" data-category="a11y">
<span class="attr-name">tabindex</span><span class="attr-type">整数</span>
<p class="attr-desc">控制键盘焦点顺序。0=按文档顺序,-1=仅编程聚焦,正数=自定义顺序。</p>
<div class="attr-example"><div tabindex="0">可通过Tab聚焦</div></div>
</div>
<!-- accesskey -->
<div class="attr-card" data-category="a11y">
<span class="attr-name">accesskey</span><span class="attr-type">字符</span>
<p class="attr-desc">分配访问快捷键。Mac: Ctrl+Option+key,Win: Alt+key。</p>
<div class="attr-example"><button accesskey="s">保存(S)</button></div>
</div>
<!-- hidden -->
<div class="attr-card" data-category="interaction">
<span class="attr-name">hidden</span><span class="attr-type">布尔</span>
<p class="attr-desc">隐藏元素,等价于 display:none 但语义更明确。可用于条件显示内容。</p>
<div class="attr-example"><div hidden>加载中...</div></div>
</div>
<!-- contenteditable -->
<div class="attr-card" data-category="interaction">
<span class="attr-name">contenteditable</span><span class="attr-type">布尔/"plaintext-only"</span>
<p class="attr-desc">使元素内容可编辑,用于构建富文本编辑器。值:true/false/"inherit"。</p>
<div class="attr-example"><div contenteditable="true">可编辑</div></div>
</div>
<!-- draggable -->
<div class="attr-card" data-category="interaction">
<span class="attr-name">draggable</span><span class="attr-type">布尔/auto</span>
<p class="attr-desc">声明元素是否可拖拽。配合 Drag and Drop API 使用。</p>
<div class="attr-example"><div draggable="true">拖拽我</div></div>
</div>
<!-- spellcheck -->
<div class="attr-card" data-category="interaction">
<span class="attr-name">spellcheck</span><span class="attr-type">布尔</span>
<p class="attr-desc">是否启用拼写检查。适合评论框、富文本编辑器等场景。</p>
<div class="attr-example"><textarea spellcheck="true"></textarea></div>
</div>
<!-- inputmode -->
<div class="attr-card" data-category="mobile">
<span class="attr-name">inputmode</span><span class="attr-type">关键字</span>
<p class="attr-desc">建议移动端虚拟键盘类型:text/tel/email/numeric/decimal/search/url。</p>
<div class="attr-example"><input inputmode="numeric" /> → 数字键盘</div>
</div>
<!-- enterkeyhint -->
<div class="attr-card" data-category="mobile">
<span class="attr-name">enterkeyhint</span><span class="attr-type">关键字</span>
<p class="attr-desc">指定回车键提示文案:enter/go/done/search/next/send/previous。</p>
<div class="attr-example"><input enterkeyhint="search" /> → "搜索"</div>
</div>
<!-- autocapitalize -->
<div class="attr-card" data-category="mobile">
<span class="attr-name">autocapitalize</span><span class="attr-type">关键字</span>
<p class="attr-desc">控制移动端自动大小写:none/sentences/words/characters/off/on。</p>
<div class="attr-example"><input autocapitalize="words" /> → 首字母大写</div>
</div>
<!-- autofocus -->
<div class="attr-card" data-category="mobile">
<span class="attr-name">autofocus</span><span class="attr-type">布尔</span>
<p class="attr-desc">页面加载时自动获取焦点。每个页面只能有一个 autofocus 元素。</p>
<div class="attr-example"><input type="text" autofocus /></div>
</div>
<!-- inert -->
<div class="attr-card" data-category="a11y">
<span class="attr-name">inert</span><span class="attr-type">布尔</span>
<p class="attr-desc">将子树标记为不可交互。用于模态框背景禁用、加载状态等场景。</p>
<div class="attr-example"><div inert>禁用此区域所有交互</div></div>
</div>
<!-- data-* -->
<div class="attr-card" data-category="core">
<span class="attr-name">data-*</span><span class="attr-type">任意字符串</span>
<p class="attr-desc">自定义数据属性,通过 JS dataset API 访问。不渲染但可存储状态。</p>
<div class="attr-example"><div data-user-id="123" data-role="admin"></div></div>
</div>
</div>
<!-- 交互演示区域 -->
<div class="demo-section">
<h3>🎮 互动演示 — 尝试以下效果</h3>
<div style="display:grid; grid-template-columns: repeat(auto-fit,minmax(280px,1fr)); gap:20px;">
<!-- contenteditable 演示 -->
<div>
<h4 style="margin-bottom:8px;color:#555;">✏️ contenteditable — 点击编辑</h4>
<div class="editable-area" contenteditable="true">👋 你可以直接在这里输入文字,试试加粗或斜体!</div>
</div>
<!-- hidden 演示 -->
<div>
<h4 style="margin-bottom:8px;color:#555;">👁️ hidden — 切换显示</h4>
<button class="cat-tab" onclick="toggleHidden()">切换隐藏/显示</button>
<div class="hidden-toggle">
<p data-hidden>这是一段可以被隐藏的文字</p>
<p>这段文字始终可见</p>
</div>
</div>
<!-- draggable 演示 -->
<div>
<h4 style="margin-bottom:8px;color:#555;">🖱️ draggable — 拖拽到下方区域</h4>
<ul class="drag-list">
<li class="drag-item" draggable="true" id="item1">拖拽项 A</li>
<li class="drag-item" draggable="true" id="item2">拖拽项 B</li>
<li class="drag-item" draggable="true" id="item3">拖拽项 C</li>
</ul>
<div class="drop-zone" id="dropZone">📥 将上方项目拖到这里</div>
</div>
<!-- spellcheck 演示 -->
<div>
<h4 style="margin-bottom:8px;color:#555;">📝 spellcheck — 拼写检查</h4>
<textarea class="spellcheck-area" spellcheck="true" placeholder="在此输入英文文字测试拼写检查..."></textarea>
<small style="color:#888;font-size:11px;">注意:拼写错误会显示红色波浪线</small>
</div>
</div>
</div>
</div>
<script>
// 分类筛选
function filterCards(category) {
document.querySelectorAll('.cat-tab').forEach(t => t.classList.remove('active'))
event.target.classList.add('active')
const cards = document.querySelectorAll('.attr-card')
cards.forEach(card => {
if (category === 'all' || card.dataset.category === category) {
card.style.display = ''
} else {
card.style.display = 'none'
}
})
}
// hidden 切换
function toggleHidden() {
const el = document.querySelector('[data-hidden]')
el.hidden = !el.hidden
}
// 拖拽演示
const dropZone = document.getElementById('dropZone')
document.querySelectorAll('.drag-item').forEach(item => {
item.addEventListener('dragstart', e => e.dataTransfer.setData('text/plain', item.id))
})
dropZone.addEventListener('dragover', e => {
e.preventDefault()
dropZone.classList.add('drag-over')
})
dropZone.addEventListener('dragleave', () => dropZone.classList.remove('drag-over'))
dropZone.addEventListener('drop', e => {
e.preventDefault()
dropZone.classList.remove('drag-over')
const itemId = e.dataTransfer.getData('text/plain')
const item = document.getElementById(itemId)
if (item) {
dropZone.innerHTML = `✅ 已放置: ${item.textContent}`
dropZone.style.background = '#d4edda'
dropZone.style.color = '#155724'
}
})
</script>
</body>
</html>核心属性
<h4>043-class-id-style-core.html</h4><!DOCTYPE html>
<!--
来源:基础知识/7-全局属性.md
演示:class / id / style 三大核心属性实战
-->
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>class/id/style 核心属性实战</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, sans-serif; padding: 30px; background: #f0f2f5; }
h1 { color: #1a1a1a; margin-bottom: 25px; }
.demo-box { background: white; padding: 25px; margin-bottom: 20px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.08); }
h2 { color: #444; margin-bottom: 15px; font-size: 17px; border-bottom: 2px solid #eee; padding-bottom: 8px; }
/* class 演示样式 */
.btn { padding: 10px 20px; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; transition: all 0.2s; margin: 5px; }
.btn-primary { background: #3498db; color: white; }
.btn-primary:hover { background: #2980b9; }
.btn-success { background: #27ae60; color: white; }
.btn-success:hover { background: #219a52; }
.btn-danger { background: #e74c3c; color: white; }
.btn-danger:hover { background: #c0392b; }
.btn-warning { background: #f39c12; color: white; }
.btn-lg { padding: 14px 28px; font-size: 16px; }
.btn-sm { padding: 6px 12px; font-size: 12px; }
.btn-disabled { opacity: 0.5; cursor: not-allowed; }
.active { box-shadow: 0 0 0 3px rgba(52,152,219,0.5); transform: scale(1.05); }
.card { padding: 15px; border-radius: 8px; margin: 8px; display: inline-block; }
.bg-blue { background: #ebf5fb; border: 1px solid #aed6f1; }
.bg-green { background: #eafaf1; border: 1px solid #a9dfbf; }
.bg-red { background: #fdedec; border: 1px solid #fadbd8; }
.rounded { border-radius: 20px; }
.shadow { box-shadow: 0 4px 12px rgba(0,0,0,0.15); }
code { background: #f4f4f4; padding: 2px 6px; border-radius: 3px; font-size: 13px; color: #e74c3c; }
pre { background: #282c34; color: #abb2bf; padding: 15px; border-radius: 6px; overflow-x: auto; font-size: 13px; line-height: 1.6; }
.log { background: #f8f9fa; border-left: 3px solid #3498db; padding: 10px 15px; margin: 10px 0; font-family: monospace; font-size: 13px; }
#unique-element { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; border-radius: 10px; }
#anchor-target { padding: 40px; background: #fff3cd; border: 2px dashed #ffc107; border-radius: 8px; margin: 15px 0; }
</style>
</head>
<body>
<h1>🎯 class / id / style 三大核心属性实战</h1>
<!-- ==================== class 属性演示 ==================== -->
<div class="demo-box">
<h2>1. class 属性 — 多类名组合与动态切换</h2>
<p><strong>多类名组合示例:</strong></p>
<button class="btn btn-primary">主要按钮</button>
<button class="btn btn-success">成功按钮</button>
<button class="btn btn-danger">危险按钮</button>
<button class="btn btn-warning btn-lg">大号警告</button>
<button class="btn btn-primary btn-sm">小型按钮</button>
<button class="btn btn-primary btn-disabled">禁用状态</button>
<p style="margin-top:15px;"><strong>组合效果:</strong></p>
<div class="card bg-blue">基础卡片</div>
<div class="card bg-green rounded">圆角绿色卡片</div>
<div class="card bg-red shadow">红色阴影卡片</div>
<hr style="margin:15px 0;">
<button class="btn btn-primary" onclick="toggleClass()">切换 active 类</button>
<button class="btn btn-success" onclick="addClassListDemo()" classList 操作演示</button>
<pre id="class-log" class="log">// 点击按钮查看 class 操作</pre>
</div>
<!-- ==================== id 属性演示 ==================== -->
<div class="demo-box">
<h2>2. id 属性 — 唯一标识与锚点导航</h2>
<p><strong>锚点导航:</strong></p>
<nav style="margin-bottom:15px;">
<a href="#anchor-target" class="btn btn-primary btn-sm">跳转到目标区域 ↓</a>
<a href="#unique-element" class="btn btn-success btn-sm">跳转到唯一元素 ↓</a>
</nav>
<div id="unique-element">
<strong>id="unique-element"</strong><br>
这是页面中唯一的元素,通过 getElementById 快速定位
</div>
<div id="anchor-target">
<strong>id="anchor-target"</strong><br>
你已经通过锚点链接跳转到了这里!<br>
<small>URL 应该显示为 ...#anchor-target</small>
</div>
<hr style="margin:15px 0;">
<button class="btn btn-primary" onclick="demoIdApi()">演示 ID API</button>
<pre id="id-log" class="log">// 点击查看 ID 相关 API</pre>
</div>
<!-- ==================== style 属性演示 ==================== -->
<div class="demo-box">
<h2>3. style 内联样式 — 动态样式控制</h2>
<div id="style-demo" style="padding:20px; background:#f8f9fa; border-radius:8px; text-align:center; transition:all 0.5s;">
<p style="font-size:18px; margin-bottom:10px;">✨ 这段文字的样式可以通过下方按钮改变 ✨</p>
<p id="style-info">当前样式将在这里显示</p>
</div>
<div style="margin-top:15px;">
<button class="btn btn-primary" onclick="changeColor('#e74c3c')">红色</button>
<button class="btn btn-success" onclick="changeColor('#27ae60')">绿色</button>
<button class="btn" style="background:#9b59b6;color:white;" onclick="changeColor('#9b59b6')">紫色</button>
<button class="btn" style="background:#f39c12;color:white;" onclick="changeColor('#f39c12')">橙色</button>
<button class="btn btn-danger" onclick="changeSize(14)">缩小</button>
<button class="btn btn-success" onclick="changeSize(24)">放大</button>
<button class="btn btn-primary" onclick="toggleBorder()">切换边框</button>
<button class="btn" style="background:#34495e;color:white;" onclick="resetStyle()">重置</button>
</div>
<pre id="style-log" class="log">// 样式变化日志</pre>
</div>
<!-- ==================== 三者对比 ==================== -->
<div class="demo-box">
<h2>4. class vs id vs style 使用场景对比</h2>
<table style="width:100%;border-collapse:collapse;font-size:14px;">
<thead>
<tr style="background:#34495e;color:white;">
<th style="padding:10px;text-align:left;">属性</th>
<th style="padding:10px;text-align:left;">选择器语法</th>
<th style="padding:10px;text-align:left;">唯一性</th>
<th style="padding:10px;text-align:left;">典型用途</th>
</tr>
</thead>
<tbody>
<tr style="background:#ebf5fb;">
<td style="padding:10px;"><code>.class</code></td>
<td style="padding:10px;"><code>.classname</code></td>
<td style="padding:10px;">可复用(多个元素)</td>
<td style="padding:10px;">样式分组、状态标记、CSS 选择</td>
</tr>
<tr style="background:#fdedec;">
<td style="padding:10px;"><code>#id</code></td>
<td style="padding:10px;"><code>#idname</code></td>
<td style="padding:10px;">唯一(单个元素)</td>
<td style="padding:10px;">JS 定位、锚点跳转、表单关联</td>
</tr>
<tr style="background:#eafaf1;">
<td style="padding:10px;"><code>style</code></td>
<td style="padding:10px;">内联声明</td>
<td style="padding:10px;">仅当前元素</td>
<td style="padding:10px;">动态覆盖、临时调试、JS 控制</td>
</tr>
</tbody>
</table>
</div>
<script>
// ===== class 操作演示 =====
function toggleClass() {
const btn = event.target;
btn.classList.toggle('active');
const log = document.getElementById('class-log');
log.textContent = `
// toggleClass 结果:
btn.classList.contains('active') = ${btn.classList.contains('active')}
btn.className = "${btn.className}"
btn.classList.toString() = "${btn.classList.toString()}"
// classList 的优势:
// - 自动处理空格分隔
// - 提供 add/remove/toggle/contains/replace 方法
// - 返回 DOMTokenList 对象(类数组)`;
}
function addClassListDemo() {
const log = document.getElementById('class-log');
const demoClasses = ['btn', 'primary', 'rounded', 'shadow', 'active'];
log.textContent = `
// classList 完整 API 演示:
const list = element.classList;
// 基础方法
list.add('new-class') // 添加类名
list.remove('old-class') // 移除类名
list.toggle('active') // 切换类名(返回是否添加)
list.contains('btn') // 是否包含 → true/false
list.replace('a', 'b') // 替换类名
// 实用方法
list.length // 当前类名数量
list.value // 字符串形式
list.entries() // 迭代器
list.forEach(c => console.log(c))
// 当前示例中的类名组合:
${demoClasses.map((c,i) => ` [${i}] "${c}"`).join('\n')}
// 推荐用法:优先使用 classList 而非 className 字符串拼接`;
}
// ===== id 操作演示 =====
function demoIdApi() {
const log = document.getElementById('id-log');
const el = document.getElementById('unique-element');
log.textContent = `
// ===== ID 相关 API =====
// 1. 通过 ID 获取元素
const el = document.getElementById('unique-element');
// 返回:${el ? 'Element 对象 ✓' : 'null ✗'}
// 2. 通过 CSS 选择器获取
const el2 = document.querySelector('#unique-element');
// 返回:${el2 ? 'Element 对象 ✓' : 'null ✗'}
// 3. 获取/设置 ID
el.id // "${el.id}"
el.id = 'new-id'; // 修改 ID
// 4. 锚点跳转
location.hash = '#unique-element';
// 页面滚动到该元素位置
// 5. 表单关联(label for)
<label for="username"> ← 关联 → <input id="username">
// ⚠️ 注意事项:
// - 同一文档中 ID 必须唯一
// - ID 大小写敏感
// - 不能以数字开头(CSS 选择器限制)
// - 避免使用纯数字作为 ID`;
}
// ===== style 操作演示 =====
let hasBorder = false;
function changeColor(color) {
const el = document.getElementById('style-demo');
el.style.color = color;
updateStyleLog(`color: ${color}`);
}
function changeSize(size) {
const el = document.getElementById('style-demo');
const p = el.querySelector('p');
p.style.fontSize = size + 'px';
updateStyleLog(`fontSize: ${size}px`);
}
function toggleBorder() {
const el = document.getElementById('style-demo');
hasBorder = !hasBorder;
el.style.border = hasBorder ? '3px dashed #3498db' : 'none';
updateStyleLog(`border: ${hasBorder ? '3px dashed #3498db' : 'none'}`);
}
function resetStyle() {
const el = document.getElementById('style-demo');
el.removeAttribute('style');
el.setAttribute('style', 'padding:20px; background:#f8f9fa; border-radius:8px; text-align:center; transition:all 0.5s;');
el.querySelector('p').removeAttribute('style');
hasBorder = false;
updateStyleLog('样式已重置');
}
function updateStyleLog(action) {
const log = document.getElementById('style-log');
const now = new Date().toLocaleTimeString();
log.textContent = `[${now}] 执行:element.style.${action}\n` +
'// 读取当前内联样式:\n' +
`element.style.cssText = "${document.getElementById('style-demo').style.cssText}"\n\n` +
"// 注意:style 属性只能操作内联样式\n" +
"// 要获取计算后的最终样式,请使用 getComputedStyle()";
}
</script>
</body>
</html>id - 唯一标识符
id 属性为元素指定唯一的标识符。在整个文档中,每个 id 值应该是唯一的。
主要用途
- CSS 选择器:通过
#id选择器定位元素 - JavaScript 操作:使用
document.getElementById()获取元素 - 锚点链接:创建页面内跳转链接(如
#section1) - 表单关联:
<label>的for属性关联表单元素
规则与限制
| 规则 | 说明 | 示例 |
|---|---|---|
| 唯一性 | 文档中必须唯一 | ❌ 两个元素使用 id="header" |
| 大小写敏感 | id="Header" ≠ id="header" | ✅ id="mainHeader" |
| 无空格 | 不能包含空格字符 | ❌ id="main header" |
| 字符限制 | 几乎所有字符都允许,但建议只用字母、数字、连字符、下划线 | ✅ id="user-123" |
| 不能以数字开头(CSS) | 虽然 HTML 允许,但 CSS 选择器需要转义 | ⚠️ id="1header" 需写成 #\31 header |
JavaScript API
// 获取元素
const element = document.getElementById('myId');
// 检查 ID 是否存在
if (document.getElementById('myId')) {
console.log('元素存在');
}
// 设置 ID
element.id = 'newId';
// 通过 CSS 选择器获取
const element = document.querySelector('#myId');使用示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>id 属性示例</title>
<style>
/* CSS 选择器 */
#header {
background-color: #333;
color: white;
padding: 20px;
}
#main-content {
margin: 20px;
}
</style>
</head>
<body>
<!-- 页面头部 -->
<header id="header">
<h1>网站标题</h1>
</header>
<!-- 主要内容 -->
<main id="main-content">
<h2 id="section1">第一章节</h2>
<p>这是第一章节的内容。</p>
<h2 id="section2">第二章节</h2>
<p>这是第二章节的内容。</p>
</main>
<!-- 锚点导航 -->
<nav id="navigation">
<a href="#section1">跳转到第一章</a>
<a href="#section2">跳转到第二章</a>
</nav>
<!-- 表单关联 -->
<form>
<label for="username">用户名:</label>
<input type="text" id="username" name="username">
<label for="email">邮箱:</label>
<input type="email" id="email" name="email">
</form>
<script>
// JavaScript 操作
const header = document.getElementById('header');
header.style.borderBottom = '2px solid #666';
// 平滑滚动到锚点
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' });
}
});
});
</script>
</body>
</html>最佳实践
<!-- ✅ 推荐:语义化、有意义的 ID -->
<header id="site-header"></header>
<nav id="main-navigation"></nav>
<main id="content"></main>
<article id="article-123"></article>
<!-- ❌ 避免:无意义或自动生成的 ID -->
<div id="div1"></div>
<div id="container-wrapper-box"></div>
<div id="____"></div>class - 样式类名
class 属性为元素分配一个或多个类名,用于样式分组和 JavaScript 批量操作。
主要用途
- CSS 样式分组:为多个元素应用相同的样式
- JavaScript 批量操作:使用
document.getElementsByClassName()或querySelectorAll()选择元素 - 语义化标记:表示元素的类型或状态
特点
| 特点 | 说明 |
|---|---|
| 可重复 | 同一个类名可用于多个元素 |
| 多类名 | 一个元素可以有多个类名,用空格分隔 |
| 大小写敏感 | .Button ≠ .button |
| 语义化 | 建议使用有意义的名称(如 .error-message 而非 .red) |
JavaScript API
// 获取元素集合(HTMLCollection,动态)
const elements = document.getElementsByClassName('my-class');
// 获取元素集合(NodeList,静态)
const elements = document.querySelectorAll('.my-class');
// 操作类名
const element = document.querySelector('#myElement');
// 添加类名
element.classList.add('new-class');
// 移除类名
element.classList.remove('old-class');
// 切换类名
element.classList.toggle('active');
// 检查是否有某类名
if (element.classList.contains('active')) {
console.log('元素处于激活状态');
}
// 替换类名
element.classList.replace('old-class', 'new-class');
// 获取类名数量
console.log(element.classList.length);
// 获取所有类名
console.log(element.className); // 字符串
console.log(element.classList); // DOMTokenList命名规范
| 方法 | 说明 | 示例 |
|---|---|---|
| BEM | Block__Element--Modifier | .card__header--active |
| OOCSS | Object-Oriented CSS | .btn, .btn-primary |
| SMACSS | Scalable and Modular Architecture | .l-grid, .is-active |
| Utility-First | 原子化 CSS | .p-4, .text-center |
使用示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>class 属性示例</title>
<style>
/* 基础样式 */
.btn {
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
/* 修饰符样式 */
.btn-primary {
background-color: #007bff;
color: white;
}
.btn-secondary {
background-color: #6c757d;
color: white;
}
.btn-danger {
background-color: #dc3545;
color: white;
}
/* 状态样式 */
.btn.disabled {
opacity: 0.5;
cursor: not-allowed;
}
.btn.loading {
position: relative;
color: transparent;
}
.btn.loading::after {
content: '';
position: absolute;
width: 16px;
height: 16px;
border: 2px solid white;
border-radius: 50%;
border-top-color: transparent;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* 尺寸样式 */
.btn-sm {
padding: 5px 10px;
font-size: 12px;
}
.btn-lg {
padding: 15px 30px;
font-size: 18px;
}
</style>
</head>
<body>
<!-- 单个类名 -->
<p class="highlight">高亮文本</p>
<!-- 多个类名组合 -->
<button class="btn btn-primary">主要按钮</button>
<button class="btn btn-secondary">次要按钮</button>
<button class="btn btn-danger">危险按钮</button>
<!-- 状态类名 -->
<button class="btn btn-primary disabled">禁用按钮</button>
<button class="btn btn-primary loading">加载中</button>
<!-- 尺寸类名 -->
<button class="btn btn-primary btn-sm">小按钮</button>
<button class="btn btn-primary btn-lg">大按钮</button>
<script>
// 切换按钮状态
document.querySelectorAll('.btn').forEach(btn => {
btn.addEventListener('click', function() {
// 添加加载状态
this.classList.add('loading');
// 模拟异步操作
setTimeout(() => {
this.classList.remove('loading');
this.classList.add('disabled');
}, 2000);
});
});
// 批量操作
const allButtons = document.querySelectorAll('.btn');
console.log(`共有 ${allButtons.length} 个按钮`);
</script>
</body>
</html>style - 内联样式
style 属性用于直接在元素上定义内联 CSS 样式。
主要用途
- 快速样式应用:为单个元素快速添加样式
- 动态样式:通过 JavaScript 动态修改样式
- 覆盖外部样式:内联样式优先级最高
注意事项
不推荐大规模使用 style 属性,原因:
- 维护困难:样式分散在 HTML 中,难以统一管理
- 代码重复:相同样式需要在多个元素上重复定义
- 性能影响:内联样式无法被浏览器缓存
- 可访问性:不利于响应式设计和主题切换
最佳实践:将样式放在 <style> 标签或外部 CSS 文件中,仅在需要动态修改或覆盖特定样式时使用内联样式。
JavaScript API
const element = document.getElementById('myElement');
// 读取样式(注意:只能读取内联样式)
console.log(element.style.color);
console.log(element.style.backgroundColor);
// 设置样式
element.style.color = 'red';
element.style.backgroundColor = 'blue';
element.style.fontSize = '16px'; // 注意:驼峰命名
// 批量设置样式
element.style.cssText = 'color: red; background: blue; padding: 10px;';
// 清除样式
element.style.color = '';
// 获取计算后的样式
const computedStyle = window.getComputedStyle(element);
console.log(computedStyle.color);
console.log(computedStyle.backgroundColor);使用示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>style 属性示例</title>
<style>
.box {
width: 200px;
height: 200px;
background-color: #f0f0f0;
transition: all 0.3s;
}
</style>
</head>
<body>
<!-- 基本用法 -->
<p style="color: red; font-size: 14px;">红色文字</p>
<!-- 动态修改样式 -->
<div id="box" class="box" style="border: 2px solid blue;">
动态盒子
</div>
<button onclick="changeColor()">改变颜色</button>
<button onclick="changeSize()">改变大小</button>
<button onclick="resetStyle()">重置样式</button>
<script>
const box = document.getElementById('box');
function changeColor() {
// 随机颜色
const randomColor = '#' + Math.floor(Math.random()*16777215).toString(16);
box.style.backgroundColor = randomColor;
}
function changeSize() {
// 动态修改尺寸
const currentWidth = parseInt(box.style.width || '200');
box.style.width = (currentWidth + 50) + 'px';
box.style.height = (currentWidth + 50) + 'px';
}
function resetStyle() {
// 重置样式
box.style.cssText = 'border: 2px solid blue;';
}
// 鼠标悬停效果
box.addEventListener('mouseenter', function() {
this.style.transform = 'scale(1.1)';
});
box.addEventListener('mouseleave', function() {
this.style.transform = 'scale(1)';
});
</script>
</body>
</html>内联样式 vs CSS 类
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 静态样式 | CSS 类 | 可复用、易维护 |
| 动态计算的样式 | 内联样式 | 值需要动态计算 |
| 用户自定义样式 | 内联样式 | 来自用户输入 |
| 主题切换 | CSS 变量 | 便于全局控制 |
| 第三方组件覆盖 | 内联样式 | 优先级高 |
title - 提示文本
title 属性定义元素的额外信息,当用户鼠标悬停在元素上时会显示提示文本(tooltip)。
主要用途
- 提供补充信息:为缩写、术语等提供完整解释
- 增强用户体验:为图标按钮、链接等提供说明
- 可访问性:为屏幕阅读器用户提供额外上下文
注意事项
- 提示文本会在鼠标悬停时显示,但延迟时间因浏览器而异
- 移动设备上可能无法显示(没有鼠标悬停)
- 不要将重要信息仅放在
title中,因为某些用户可能无法看到 - 对于链接,优先使用描述性的链接文本,而不是依赖
title
使用示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>title 属性示例</title>
<style>
abbr {
text-decoration: underline dotted;
cursor: help;
}
.icon-button {
padding: 8px;
background: none;
border: 1px solid #ccc;
cursor: pointer;
}
</style>
</head>
<body>
<!-- 缩写解释 -->
<p>
<abbr title="HyperText Markup Language">HTML</abbr> 是构建网页的标准标记语言。
<abbr title="Cascading Style Sheets">CSS</abbr> 用于控制网页的样式。
</p>
<!-- 链接提示 -->
<p>
访问我们的
<a href="/about" title="了解更多关于我们的信息、公司历史和团队介绍">
关于我们
</a>
页面。
</p>
<!-- 图片说明 -->
<img src="photo.jpg" alt="照片" title="2023年拍摄于北京颐和园">
<!-- 表单字段提示 -->
<form>
<label for="username">用户名:</label>
<input type="text" id="username" name="username"
title="请输入您的用户名,3-20个字符,只能包含字母、数字和下划线">
<label for="password">密码:</label>
<input type="password" id="password" name="password"
title="密码长度至少8位,必须包含大小写字母和数字">
</form>
<!-- 图标按钮提示 -->
<button class="icon-button" title="保存当前更改(快捷键:Ctrl+S)">
💾
</button>
<button class="icon-button" title="删除此项(不可恢复)">
🗑️
</button>
<!-- 表格单元格提示 -->
<table border="1">
<tr>
<th>产品</th>
<th title="产品库存数量">库存</th>
<th title="产品销售价格(人民币)">价格</th>
</tr>
<tr>
<td>手机</td>
<td title="库存充足">150</td>
<td title="含税价格">¥2999</td>
</tr>
</table>
</body>
</html>title vs aria-label
| 属性 | 可见性 | 用途 | 屏幕阅读器 |
|---|---|---|---|
title | 鼠标悬停可见 | 补充说明 | 部分支持 |
aria-label | 不可见 | 可访问性标签 | 完全支持 |
alt | 图片不显示时可见 | 图片替代文本 | 完全支持 |
<!-- 推荐:同时使用 title 和 aria-label -->
<button title="保存更改" aria-label="保存更改">
💾
</button><a id="国际化属性"></a>
国际化属性
lang - 语言标识
lang 属性用于指定元素内容的语言。它对于无障碍访问、搜索引擎优化(SEO)、拼写检查以及正确显示特殊字符等方面都非常重要。
主要用途
- 辅助技术支持:帮助屏幕阅读器等辅助设备正确发音和断句
- 搜索引擎优化:帮助搜索引擎理解页面内容的语言
- 拼写检查:浏览器可以根据语言设置启用正确的拼写检查
- 样式控制:CSS 可以基于语言应用不同的样式规则
- 字符处理:确保特殊字符和连字正确显示
常见语言代码
| 语言 | 代码 | 语言 | 代码 |
|---|---|---|---|
| 简体中文 | zh-CN | 英语 | en |
| 繁体中文 | zh-TW | 美式英语 | en-US |
| 繁体中文(香港) | zh-HK | 英式英语 | en-GB |
| 日语 | ja | 法语 | fr |
| 韩语 | ko | 德语 | de |
| 西班牙语 | es | 俄语 | ru |
| 阿拉伯语 | ar | 葡萄牙语 | pt |
JavaScript API
// 获取语言设置
const lang = document.documentElement.lang;
console.log(lang); // 'zh-CN'
// 获取特定元素的语言
const element = document.getElementById('content');
const elementLang = element.lang;
// 动态设置语言
document.documentElement.lang = 'en';
// 获取浏览器语言
const browserLang = navigator.language;
console.log(browserLang); // 'zh-CN'使用示例
<!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>
/* 根据语言应用不同样式 */
[lang="ar"] {
font-family: 'Traditional Arabic', serif;
direction: rtl;
}
[lang="ja"] {
font-family: 'Hiragino Sans', sans-serif;
}
[lang="en"] {
font-family: 'Georgia', serif;
}
/* 语言标记 */
.lang-tag {
font-size: 0.8em;
color: #666;
margin-left: 5px;
}
</style>
</head>
<body>
<header>
<h1>多语言内容示例</h1>
<nav>
<a href="#" lang="en">Home</a> |
<a href="#" lang="zh-CN">首页</a> |
<a href="#" lang="ja">ホーム</a> |
<a href="#" lang="ar">الرئيسية</a>
</nav>
</header>
<main>
<!-- 英语部分 -->
<article lang="en">
<h2>English Article</h2>
<p>This is an article written in English. The browser will use English pronunciation for screen readers.</p>
<blockquote>
"To be or not to be, that is the question."
<footer>— William Shakespeare</footer>
</blockquote>
</article>
<!-- 中文部分 -->
<article lang="zh-CN">
<h2>中文文章</h2>
<p>这是一篇用简体中文写的文章。浏览器会使用中文语音进行朗读。</p>
<blockquote>
"学而不思则罔,思而不学则殆。"
<footer>— 孔子</footer>
</blockquote>
</article>
<!-- 日语部分 -->
<article lang="ja">
<h2>日本語の記事</h2>
<p>これは日本語で書かれた記事です。ブラウザは日本語の発音を使用します。</p>
<blockquote>
"七転び八起き"
<footer>— 日本のことわざ</footer>
</blockquote>
</article>
<!-- 阿拉伯语部分(RTL) -->
<article lang="ar" dir="rtl">
<h2>مقال عربي</h2>
<p>هذا مقال مكتوب باللغة العربية. سيستخدم المتصفح النطق العربي.</p>
</article>
<!-- 法语引用 -->
<blockquote lang="fr">
<p>"Ceci est une citation en français."</p>
<footer>— Un auteur français</footer>
</blockquote>
</main>
<footer>
<p>© 2023 多语言网站</p>
</footer>
<script>
// 动态语言切换
function changeLanguage(lang) {
document.documentElement.lang = lang;
// 更新页面内容(实际项目中会从服务器加载翻译)
const translations = {
'zh-CN': {
title: '多语言页面示例',
greeting: '欢迎访问'
},
'en': {
title: 'Multilingual Page',
greeting: 'Welcome'
}
};
document.title = translations[lang]?.title || document.title;
}
// 语言选择器
const langSelector = document.createElement('select');
langSelector.innerHTML = `
<option value="zh-CN">简体中文</option>
<option value="en">English</option>
<option value="ja">日本語</option>
`;
langSelector.addEventListener('change', (e) => {
changeLanguage(e.target.value);
});
document.body.insertBefore(langSelector, document.body.firstChild);
</script>
</body>
</html>dir - 文本方向
dir 属性用于指定元素内容的文本方向(即文字的排列方向)。
取值说明
| 值 | 说明 | 适用场景 |
|---|---|---|
ltr | 从左到右(默认) | 英语、中文等 LTR 语言 |
rtl | 从右到左 | 阿拉伯语、希伯来语等 RTL 语言 |
auto | 自动判断方向 | 用户输入内容、混合文本 |
JavaScript API
// 获取文本方向
const dir = element.dir;
// 设置文本方向
element.dir = 'rtl';
// 获取计算后的文本方向
const computedDir = window.getComputedStyle(element).direction;使用示例
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<title>dir 属性示例</title>
<style>
.text-example {
padding: 15px;
margin: 10px 0;
border: 1px solid #ddd;
background: #f9f9f9;
}
[dir="rtl"] {
text-align: right;
}
[dir="ltr"] {
text-align: left;
}
</style>
</head>
<body>
<h1>文本方向示例</h1>
<!-- 从左到右 -->
<div class="text-example" dir="ltr">
<p>This text is written from left to right.</p>
<p>这是从左到右书写的中文文本。</p>
</div>
<!-- 从右到左 -->
<div class="text-example" dir="rtl">
<p>هذا النص مكتوب من اليمين إلى اليسار</p>
<p>זהו טקסט שנכתב מימין לשמאל</p>
</div>
<!-- 自动判断 -->
<div class="text-example" dir="auto">
<p>Hello العالم مرحبا</p>
</div>
<!-- 表单输入 -->
<form>
<label for="english-name">Name (English):</label>
<input type="text" id="english-name" dir="ltr">
<label for="arabic-name">الاسم (العربية):</label>
<input type="text" id="arabic-name" dir="rtl">
</form>
<!-- 混合内容 -->
<article>
<h2 dir="rtl">عنوان عربي</h2>
<p dir="rtl">
هذه فقرة باللغة العربية.
<span dir="ltr">This is English text embedded in Arabic.</span>
وباقي النص بالعربية.
</p>
</article>
<script>
// 动态检测文本方向
function detectDirection(text) {
// 检测第一个强方向字符
const rtlRegex = /[\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC]/;
return rtlRegex.test(text) ? 'rtl' : 'ltr';
}
// 为用户输入自动设置方向
document.querySelectorAll('input[type="text"]').forEach(input => {
input.addEventListener('input', function() {
this.dir = detectDirection(this.value);
});
});
</script>
</body>
</html>注意事项
- CSS 优先级:如果同时使用 CSS 的
direction属性和 HTML 的dir属性,CSS 的优先级更高 - 与
text-align的区别:dir控制的是文本的整体流向,而text-align控制的是行内内容的对齐方式 - 表单方向:对于表单元素,特别是输入框,设置正确的
dir值可以改善用户体验
translate - 翻译控制
translate 属性用于指定在浏览器或翻译工具执行自动翻译时,该元素的文本内容是否应该被翻译。
取值说明
| 值 | 说明 | 适用场景 |
|---|---|---|
yes | 允许翻译(默认) | 需要翻译的内容 |
no | 不允许翻译 | 品牌名、代码、专有名词 |
使用示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>translate 属性示例</title>
<style>
nav ul {
list-style: none;
display: flex;
gap: 20px;
padding: 0;
}
nav li {
padding: 10px 20px;
background: #244ca5;
color: white;
}
[translate="no"] {
border-bottom: 2px solid #ff9800;
}
</style>
</head>
<body>
<nav>
<ul>
<li>Home Page</li>
<li translate="no">Python</li>
<li>Course</li>
<li>Book</li>
<li>Resource Library</li>
<li>Community</li>
<li>Service</li>
<li translate="no">App</li>
</ul>
</nav>
<!-- 代码示例不应翻译 -->
<article>
<h2>Code Example</h2>
<pre translate="no"><code>
function hello() {
console.log("Hello, World!");
}
</code></pre>
</article>
<!-- 品牌名不应翻译 -->
<p>
Welcome to <span translate="no">Microsoft</span>!
Learn more about <span translate="no">Windows 11</span>.
</p>
<!-- 个人姓名通常不翻译 -->
<p>
Author: <span translate="no">张三</span>
</p>
</body>
</html>注意事项
- 浏览器支持因内核和版本而异
- 对于不需要翻译的内容(如品牌名、代码、专有名词),应设置为
translate="no" - 可以继承父元素的
translate设置
<a id="可访问性属性"></a>
可访问性属性
tabindex - 焦点控制
tabindex 属性用于控制元素是否可以通过键盘的 Tab 键获得焦点,以及焦点的导航顺序。它是构建可访问 Web 应用程序的关键属性之一。
取值说明
| 值 | 行为 | 适用场景 | 推荐度 |
|---|---|---|---|
| 省略 | 自然导航顺序 | 大多数元素 | ✅ 推荐 |
0 | 按DOM顺序可聚焦 | 自定义控件 | ✅ 推荐 |
-1 | 仅编程可聚焦 | 模态框、动态焦点 | ✅ 推荐 |
| 正整数 | 自定义顺序 | 不推荐 | ❌ 避免 |
焦点管理流程
用户按 Tab 键
↓
检查 tabindex 值
↓
├─ 省略或 0 → 按DOM顺序导航
├─ -1 → 跳过(不参与Tab导航)
└─ 正整数 → 按数值从小到大导航(不推荐)
↓
聚焦到目标元素
↓
触发 focus 事件
↓
显示焦点样式(outline)JavaScript API
const element = document.getElementById('myElement');
// 获取 tabindex
console.log(element.tabIndex); // -1, 0, 或正整数
// 设置 tabindex
element.tabIndex = 0;
// 编程聚焦
element.focus();
// 编程失焦
element.blur();
// 检查是否可聚焦
function isFocusable(element) {
if (element.tabIndex < 0) return false;
if (element.hasAttribute('disabled')) return false;
if (element.hidden) return false;
return true;
}
// 获取所有可聚焦元素
const focusableElements = document.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);使用示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>tabindex 示例</title>
<style>
/* 自定义焦点样式 */
:focus {
outline: 2px solid #0066ff;
outline-offset: 2px;
}
.custom-control {
padding: 10px;
margin: 5px;
border: 1px solid #ccc;
cursor: pointer;
display: inline-block;
}
.custom-control:hover {
background-color: #f0f0f0;
}
/* 模态框样式 */
#modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 20px;
background: white;
border: 1px solid #ccc;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
z-index: 1000;
}
.overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 999;
}
/* 焦点陷阱提示 */
.focus-trap-info {
background: #fff3cd;
padding: 10px;
margin: 10px 0;
}
</style>
</head>
<body>
<h1>tabindex 完整示例</h1>
<h2>自然焦点顺序</h2>
<p>使用 Tab 键测试这些按钮的焦点顺序:</p>
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<h2>自定义控件(tabindex="0")</h2>
<p>使用 Tab 键可以聚焦这些自定义元素:</p>
<div class="custom-control" tabindex="0" role="button">
自定义控件1
</div>
<div class="custom-control" tabindex="0" role="button">
自定义控件2
</div>
<div class="custom-control" tabindex="0" role="button">
自定义控件3
</div>
<h2>避免使用正整数 tabindex</h2>
<p class="focus-trap-info">
⚠️ 以下示例使用正整数 tabindex,这会破坏自然的 DOM 顺序,不推荐使用!
</p>
<button>按钮A</button>
<div class="custom-control" tabindex="3">不应该这样(tabindex="3")</div>
<div class="custom-control" tabindex="1">第一个获得焦点(tabindex="1")</div>
<div class="custom-control" tabindex="2">第二个获得焦点(tabindex="2")</div>
<button>按钮B</button>
<h2>编程控制焦点(tabindex="-1")</h2>
<button id="open-modal">打开模态框</button>
<div id="overlay" class="overlay" tabindex="-1" style="display:none;"></div>
<div id="modal" tabindex="-1" style="display:none;">
<h3>模态对话框</h3>
<p>这是一个模态框。按 Esc 键关闭。</p>
<input type="text" placeholder="输入内容">
<button id="close-modal">关闭</button>
</div>
<h2>焦点陷阱(Focus Trap)</h2>
<div class="focus-trap-info">
模态框打开后,焦点会被限制在模态框内循环,不会回到背景内容。
</div>
<script>
// 为自定义控件添加键盘支持
document.querySelectorAll('.custom-control').forEach(control => {
control.addEventListener('keydown', function(e) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
this.click();
}
});
control.addEventListener('click', function() {
alert(`你点击了: ${this.textContent}`);
});
});
// 模态框管理
const openBtn = document.getElementById('open-modal');
const closeBtn = document.getElementById('close-modal');
const modal = document.getElementById('modal');
const overlay = document.getElementById('overlay');
// 保存打开模态框前的焦点元素
let previouslyFocused = null;
openBtn.addEventListener('click', function() {
previouslyFocused = document.activeElement;
overlay.style.display = 'block';
modal.style.display = 'block';
// 聚焦到模态框
modal.focus();
});
closeBtn.addEventListener('click', closeModal);
overlay.addEventListener('click', closeModal);
function closeModal() {
overlay.style.display = 'none';
modal.style.display = 'none';
// 恢复之前的焦点
if (previouslyFocused) {
previouslyFocused.focus();
}
}
// 键盘事件处理
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && modal.style.display === 'block') {
closeModal();
}
// Tab 键焦点陷阱
if (e.key === 'Tab' && modal.style.display === 'block') {
const focusableElements = modal.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
if (e.shiftKey) {
// Shift + Tab
if (document.activeElement === firstElement) {
e.preventDefault();
lastElement.focus();
}
} else {
// Tab
if (document.activeElement === lastElement) {
e.preventDefault();
firstElement.focus();
}
}
}
});
// 焦点管理工具函数
class FocusManager {
constructor() {
this.focusableSelectors = [
'button:not([disabled])',
'[href]',
'input:not([disabled])',
'select:not([disabled])',
'textarea:not([disabled])',
'[tabindex]:not([tabindex="-1"]):not([disabled])'
].join(', ');
}
getFocusableElements(container = document) {
return Array.from(container.querySelectorAll(this.focusableSelectors))
.filter(el => !el.hidden && el.offsetParent !== null);
}
trapFocus(container) {
const focusableElements = this.getFocusableElements(container);
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
container.addEventListener('keydown', (e) => {
if (e.key !== 'Tab') return;
if (e.shiftKey) {
if (document.activeElement === firstElement) {
e.preventDefault();
lastElement.focus();
}
} else {
if (document.activeElement === lastElement) {
e.preventDefault();
firstElement.focus();
}
}
});
}
}
// 使用焦点管理器
const focusManager = new FocusManager();
focusManager.trapFocus(modal);
</script>
</body>
</html>accesskey - 快捷键
accesskey 属性用于为元素分配快捷键,使用户可以通过键盘快速访问该元素。
浏览器支持差异
| 浏览器/操作系统 | 触发方式 |
|---|---|
| Windows (Chrome/Edge/Firefox) | Alt + accesskey |
| Mac (Safari) | Control + Option + accesskey |
| Mac (Chrome/Firefox) | Control + Option + accesskey |
| Linux | Alt + Shift + accesskey |
使用示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>accesskey 示例</title>
<style>
.shortcut-hint {
font-size: 0.8em;
color: #666;
margin-left: 5px;
}
kbd {
background: #f0f0f0;
border: 1px solid #ccc;
border-radius: 3px;
padding: 2px 6px;
font-family: monospace;
}
</style>
</head>
<body>
<header>
<h1>网站导航(带快捷键)</h1>
<nav>
<ul>
<li><a href="/" accesskey="h">首页 <kbd>Alt+H</kbd></a></li>
<li><a href="/products" accesskey="p">产品 <kbd>Alt+P</kbd></a></li>
<li><a href="/services" accesskey="s">服务 <kbd>Alt+S</kbd></a></li>
<li><a href="/contact" accesskey="c">联系我们 <kbd>Alt+C</kbd></a></li>
</ul>
</nav>
</header>
<main>
<section>
<h2>搜索 <kbd>Alt+F</kbd></h2>
<form action="/search" method="get">
<label for="search-input">搜索内容:</label>
<input type="text" id="search-input" name="q" accesskey="f">
<button type="submit" accesskey="g">搜索 <kbd>Alt+G</kbd></button>
</form>
</section>
<section>
<h2>快捷键列表</h2>
<table>
<thead>
<tr>
<th>快捷键</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr><td><kbd>Alt+H</kbd></td><td>返回首页</td></tr>
<tr><td><kbd>Alt+P</kbd></td><td>产品页面</td></tr>
<tr><td><kbd>Alt+S</kbd></td><td>服务页面</td></tr>
<tr><td><kbd>Alt+C</kbd></td><td>联系我们</td></tr>
<tr><td><kbd>Alt+F</kbd></td><td>聚焦搜索框</td></tr>
<tr><td><kbd>Alt+G</kbd></td><td>执行搜索</td></tr>
</tbody>
</table>
</section>
</main>
<footer>
<p>© 2023 我的网站</p>
<button id="back-to-top" accesskey="t">返回顶部 <kbd>Alt+T</kbd></button>
</footer>
<script>
// 返回顶部功能
document.getElementById('back-to-top').addEventListener('click', () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
});
// 显示快捷键提示
document.addEventListener('keydown', (e) => {
if (e.altKey) {
const key = e.key.toLowerCase();
const element = document.querySelector(`[accesskey="${key}"]`);
if (element) {
console.log(`快捷键激活: ${element.textContent}`);
}
}
});
</script>
</body>
</html>最佳实践
- 避免冲突:不要与浏览器或操作系统快捷键冲突
- 提供提示:在界面上显示快捷键提示
- 一致性:在整个网站中使用一致的快捷键
- 可访问性:不要仅依赖快捷键,确保所有功能都有其他访问方式
<a id="交互控制属性"></a>
交互控制属性
hidden - 元素隐藏
hidden 属性用于指示元素当前不应该被显示。这个属性简单但功能强大,是控制元素可见性的重要工具。
与 CSS display: none 的区别
| 特性 | hidden 属性 | display: none |
|---|---|---|
| 语义 | 表示元素当前不可见(有语义含义) | 纯粹的样式控制 |
| 可访问性 | 屏幕阅读器通常会忽略 | 取决于实现,可能被忽略 |
| JavaScript 操作 | 直接修改属性 | 需要修改样式 |
| 性能 | 更高效(直接属性操作) | 需要样式计算 |
JavaScript API
const element = document.getElementById('myElement');
// 获取 hidden 状态
console.log(element.hidden); // true 或 false
// 设置 hidden
element.hidden = true; // 隐藏
element.hidden = false; // 显示
// 切换显示状态
element.hidden = !element.hidden;
// 检查元素是否隐藏
if (element.hidden) {
console.log('元素已隐藏');
}使用示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>hidden 属性示例</title>
<style>
.container {
margin: 20px;
padding: 20px;
border: 1px solid #ddd;
}
</style>
</head>
<body>
<h1>hidden 属性完整示例</h1>
<!-- 基本用法 -->
<div class="container">
<h2>基本显示/隐藏</h2>
<button id="toggleButton">切换隐藏状态</button>
<div id="content">
<p>这段内容可以通过 JavaScript 控制显示/隐藏。</p>
</div>
</div>
<!-- 条件性显示 -->
<div class="container">
<h2>条件性显示</h2>
<div hidden id="adminPanel">
<h3>管理面板</h3>
<ul>
<li><a href="/users">用户管理</a></li>
<li><a href="/settings">系统设置</a></li>
</ul>
</div>
<button id="toggleAdmin">切换管理面板</button>
</div>
<!-- 加载状态 -->
<div class="container">
<h2>加载状态</h2>
<div id="loading" hidden>
<p>加载中,请稍候...</p>
</div>
<div id="content-loaded">
<p>这是加载完成后的内容。</p>
</div>
<button id="simulateLoad">模拟加载</button>
</div>
<!-- 表单隐藏字段 -->
<div class="container">
<h2>表单隐藏字段</h2>
<form action="/submit" method="post">
<label for="username">用户名:</label>
<input type="text" id="username" name="username">
<!-- 隐藏的 CSRF 令牌 -->
<input type="hidden" name="csrf_token" value="abc123xyz">
<!-- 隐藏的用户 ID(用于编辑现有记录) -->
<input type="hidden" name="user_id" value="42">
<button type="submit">提交</button>
</form>
</div>
<script>
// 基本切换
const toggleButton = document.getElementById('toggleButton');
const content = document.getElementById('content');
toggleButton.addEventListener('click', () => {
content.hidden = !content.hidden;
toggleButton.textContent = content.hidden ? '显示内容' : '隐藏内容';
});
// 管理面板
const toggleAdmin = document.getElementById('toggleAdmin');
const adminPanel = document.getElementById('adminPanel');
// 模拟检查用户权限
const userRole = 'user'; // 'admin' 或 'user'
adminPanel.hidden = userRole !== 'admin';
toggleAdmin.addEventListener('click', () => {
adminPanel.hidden = !adminPanel.hidden;
});
// 加载状态
const loading = document.getElementById('loading');
const contentLoaded = document.getElementById('content-loaded');
const simulateLoad = document.getElementById('simulateLoad');
simulateLoad.addEventListener('click', () => {
loading.hidden = false;
contentLoaded.hidden = true;
// 模拟异步加载
setTimeout(() => {
loading.hidden = true;
contentLoaded.hidden = false;
}, 2000);
});
// 初始状态
contentLoaded.hidden = false;
</script>
</body>
</html>contenteditable - 可编辑内容
contenteditable 属性允许用户直接在网页上编辑元素内容。
取值说明
| 值 | 含义 | 说明 |
|---|---|---|
true 或空字符串 | 可编辑 | 元素及其内容可编辑 |
false | 不可编辑 | 元素不可编辑 |
plaintext-only | 仅纯文本 | 仅允许纯文本编辑(不支持富文本格式) |
JavaScript API
const element = document.getElementById('myElement');
// 获取 contenteditable 状态
console.log(element.contentEditable); // 'true', 'false', 或 'inherit'
console.log(element.isContentEditable); // true 或 false
// 设置 contenteditable
element.contentEditable = 'true';
// 监听内容变化
element.addEventListener('input', function() {
console.log('内容已修改:', this.innerHTML);
});
// 监听粘贴事件
element.addEventListener('paste', function(e) {
e.preventDefault();
// 只粘贴纯文本
const text = e.clipboardData.getData('text/plain');
document.execCommand('insertText', false, text);
});使用示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>contenteditable 示例</title>
<style>
.editor-container {
border: 1px solid #ccc;
padding: 10px;
margin: 20px;
min-height: 200px;
width: 600px;
}
.toolbar button {
margin-right: 5px;
padding: 5px 10px;
}
[contenteditable="true"]:focus {
outline: 2px solid #0066ff;
}
</style>
</head>
<body>
<h1>contenteditable 示例</h1>
<h2>基础示例</h2>
<div contenteditable="true" style="border:1px solid #ccc; padding:10px; min-height:100px;">
点击这里编辑这段文字。你可以添加、删除或修改任何内容。
</div>
<h2>富文本编辑器</h2>
<div class="toolbar">
<button onclick="formatText('bold')"><b>B</b></button>
<button onclick="formatText('italic')"><i>I</i></button>
<button onclick="formatText('underline')"><u>U</u></button>
<button onclick="formatText('strikeThrough')"><s>S</s></button>
<button onclick="formatText('insertUnorderedList')">• 列表</button>
<button onclick="formatText('insertOrderedList')">1. 列表</button>
<button onclick="insertLink()">链接</button>
<button onclick="formatText('removeFormat')">清除格式</button>
</div>
<div id="editor" class="editor-container" contenteditable="true">
<h2>这是一个可编辑的标题</h2>
<p>在这里输入内容,使用上方工具栏格式化文本。</p>
<ul>
<li>支持列表</li>
<li>支持多种格式</li>
</ul>
</div>
<div>
<button onclick="getHTML()">获取HTML</button>
<button onclick="getText()">获取纯文本</button>
<button onclick="clearContent()">清空内容</button>
</div>
<div id="output" style="margin-top:20px; padding:10px; background:#f0f0f0;"></div>
<h2>嵌套编辑区域</h2>
<div contenteditable="true" style="border:1px solid #ccc; padding:10px;">
<p>这是可编辑的段落。</p>
<div contenteditable="false" style="background:#f9f9f9; padding:10px;">
<p>这段文字被明确标记为不可编辑,即使父元素可编辑。</p>
</div>
<p>回到可编辑区域。</p>
</div>
<h2>仅纯文本编辑</h2>
<div contenteditable="plaintext-only" style="border:1px solid #ccc; padding:10px; min-height:100px;">
这个区域只能输入纯文本,不支持富文本格式。
</div>
<script>
// 格式化文本
function formatText(command, value = null) {
document.execCommand(command, false, value);
document.getElementById('editor').focus();
}
// 插入链接
function insertLink() {
const url = prompt('请输入链接URL:');
if (url) {
formatText('createLink', url);
}
}
// 获取HTML
function getHTML() {
const editor = document.getElementById('editor');
document.getElementById('output').innerHTML =
'<strong>HTML内容:</strong><pre>' +
escapeHtml(editor.innerHTML) +
'</pre>';
}
// 获取纯文本
function getText() {
const editor = document.getElementById('editor');
document.getElementById('output').innerHTML =
'<strong>纯文本内容:</strong><pre>' +
escapeHtml(editor.textContent) +
'</pre>';
}
// 清空内容
function clearContent() {
if (confirm('确定要清空内容吗?')) {
document.getElementById('editor').innerHTML = '';
}
}
// 转义HTML
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// 监听编辑器内容变化
const editor = document.getElementById('editor');
editor.addEventListener('input', function() {
console.log('内容已修改');
});
// 保存内容(Ctrl+S)
document.addEventListener('keydown', function(e) {
if (e.ctrlKey && e.key === 's') {
e.preventDefault();
const content = editor.innerHTML;
console.log('保存的内容:', content);
alert('内容已保存!\n' + content.substring(0, 100) + '...');
}
});
</script>
</body>
</html>spellcheck - 拼写检查
spellcheck 属性用于控制元素内容是否应该被拼写检查。
取值说明
| 值 | 说明 |
|---|---|
true | 启用拼写检查 |
false | 禁用拼写检查 |
| 省略 | 浏览器默认行为 |
JavaScript API
const element = document.getElementById('myInput');
// 获取拼写检查状态
console.log(element.spellcheck); // true 或 false
// 设置拼写检查
element.spellcheck = true;使用示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>spellcheck 示例</title>
<style>
.container {
margin: 20px;
padding: 20px;
border: 1px solid #ddd;
}
textarea {
width: 100%;
height: 100px;
}
</style>
</head>
<body>
<h1>拼写检查示例</h1>
<div class="container">
<h2>启用拼写检查</h2>
<textarea spellcheck="true">
This is a smple sentnce with sme erors.
You will see red underlines for misspelled words.
</textarea>
</div>
<div class="container">
<h2>禁用拼写检查</h2>
<textarea spellcheck="false">
This is a smple sentnce with sme erors.
No red underlines will appear.
</textarea>
</div>
<div class="container">
<h2>可编辑区域中的拼写检查</h2>
<div contenteditable="true" spellcheck="true"
style="border:1px solid #ccc; padding:10px; min-height:100px;">
这是一个可编辑区域,启用拼写检查。Try typing some misspelled words.
</div>
</div>
<div class="container">
<h2>动态控制拼写检查</h2>
<div id="editable" contenteditable="true" spellcheck="true"
style="border:1px solid #ccc; padding:10px; min-height:100px;">
尝试在这里输入一些拼写错误的单词。
</div>
<div>
<button onclick="toggleSpellcheck(true)">启用拼写检查</button>
<button onclick="toggleSpellcheck(false)">禁用拼写检查</button>
<button onclick="checkCurrentState()">检查当前状态</button>
</div>
</div>
<script>
function toggleSpellcheck(enable) {
const editor = document.getElementById('editable');
editor.spellcheck = enable;
alert(`拼写检查已${enable ? '启用' : '禁用'}`);
}
function checkCurrentState() {
const editor = document.getElementById('editable');
alert(`当前拼写检查状态: ${editor.spellcheck ? '启用' : '禁用'}`);
}
</script>
</body>
</html>draggable - 拖拽控制
draggable 属性用于控制元素是否可以被拖拽。
取值说明
| 值 | 说明 |
|---|---|
true | 元素可拖拽 |
false | 元素不可拖拽 |
auto | 浏览器决定(图片、链接默认可拖拽) |
拖拽事件流程
拖拽元素 放置区域
| |
| dragstart |
|-------------------------->|
| |
| drag |
|-------------------------->|
| | dragenter
| |---------->|
| |
| | dragover
| |---------->|
| |
| | dragleave
| |---------->|
| |
| dragend |
|-------------------------->|
| | drop
| |---------->|JavaScript API
const element = document.getElementById('myElement');
// 获取 draggable 状态
console.log(element.draggable); // true 或 false
// 设置 draggable
element.draggable = true;
// 拖拽事件
element.addEventListener('dragstart', function(e) {
e.dataTransfer.setData('text/plain', this.id);
});
element.addEventListener('dragend', function(e) {
console.log('拖拽结束');
});使用示例
详细示例请参见原文档中的拖拽相关章节,包括:
- 基本可拖拽元素
- 图像拖拽
- 列表项拖拽排序
- 文件拖放上传
<a id="数据属性"></a>
数据属性
data-* - 自定义数据
data-* 属性是 HTML5 引入的一种自定义数据属性,允许开发者在 HTML 元素上存储额外的信息。
命名规则
| 规则 | 说明 | 示例 |
|---|---|---|
必须以 data- 开头 | 前缀不可省略 | ✅ data-user-id |
| 只能包含小写字母、数字、连字符、下划线 | 不支持大写字母 | ❌ data-UserId |
| 连字符会转为驼峰 | JavaScript 中访问时 | data-user-id → dataset.userId |
JavaScript API
const element = document.getElementById('myElement');
// 获取数据(dataset API)
console.log(element.dataset.userId); // '12345'
console.log(element.dataset.userName); // '张三'
// 设置数据
element.dataset.userId = '67890';
element.dataset.newAttr = '新属性';
// 删除数据
delete element.dataset.userId;
// 传统 API
console.log(element.getAttribute('data-user-id'));
element.setAttribute('data-user-id', 'new-value');
element.removeAttribute('data-user-id');使用示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>data-* 属性示例</title>
<style>
/* CSS 中使用 data-* 属性 */
[data-status="active"] {
background-color: #4caf50;
color: white;
}
[data-status="inactive"] {
background-color: #f44336;
color: white;
}
/* 使用 attr() 显示 data-* 值 */
.show-data::after {
content: attr(data-tooltip);
display: block;
font-size: 12px;
color: #666;
margin-top: 5px;
}
.user-card {
border: 1px solid #ddd;
padding: 15px;
margin: 10px 0;
display: flex;
align-items: center;
gap: 15px;
}
.user-card img {
width: 50px;
height: 50px;
border-radius: 50%;
}
.user-info {
flex: 1;
}
</style>
</head>
<body>
<h1>data-* 属性完整示例</h1>
<h2>基础用法</h2>
<div id="user"
data-user-id="12345"
data-username="张三"
data-role="admin"
data-last-login="2023-05-15T08:30:00Z">
用户信息
</div>
<h2>用于样式控制</h2>
<div class="status-indicator" data-status="active">系统状态: 正常</div>
<div class="status-indicator" data-status="inactive">系统状态: 维护中</div>
<h2>用于工具提示</h2>
<button class="show-data" data-tooltip="点击保存更改">保存</button>
<button class="show-data" data-tooltip="放弃所有未保存的更改">取消</button>
<h2>用户卡片列表</h2>
<div id="user-list">
<!-- 用户卡片将通过 JavaScript 动态生成 -->
</div>
<h2>交互式列表</h2>
<ul id="todo-list">
<li data-id="1" data-completed="false" data-priority="high">
<span class="todo-text">完成项目报告</span>
<button onclick="toggleTodo(this)">完成</button>
<button onclick="deleteTodo(this)">删除</button>
</li>
<li data-id="2" data-completed="true" data-priority="medium">
<span class="todo-text" style="text-decoration: line-through;">回复邮件</span>
<button onclick="toggleTodo(this)">撤销</button>
<button onclick="deleteTodo(this)">删除</button>
</li>
</ul>
<h2>数据筛选</h2>
<div>
<button onclick="filterByPriority('all')">全部</button>
<button onclick="filterByPriority('high')">高优先级</button>
<button onclick="filterByPriority('medium')">中优先级</button>
<button onclick="filterByPriority('low')">低优先级</button>
</div>
<script>
// 获取元素
const userElement = document.getElementById('user');
// 访问 data-* 属性
console.log('用户ID:', userElement.dataset.userId); // 注意: 连字符转为驼峰命名
console.log('用户名:', userElement.dataset.username);
console.log('角色:', userElement.dataset.role);
console.log('最后登录:', userElement.dataset.lastLogin);
// 修改 data-* 属性
userElement.dataset.role = 'editor';
console.log('修改后的角色:', userElement.dataset.role);
// 动态生成用户卡片
const users = [
{ id: 1, name: '张三', role: 'admin', status: 'active' },
{ id: 2, name: '李四', role: 'user', status: 'active' },
{ id: 3, name: '王五', role: 'user', status: 'inactive' }
];
const userList = document.getElementById('user-list');
users.forEach(user => {
const card = document.createElement('div');
card.className = 'user-card';
card.dataset.userId = user.id;
card.dataset.userName = user.name;
card.dataset.userRole = user.role;
card.dataset.userStatus = user.status;
card.innerHTML = `
<img src="https://via.placeholder.com/50" alt="${user.name}">
<div class="user-info">
<strong>${user.name}</strong>
<p>角色: ${user.role}</p>
<p>状态: <span data-status="${user.status}">${user.status === 'active' ? '活跃' : '未激活'}</span></p>
</div>
<button onclick="editUser(${user.id})">编辑</button>
<button onclick="deleteUser(${user.id})">删除</button>
`;
userList.appendChild(card);
});
// 用户操作
function editUser(id) {
const card = document.querySelector(`[data-user-id="${id}"]`);
console.log('编辑用户:', card.dataset.userName);
}
function deleteUser(id) {
if (confirm('确定要删除这个用户吗?')) {
const card = document.querySelector(`[data-user-id="${id}"]`);
card.remove();
}
}
// Todo 操作
function toggleTodo(button) {
const li = button.parentElement;
const completed = li.dataset.completed === 'true';
li.dataset.completed = !completed;
const textSpan = li.querySelector('.todo-text');
textSpan.style.textDecoration = completed ? 'none' : 'line-through';
button.textContent = completed ? '完成' : '撤销';
}
function deleteTodo(button) {
const li = button.parentElement;
const id = li.dataset.id;
if (confirm('确定要删除这个任务吗?')) {
li.remove();
console.log('删除任务 ID:', id);
}
}
// 筛选功能
function filterByPriority(priority) {
const todos = document.querySelectorAll('#todo-list li');
todos.forEach(todo => {
if (priority === 'all' || todo.dataset.priority === priority) {
todo.style.display = '';
} else {
todo.style.display = 'none';
}
});
}
</script>
</body>
</html><a id="移动端优化属性"></a>
移动端优化属性
inputmode - 虚拟键盘类型
inputmode 用于向浏览器提示当前输入区域期望的输入类型,从而选择更合适的虚拟键盘布局。
取值说明
| 值 | 键盘类型 | 适用场景 |
|---|---|---|
text | 普通文本键盘(默认) | 一般文本输入 |
numeric | 纯数字键盘 | 数字验证码、PIN码 |
decimal | 数字+小数点键盘 | 金额输入 |
tel | 电话号码键盘 | 手机号、座机号 |
email | 邮箱键盘(带@符号) | 邮箱地址 |
url | URL键盘(带/和.com) | 网址输入 |
search | 搜索键盘(回车键显示"搜索") | 搜索框 |
使用示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>inputmode 示例</title>
<style>
form {
max-width: 500px;
margin: 20px;
}
label {
display: block;
margin: 15px 0 5px;
font-weight: bold;
}
input {
width: 100%;
padding: 10px;
font-size: 16px;
border: 1px solid #ddd;
border-radius: 4px;
}
.hint {
font-size: 12px;
color: #666;
margin-top: 5px;
}
</style>
</head>
<body>
<h1>inputmode 虚拟键盘优化</h1>
<form>
<label for="amount">金额(decimal)</label>
<input type="text" id="amount" name="amount"
inputmode="decimal" placeholder="请输入金额">
<p class="hint">显示数字键盘+小数点</p>
<label for="phone">手机号(tel)</label>
<input type="text" id="phone" name="phone"
inputmode="tel" placeholder="请输入手机号">
<p class="hint">显示电话号码键盘</p>
<label for="email">邮箱(email)</label>
<input type="text" id="email" name="email"
inputmode="email" placeholder="请输入邮箱">
<p class="hint">显示带@符号的键盘</p>
<label for="website">网址(url)</label>
<input type="text" id="website" name="website"
inputmode="url" placeholder="请输入网址">
<p class="hint">显示带/和.com的键盘</p>
<label for="search">搜索(search)</label>
<input type="text" id="search" name="search"
inputmode="search" placeholder="搜索内容">
<p class="hint">回车键显示"搜索"</p>
<label for="pin">验证码(numeric)</label>
<input type="text" id="pin" name="pin"
inputmode="numeric" maxlength="6" placeholder="请输入6位验证码">
<p class="hint">显示纯数字键盘</p>
<label for="quantity">数量(numeric)</label>
<input type="text" id="quantity" name="quantity"
inputmode="numeric" placeholder="请输入数量">
<p class="hint">显示纯数字键盘</p>
</form>
<script>
// 监听输入事件
document.querySelectorAll('input').forEach(input => {
input.addEventListener('input', function() {
console.log(`输入内容: ${this.value}, inputmode: ${this.inputMode}`);
});
});
</script>
</body>
</html>enterkeyhint - 回车键提示
enterkeyhint 用于为虚拟键盘上的"回车/确认"键指定提示文案。
取值说明
| 值 | 显示文本 | 适用场景 |
|---|---|---|
enter | 回车/换行 | 多行文本 |
done | 完成 | 完成输入 |
go | 前往 | 导航、跳转 |
next | 下一步 | 表单多步填写 |
previous | 上一步 | 表单多步填写 |
search | 搜索 | 搜索框 |
send | 发送 | 消息发送 |
使用示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>enterkeyhint 示例</title>
<style>
form {
max-width: 500px;
margin: 20px;
}
label {
display: block;
margin: 15px 0 5px;
font-weight: bold;
}
input {
width: 100%;
padding: 10px;
font-size: 16px;
border: 1px solid #ddd;
border-radius: 4px;
}
</style>
</head>
<body>
<h1>enterkeyhint 回车键提示</h1>
<form>
<label for="search">搜索框</label>
<input type="search" id="search" name="q"
enterkeyhint="search" placeholder="搜索内容">
<label for="message">消息发送</label>
<input type="text" id="message" name="message"
enterkeyhint="send" placeholder="输入消息">
<label for="url">网址输入</label>
<input type="url" id="url" name="url"
enterkeyhint="go" placeholder="输入网址">
<label for="name">多步表单</label>
<input type="text" id="name" name="name"
enterkeyhint="next" placeholder="第一步:姓名">
<label for="comment">评论输入</label>
<input type="text" id="comment" name="comment"
enterkeyhint="done" placeholder="输入评论">
</form>
<script>
// 监听回车事件
document.querySelectorAll('input').forEach(input => {
input.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
e.preventDefault();
console.log(`回车键按下,提示类型: ${this.enterKeyHint}`);
// 根据提示类型执行不同操作
switch(this.enterKeyHint) {
case 'search':
console.log('执行搜索');
break;
case 'send':
console.log('发送消息');
break;
case 'next':
console.log('跳转到下一个字段');
break;
case 'done':
console.log('完成输入');
break;
}
}
});
});
</script>
</body>
</html>autocapitalize - 自动大写
autocapitalize 主要用于控制在虚拟键盘输入时,浏览器是否自动将输入内容的首字母大写。
取值说明
| 值 | 说明 | 适用场景 |
|---|---|---|
off / none | 不自动大写 | 账号、邮箱、验证码 |
on | 按平台默认策略 | 默认行为 |
sentences | 每句首字母大写 | 段落文本 |
words | 每个单词首字母大写 | 姓名、标题 |
characters | 所有字符大写 | 验证码、ID |
使用示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>autocapitalize 示例</title>
<style>
form {
max-width: 500px;
margin: 20px;
}
label {
display: block;
margin: 15px 0 5px;
font-weight: bold;
}
input {
width: 100%;
padding: 10px;
font-size: 16px;
border: 1px solid #ddd;
border-radius: 4px;
}
</style>
</head>
<body>
<h1>autocapitalize 自动大写控制</h1>
<form>
<label for="username">用户名(无大写)</label>
<input type="text" id="username" name="username"
autocapitalize="none" placeholder="输入用户名">
<label for="email">邮箱(无大写)</label>
<input type="email" id="email" name="email"
autocapitalize="none" placeholder="输入邮箱">
<label for="fullName">姓名(单词首字母大写)</label>
<input type="text" id="fullName" name="fullName"
autocapitalize="words" placeholder="输入姓名">
<label for="title">标题(句子首字母大写)</label>
<input type="text" id="title" name="title"
autocapitalize="sentences" placeholder="输入标题">
<label for="code">验证码(全大写)</label>
<input type="text" id="code" name="code"
autocapitalize="characters" maxlength="6" placeholder="输入验证码">
</form>
</body>
</html>autofocus - 自动聚焦
autofocus 是一个布尔全局属性,用于在页面加载完成后自动将键盘焦点移动到指定元素上。
使用示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>autofocus 示例</title>
</head>
<body>
<h1>登录页面</h1>
<form>
<!-- 页面加载后自动聚焦到用户名输入框 -->
<label for="username">用户名:</label>
<input type="text" id="username" name="username" autofocus>
<label for="password">密码:</label>
<input type="password" id="password" name="password">
<button type="submit">登录</button>
</form>
<script>
// 检查 autofocus 支持
const autofocusSupported = 'autofocus' in document.createElement('input');
console.log('autofocus 支持:', autofocusSupported);
// 如果不支持,手动聚焦
if (!autofocusSupported) {
document.getElementById('username').focus();
}
</script>
</body>
</html>注意事项
- 每个文档中只应有一个带
autofocus的元素 - 在复杂单页应用中,推荐使用 JavaScript 明确控制焦点
- 对屏幕阅读器用户来说,自动聚焦可能打断阅读流程
<a id="web-components-属性"></a>
Web Components 属性
slot - 插槽
slot 用于指定传入的内容要插入到哪个插槽中。
part - 样式部件
part 用于为 Shadow DOM 内部元素打上"部件名称",以便宿主页面通过 ::part() 伪元素选择器进行样式覆盖。
exportparts - 部件导出
exportparts 用于在组件嵌套时,把内部组件的 part 名称重新映射并暴露出去。
is - 自定义元素
is 属性用于"自定义内建元素"(Customized built-in elements),让内建标签使用自定义元素的逻辑。
完整示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Web Components 属性示例</title>
<style>
/* 自定义危险按钮 */
button[is="danger-button"] {
background-color: #e02424;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
}
button[is="danger-button"]:hover {
background-color: #c81e1e;
}
</style>
</head>
<body>
<h1>Web Components 属性示例</h1>
<h2>自定义内建元素(is 属性)</h2>
<button is="danger-button">删除</button>
<script>
// 定义自定义元素
class DangerButton extends HTMLButtonElement {
connectedCallback() {
this.addEventListener('click', () => {
if (confirm('确定要删除吗?')) {
console.log('删除操作已执行');
}
});
}
}
customElements.define('danger-button', DangerButton, { extends: 'button' });
</script>
<h2>插槽示例(slot 属性)</h2>
<template id="card-template">
<style>
.card {
border: 1px solid #ddd;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.card-header {
font-size: 1.2em;
font-weight: bold;
margin-bottom: 10px;
}
.card-body {
color: #666;
}
.card-footer {
margin-top: 10px;
padding-top: 10px;
border-top: 1px solid #eee;
}
</style>
<div class="card">
<div class="card-header">
<slot name="title"></slot>
</div>
<div class="card-body">
<slot name="content"></slot>
</div>
<div class="card-footer">
<slot name="footer"></slot>
</div>
</div>
</template>
<my-card>
<h3 slot="title">卡片标题</h3>
<p slot="content">这是卡片的内容部分。</p>
<button slot="footer">操作按钮</button>
</my-card>
<script>
class MyCard extends HTMLElement {
constructor() {
super();
const template = document.getElementById('card-template');
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.appendChild(template.content.cloneNode(true));
}
}
customElements.define('my-card', MyCard);
</script>
</body>
</html><a id="高级特性属性"></a>
高级特性属性
<h4>047-inert-microdata.html</h4><!DOCTYPE html>
<!--
来源:基础知识/7-全局属性.md
演示:inert 属性(禁用区域内所有交互)、微数据 itemscope/itemprop/itemtype 结构化数据
-->
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>inert 属性与微数据演示</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, sans-serif; padding: 30px; background: #f0f2f5; }
h1 { color: #1a1a1a; margin-bottom: 25px; }
.section { background: white; padding: 25px; margin-bottom: 25px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.08); }
h2 { color: #333; margin-bottom: 15px; font-size: 17px; border-bottom: 2px solid #eee; padding-bottom: 8px; }
/* inert 演示 */
.modal-overlay {
display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.6); z-index: 1000; justify-content: center; align-items: center;
}
.modal-overlay.show { display: flex; }
.modal-content {
background: white; padding: 35px; border-radius: 14px; max-width: 480px; width: 90%;
box-shadow: 0 20px 60px rgba(0,0,0,0.3); text-align: center;
}
.modal-content h2 { border: none; font-size: 22px; margin-bottom: 15px; }
.page-content { transition: filter 0.3s; }
.page-content.blurred { filter: blur(3px); pointer-events: none; }
.interactive-card {
padding: 20px; margin: 10px 0; background: #f8f9fa; border: 2px solid #dee2e6;
border-radius: 8px; cursor: pointer; transition: all 0.2s;
}
.interactive-card:hover { border-color: #3498db; background: #ebf5fb; }
.interactive-card[inert], [inert] .interactive-card {
opacity: 0.4; cursor: not-allowed; filter: grayscale(50%);
}
.interactive-card[inert]:hover, [inert] .interactive-card:hover {
border-color: #dee2e6; background: #f8f9fa;
}
button {
padding: 10px 20px; border: none; border-radius: 6px; cursor: pointer;
font-size: 14px; transition: all 0.2s;
}
.btn-primary { background: #3498db; color: white; }
.btn-primary:hover { background: #2980b9; }
.btn-success { background: #27ae60; color: white; }
.btn-success:hover { background: #219a52; }
.btn-danger { background: #e74c3c; color: white; }
.btn-danger:hover { background: #c0392b; }
/* 微数据演示 */
.microdata-product {
display: inline-block; width: 200px; margin: 10px; padding: 15px;
background: white; border: 1px solid #e0e0e0; border-radius: 10px;
text-align: center; transition: transform 0.2s, box-shadow 0.2s;
}
.microdata-product:hover { transform: translateY(-5px); box-shadow: 0 8px 24px rgba(0,0,0,0.12); }
.microdata-product img { width: 100%; height: 140px; object-fit: cover; border-radius: 8px; background: #f0f0f0; }
.microdata-product .name { font-weight: bold; margin: 10px 0 5px; font-size: 15px; }
.microdata-product .price { color: #e74c3c; font-size: 18px; font-weight: bold; }
.microdata-product .rating { color: #f39c12; font-size: 14px; }
.event-card {
padding: 20px; background: #fff8e1; border: 1px solid #ffecb3; border-radius: 10px;
margin: 10px 0;
}
code { background: #f4f4f4; padding: 2px 6px; border-radius: 3px; font-size: 13px; color: #e74c3c; }
pre { background: #1e1e1e; color: #d4d4d4; padding: 15px; border-radius: 6px; overflow-x: auto; font-size: 12px; line-height: 1.6; }
table { width: 100%; border-collapse: collapse; font-size: 13px; margin: 15px 0; }
th, td { padding: 10px; text-align: left; border: 1px solid #dee2e6; }
th { background: #34495e; color: white; }
.badge { display: inline-block; padding: 3px 8px; border-radius: 12px; font-size: 11px; font-weight: bold; }
.badge-inert { background: #fee; color: #c00; }
.badge-active { background: #efe; color: #060; }
</style>
</head>
<body>
<h1>🚫 inert 属性 & 微数据结构化</h1>
<!-- ========== 1. inert 属性演示 ========== -->
<div class="section">
<h2>1. inert 属性 — 禁用子树的所有交互</h2>
<p><code>inert</code> 是布尔属性,使元素及其所有后代不可交互、不可聚焦、被屏幕阅读器忽略。</p>
<div style="margin:15px 0;">
<button class="btn-primary" onclick="openInertModal()">打开模态框(背景 inert)</button>
<button class="btn-success" onclick="toggleSectionInert()">切换下方区域的 inert 状态</button>
</div>
<div id="inert-target-section">
<p style="margin-bottom:10px;font-size:13px;color:#666;">
▼ 这个区域可以通过上方按钮切换 inert 状态:
<span id="inert-badge" class="badge badge-active">活跃</span>
</p>
<div class="interactive-card" onclick="alert('你点击了卡片 A!')">
<strong>📇 卡片 A</strong> — 点击弹出提示<br>
<small>当父容器 inert 时,这个点击不会触发</small>
</div>
<div class="interactive-card">
<strong>✏️ 卡片 B</strong> — 包含可聚焦元素<br>
<input type="text" placeholder="可输入文字" style="margin-top:8px;padding:6px 10px;border:1px solid #ccc;border-radius:4px;">
</div>
<div class="interactive-card">
<strong>🔗 卡片 C</strong> — 包含链接<br>
<a href="#" style="color:#3498db;margin-top:8px;display:inline-block;">这是一个链接</a>
</div>
</div>
<pre id="inert-log" style="margin-top:15px;background:#f8f9fa;color:#333;padding:12px;border-radius:6px;font-size:12px;">// inert 状态日志</pre>
</div>
<!-- inert 模态框 -->
<div class="modal-overlay" id="inert-modal">
<div class="modal-content" role="dialog" aria-modal="true">
<h2>🔒 模态框开启中</h2>
<p style="color:#666;margin:15px 0;line-height:1.6;">
此时背景页面已被设置为 <code>inert</code>。<br>
尝试以下操作:<br>
❌ 无法点击背景任何元素<br>
❌ Tab 键无法聚焦到背景<br>
❌ 屏幕阅读器忽略背景内容<br>
✅ 只能与此模态框交互
</p>
<button class="btn-danger" onclick="closeInertModal()">关闭模态框</button>
</div>
</div>
<!-- ========== 2. 微数据 itemscope/itemprop/itemtype ========== -->
<div class="section">
<h2>2. 微数据 (Microdata) — 结构化数据</h2>
<p>微数据为搜索引擎提供机器可读的结构化信息,用于<strong>富摘要(Rich Snippets)</strong>显示。</p>
<h3 style="margin:20px 0 15px;">🛒 产品微数据 (schema.org/Product)</h3>
<div itemscope itemtype="https://schema.org/Product" class="microdata-product">
<img itemprop="image" src="" alt="智能手机" style="background:linear-gradient(135deg,#667eea,#764ba2);display:flex;align-items:center;justify-content:center;color:white;font-size:40px;">
<div class="name" itemprop="name">旗舰智能手机 Pro Max</div>
<div itemprop="description" style="font-size:12px;color:#666;margin:5px 0;">6.7英寸 OLED 屏幕,512GB 存储</div>
<div class="price" itemprop="price" content="6999.00">¥6,999</div>
<div itemprop="priceCurrency" content="CNY" style="display:none;"></div>
<div class="rating" itemprop="aggregateRating" itemscope itemtype="https://schema.org/AggregateRating">
⭐ <span itemprop="ratingValue">4.8</span>
(<span itemprop="reviewCount">2,580</span> 条评价)
</div>
<link itemprop="availability" href="https://schema.org/InStock">
<span style="font-size:11px;color:#27ae60;">● 有货</span>
</div>
<div itemscope itemtype="https://schema.org/Product" class="microdata-product">
<img itemprop="image" src="" alt="无线耳机" style="background:linear-gradient(135deg,#f093fb,#f5576c);display:flex;align-items:center;justify-content:center;color:white;font-size:40px;">
<div class="name" itemprop="name">降噪无线耳机</div>
<div itemprop="description" style="font-size:12px;color:#666;margin:5px 0;">主动降噪,40小时续航</div>
<div class="price" itemprop="price" content="1299.00">¥1,299</div>
<div itemprop="priceCurrency" content="CNY" style="display:none;"></div>
<div class="rating" itemprop="aggregateRating" itemscope itemtype="https://schema.org/AggregateRating">
⭐ <span itemprop="ratingValue">4.6</span>
(<span itemprop="reviewCount">1,890</span> 条评价)
</div>
<link itemprop="availability" href="https://schema.org/InStock">
<span style="font-size:11px;color:#27ae60;">● 有货</span>
</div>
<h3 style="margin:20px 0 15px;">📅 活动微数据 (schema.org/Event)</h3>
<div itemscope itemtype="https://schema.org/Event" class="event-card">
<h4 itemprop="name" style="margin-bottom:8px;">🎵 2024 前端技术大会</h4>
<p><time itemprop="startDate" datetime="2024-06-15T09:00:00+08:00">2024年6月15日 09:00</time>
~ <time itemprop="endDate" datetime="2024-06-15T18:00:00+08:00">18:00</time></p>
<p itemprop="location" itemscope itemtype="https://schema.org/Place">
📍 <span itemprop="name">北京国际会议中心</span>,
<span itemprop="address" itemscope itemtype="https://schema.org/PostalAddress">
<span itemprop="addressLocality">北京</span>
</span>
</p>
<p style="margin-top:8px;">
<span itemprop="offers" itemscope itemtype="https://schema.org/Offer">
💰 价格: <span itemprop="price" content="299">¥299</span>
<span itemprop="priceCurrency" content="CNY"></span>
<link itemprop="availability" href="https://schema.org/InStock">
</span>
|
🔢 剩余: <span itemprop="remainingAttendeeCapacity">150</span> 位
</p>
</div>
<h3 style="margin:20px 0 15px;">👤 人物微数据 (schema.org/Person)</h3>
<div itemscope itemtype="https://schema.org/Person" style="display:flex;gap:20px;align-items:center;padding:20px;background:#f0f4ff;border-radius:10px;">
<div itemprop="image" style="width:80px;height:80px;border-radius:50%;background:linear-gradient(135deg,#667eea,#764ba2);display:flex;align-items:center;justify-content:center;color:white;font-size:30px;">👤</div>
<div>
<h4 itemprop="name" style="margin-bottom:5px;">张三 (前端工程师)</h4>
<p itemprop="jobTitle" style="color:#666;">高级前端开发工程师 @ 科技公司</p>
<p style="font-size:13px;color:#888;">
📧 <span itemprop="email">zhangsan@example.com</span> |
🌐 <span itemprop="url">https://zhangsan.dev</span>
</p>
<p itemprop="knowsAbout" style="font-size:12px;color:#666;margin-top:5px;">
技能: JavaScript, React, Vue, Node.js
</p>
</div>
</div>
</div>
<!-- ========== 3. 微数据检测 ========== -->
<div class="section">
<h2>3. 微数据检测与提取</h2>
<button class="btn-primary" onclick="extractMicrodata()">🔍 提取页面微数据</button>
<button class="btn-success" onclick="showStructuredData()">📋 查看 JSON-LD 格式</button>
<pre id="microdata-output" style="max-height:350px;margin-top:15px;">// 微数据提取结果将显示在这里...</pre>
</div>
<script>
// ===== inert 模态框 =====
function openInertModal() {
document.getElementById('inert-modal').classList.add('show');
// 将主内容设为 inert
document.querySelectorAll('.section').forEach(s => s.inert = true);
document.querySelector('h1').inert = true;
logInert('模态框打开:背景内容已设置为 inert');
}
function closeInertModal() {
document.getElementById('inert-modal').classList.remove('show');
// 移除 inert
document.querySelectorAll('.section').forEach(s => s.inert = false);
document.querySelector('h1').inert = false;
logInert('模态框关闭:背景内容已移除 inert');
}
function toggleSectionInert() {
const section = document.getElementById('inert-target-section');
const wasInert = section.inert;
section.inert = !wasInert;
const badge = document.getElementById('inert-badge');
if (section.inert) {
badge.textContent = 'INERT';
badge.className = 'badge badge-inert';
} else {
badge.textContent = '活跃';
badge.className = 'badge badge-active';
}
logInert(`#inert-target-section inert → ${section.inert} (之前: ${wasInert})`);
}
function logInert(msg) {
const log = document.getElementById('inert-log');
const time = new Date().toLocaleTimeString();
log.textContent = `[${time}] ${msg}\n\n// inert 属性效果:\n` +
`- 禁止所有鼠标点击事件\n` +
`- Tab 键无法聚焦到 inert 子树内的元素\n` +
`- 屏幕阅读器忽略 inert 区域\n` +
`- 搜索引擎可能降低 inert 内容权重`;
}
// ===== 微数据提取 =====
function extractMicrodata() {
const items = document.getItems ? document.getItems() : [];
const output = document.getElementById('microdata-output');
// 手动提取 itemscope 元素
const scopes = document.querySelectorAll('[itemscope]');
let result = `// 页面中共找到 ${scopes.length} 个微数据项 (itemscope)\n\n`;
scopes.forEach((scope, index) => {
const itemType = scope.getAttribute('itemtype') || '(未指定类型)';
result += `【项目 ${index + 1}】itemtype: ${itemType}\n`;
// 提取所有 itemprop
const props = scope.querySelectorAll('[itemprop]');
props.forEach(prop => {
const propName = prop.getAttribute('itemprop');
let propValue = '';
if (prop.tagName === 'META') {
propValue = prop.getAttribute('content');
} else if (prop.tagName === 'TIME') {
propValue = prop.getAttribute('datetime') || prop.textContent.trim();
} else if (prop.tagName === 'LINK') {
propValue = prop.getAttribute('href');
} else if (prop.hasAttribute('content')) {
propValue = prop.getAttribute('content');
} else {
propValue = prop.textContent.trim().substring(0, 50);
}
if (propValue) {
result += ` └─ ${propName}: ${propValue}\n`;
}
});
result += '\n';
});
output.textContent = result;
}
function showStructuredData() {
const output = document.getElementById('microdata-output');
output.textContent = `// 微数据对应的 JSON-LD 格式示例(供搜索引擎解析):
{
"@context": "https://schema.org",
"@type": "Product",
"name": "旗舰智能手机 Pro Max",
"description": "6.7英寸 OLED 屏幕,512GB 存储",
"image": "手机图片URL",
"offers": {
"@type": "Offer",
"price": "6999.00",
"priceCurrency": "CNY",
"availability": "https://schema.org/InStock"
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.8",
"reviewCount": "2580"
}
}
// 使用 Google 结构化数据测试工具验证:
// https://search.google.com/test/rich-results`;
}
// ESC 关闭模态框
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') closeInertModal();
});
</script>
</body>
</html>inert - 交互禁用
inert 属性用于使元素及其子元素变为"非交互"状态。
使用示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>inert 属性示例</title>
<style>
:inert {
opacity: 0.5;
pointer-events: none;
}
.modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 20px;
border: 1px solid #ccc;
z-index: 1000;
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
}
.overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.5);
z-index: 999;
}
</style>
</head>
<body>
<h1>inert 属性示例</h1>
<main id="main-content">
<p>这是主要内容区域。</p>
<button>可点击的按钮</button>
<input type="text" placeholder="可输入的文本框">
</main>
<button id="open-modal">打开模态框</button>
<div id="overlay" class="overlay" style="display:none;"></div>
<div id="modal" class="modal" style="display:none;">
<h2>模态对话框</h2>
<p>这是模态框内容。背景内容已被禁用(inert)。</p>
<button id="close-modal">关闭</button>
</div>
<script>
const openBtn = document.getElementById('open-modal');
const closeBtn = document.getElementById('close-modal');
const modal = document.getElementById('modal');
const overlay = document.getElementById('overlay');
const mainContent = document.getElementById('main-content');
openBtn.addEventListener('click', () => {
overlay.style.display = 'block';
modal.style.display = 'block';
// 使背景内容变为非交互状态
mainContent.inert = true;
});
closeBtn.addEventListener('click', () => {
overlay.style.display = 'none';
modal.style.display = 'none';
// 恢复背景内容的交互状态
mainContent.inert = false;
});
</script>
</body>
</html>popover - 弹出层
popover 属性用于创建原生的弹出层元素,无需 JavaScript 即可实现显示/隐藏功能。
使用示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>popover 属性示例</title>
<style>
[popover] {
padding: 20px;
border: 1px solid #ccc;
background: white;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
max-width: 300px;
}
button {
padding: 10px 20px;
margin: 10px;
}
</style>
</head>
<body>
<h1>popover 属性示例</h1>
<!-- 基本用法 -->
<button popovertarget="info-popover">显示信息</button>
<div id="info-popover" popover>
<h3>提示信息</h3>
<p>这是一个使用 popover 属性创建的弹出层。</p>
</div>
<!-- 手动控制 -->
<button popovertarget="tooltip-popover" popovertargetaction="toggle">
切换提示
</button>
<div id="tooltip-popover" popover="manual">
<p>这是一个工具提示,点击按钮切换显示/隐藏。</p>
</div>
<script>
// JavaScript 控制
const popover = document.getElementById('info-popover');
// 显示弹出层
// popover.showPopover();
// 隐藏弹出层
// popover.hidePopover();
// 切换显示状态
// popover.togglePopover();
// 监听事件
popover.addEventListener('toggle', (e) => {
console.log('弹出层状态:', e.newState); // 'open' 或 'closed'
});
</script>
</body>
</html>nonce - 安全随机数
nonce 是配合内容安全策略(CSP)使用的全局属性,用于安全地允许特定内联脚本或样式执行。
作用机制
当服务器返回 CSP 响应头时:
Content-Security-Policy: script-src 'self' 'nonce-rAnd0m123'只有带有匹配 nonce 值的内联脚本才会被执行:
<script nonce="rAnd0m123">
console.log("这段脚本通过 CSP 校验");
</script>安全注意事项
nonce值应由服务器端为每个响应随机生成- 不要在前端代码中硬编码固定的
nonce字符串 - 需要配合 HTTPS 使用
<a id="微数据属性"></a>
微数据属性
HTML 微数据(Microdata)是一组全局属性,用于为搜索引擎等消费方提供结构化数据。
核心属性
| 属性 | 说明 |
|---|---|
itemscope | 声明一个"项目"(item)的作用域 |
itemtype | 指定项目的类型,一般为 schema.org 等命名空间的 URL |
itemprop | 声明该项目的一个属性 |
itemid | 为项目指定全局唯一标识符 |
itemref | 引用文档中其他位置的属性节点 |
使用示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>微数据示例</title>
</head>
<body>
<article itemscope itemtype="https://schema.org/Article">
<h1 itemprop="headline">HTML 全局属性详解</h1>
<p>
作者:
<span itemprop="author" itemscope itemtype="https://schema.org/Person">
<span itemprop="name">张三</span>
</span>
</p>
<time itemprop="datePublished" datetime="2025-01-01">2025-01-01</time>
</article>
</body>
</html>使用建议
- 结构化数据推荐使用 JSON-LD
- 如果已有大量微数据标注,可以继续沿用
- 尽量在关键内容上使用少量语义清晰的属性
<a id="属性对比"></a>
属性对比
id vs class
| 特性 | id | class |
|---|---|---|
| 唯一性 | 文档中唯一 | 可重复使用 |
| 数量限制 | 每个元素只能有一个 | 可以有多个 |
| CSS 选择器 | #id | .class |
| JS 获取 | getElementById() | getElementsByClassName() |
| 优先级 | 更高 | 较低 |
| 用途 | 唯一标识、锚点 | 样式分组、批量操作 |
hidden vs display:none
| 特性 | hidden | display: none |
|---|---|---|
| 语义 | 表示元素当前不可见 | 纯粹的样式控制 |
| 可访问性 | 屏幕阅读器忽略 | 取决于实现 |
| JS 操作 | element.hidden | element.style.display |
| 性能 | 更高效 | 需要样式计算 |
tabindex 值对比
| 值 | Tab 导航 | 编程聚焦 | 推荐度 |
|---|---|---|---|
| 省略 | 自然顺序 | ✅ | ✅ 推荐 |
0 | 自然顺序 | ✅ | ✅ 推荐 |
-1 | 不参与 | ✅ | ✅ 推荐 |
| 正整数 | 自定义顺序 | ✅ | ❌ 不推荐 |
<a id="javascript-api-参考"></a>
JavaScript API 参考
通用 API
// 获取属性值
element.getAttribute('data-user-id');
// 设置属性值
element.setAttribute('data-user-id', '12345');
// 移除属性
element.removeAttribute('data-user-id');
// 检查属性是否存在
element.hasAttribute('data-user-id');
// 获取所有属性
element.attributes;特定属性 API
// id
element.id = 'newId';
// class
element.className = 'class1 class2';
element.classList.add('class3');
element.classList.remove('class1');
element.classList.toggle('active');
element.classList.contains('active');
// style
element.style.color = 'red';
element.style.cssText = 'color: red; background: blue;';
window.getComputedStyle(element);
// hidden
element.hidden = true;
// contenteditable
element.contentEditable = 'true';
element.isContentEditable;
// draggable
element.draggable = true;
// tabindex
element.tabIndex = 0;
// data-*
element.dataset.userId = '12345';
delete element.dataset.userId;
// lang
element.lang = 'zh-CN';
// dir
element.dir = 'rtl';
// spellcheck
element.spellcheck = true;
// translate
element.translate = false;
// inert
element.inert = true;<a id="最佳实践"></a>
最佳实践
1. 属性选择建议
| 场景 | 推荐属性 | 原因 |
|---|---|---|
| 唯一标识元素 | id | CSS/JS 选择、锚点跳转 |
| 样式分组 | class | 可复用、易维护 |
| 动态样式 | style(谨慎使用) | 仅用于动态计算的值 |
| 存储数据 | data-* | 标准化、易于访问 |
| 隐藏元素 | hidden | 语义清晰、性能好 |
2. 可访问性清单
- 为交互元素提供
title或aria-label - 使用
lang属性指定内容语言 - 使用
tabindex管理焦点顺序(优先使用0或-1) - 使用
hidden而不是style="display:none"隐藏内容 - 为自定义控件添加键盘支持
3. 性能优化
- 避免大量使用内联
style - 使用
data-*存储数据,而不是自定义属性 - 合理使用
contenteditable,避免在大型文档上使用 - 使用
hidden属性而不是 CSS 隐藏元素
4. 国际化
- 始终在
<html>标签上设置lang属性 - 为不同语言的内容区域设置相应的
lang值 - 使用
dir属性支持 RTL 语言 - 使用
translate属性控制翻译行为
5. 移动端优化
- 使用
inputmode优化虚拟键盘 - 使用
enterkeyhint提升用户体验 - 使用
autocapitalize控制大小写 - 谨慎使用
autofocus
6. 代码组织
<!-- ✅ 推荐 -->
<div
id="main-container"
class="container primary"
lang="zh-CN"
data-theme="dark"
data-user-id="12345">
<h1 title="主标题">欢迎</h1>
</div>
<!-- ❌ 避免 -->
<div
id="main-container"
class="container primary"
style="color: red; font-size: 16px;"
data-style-color="red"
data-style-font-size="16px">
<!-- 样式应该放在 CSS 中,而不是 data-* 属性中 -->
</div><a id="常见问题"></a>
常见问题
1. id 重复会怎样?
问题:文档中有两个相同的 id 值。
影响:
getElementById()只返回第一个元素- 锚点链接可能跳转到错误的元素
- CSS 选择器可能应用到错误的元素
解决方案:
// 检查 ID 是否重复
function checkDuplicateIds() {
const ids = document.querySelectorAll('[id]');
const idMap = {};
ids.forEach(element => {
const id = element.id;
if (idMap[id]) {
console.warn(`重复的 ID: ${id}`, element);
} else {
idMap[id] = element;
}
});
}
checkDuplicateIds();2. class 名冲突怎么办?
问题:不同组件使用了相同的 class 名。
解决方案:
<!-- 使用命名空间 -->
<div class="component-name__element--modifier"></div>
<!-- 使用 BEM 方法 -->
<div class="card">
<div class="card__header"></div>
<div class="card__body"></div>
</div>
<!-- 使用 CSS Modules(构建工具支持) -->
<div class="${styles.container}"></div>3. tabindex 使用误区
问题:使用正整数 tabindex。
影响:
- 破坏自然的 DOM 导航顺序
- 导致可访问性问题
- 维护困难
解决方案:
<!-- ❌ 避免 -->
<div tabindex="1">第一个</div>
<div tabindex="3">第三个</div>
<div tabindex="2">第二个</div>
<!-- ✅ 推荐:调整 DOM 结构 -->
<div>第一个</div>
<div>第二个</div>
<div>第三个</div>
<!-- ✅ 推荐:使用 tabindex="0" -->
<div tabindex="0">自定义控件</div>4. data-* 命名转换问题
问题:JavaScript 中访问 data-* 属性时名称转换。
解决方案:
<div id="example" data-user-id="123" data-userName="张三"></div>
<script>
const element = document.getElementById('example');
// ✅ 正确:连字符转为驼峰
console.log(element.dataset.userId); // '123'
// ❌ 错误:直接使用原名称
// console.log(element.dataset.user-id); // 错误
// ⚠️ 注意:HTML 中的大小写会被转为小写
// HTML: data-userName
// JS: dataset.username(不是 userName)
console.log(element.dataset.username); // '张三'
</script>5. hidden vs display:none 选择
问题:何时使用 hidden,何时使用 display: none?
决策指南:
是否需要语义表示"当前不可见"?
├─ 是 → 使用 hidden 属性
└─ 否 → 使用 display: none
↓
是否需要动画过渡?
├─ 是 → 使用 CSS(配合 visibility 或 opacity)
└─ 否 → 使用 display: none6. tabindex=-1 不生效
问题:设置了 tabindex="-1" 但仍能通过 Tab 聚焦。
原因:
- 元素本身是可聚焦元素(如
<input>、<button>) - 浏览器默认行为
解决方案:
<!-- ❌ 不会阻止 Tab 聚焦 -->
<input type="text" tabindex="-1">
<!-- ✅ 需要同时设置 disabled -->
<input type="text" tabindex="-1" disabled>
<!-- ✅ 或使用 hidden -->
<input type="text" hidden><a id="浏览器兼容性"></a>
浏览器兼容性
| 属性 | Chrome | Firefox | Safari | Edge | 备注 |
|---|---|---|---|---|---|
id, class, style | ✅ | ✅ | ✅ | ✅ | 完全支持 |
data-* | ✅ | ✅ | ✅ | ✅ | 完全支持 |
hidden | ✅ | ✅ | ✅ | ✅ | 完全支持 |
contenteditable | ✅ | ✅ | ✅ | ✅ | 完全支持 |
draggable | ✅ | ✅ | ✅ | ✅ | 完全支持 |
tabindex | ✅ | ✅ | ✅ | ✅ | 完全支持 |
translate | ✅ | ⚠️ | ✅ | ✅ | 部分支持 |
inert | ✅ | ⚠️ | ✅ | ✅ | 较新特性 |
popover | ✅ | ⚠️ | ✅ | ✅ | 较新特性 |
inputmode | ✅ | ✅ | ✅ | ✅ | 移动端支持好 |
enterkeyhint | ✅ | ⚠️ | ✅ | ✅ | 移动端支持好 |
slot, part | ✅ | ✅ | ✅ | ✅ | Web Components |
查询工具:
参考资料
官方文档
相关规范
更新日志
| 版本 | 日期 | 更新内容 |
|---|---|---|
| 2.0 | 2026-02-12 | 重构文档结构,添加分类导航、对比表格、JavaScript API、最佳实践、常见问题 |
| 1.0 | 2023-05-23 | 初始版本 |
补充示例
<h4>042-data-custom-attributes.html</h4><!DOCTYPE html>
<!--
来源:基础知识/7-全局属性.md
演示:data-* 自定义数据属性(dataset API 读写、CSS attr() 引用)
-->
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>data-* 自定义数据属性演示</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; padding: 30px; background: #f5f5f5; }
h1 { color: #333; margin-bottom: 20px; }
.section { background: white; padding: 20px; margin-bottom: 20px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
h2 { color: #555; margin-bottom: 15px; font-size: 18px; }
/* 使用 CSS attr() 函数引用 data 属性 */
.card {
padding: 15px;
margin: 10px 0;
border-left: 4px solid;
position: relative;
}
.card::after {
content: attr(data-tooltip);
display: none;
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
background: #333;
color: white;
padding: 5px 10px;
border-radius: 4px;
font-size: 12px;
white-space: nowrap;
margin-bottom: 5px;
}
.card:hover::after { display: block; }
.card[data-priority="high"] { border-color: #e74c3c; background: #fef5f5; }
.card[data-priority="medium"] { border-color: #f39c12; background: #fef9f0; }
.card[data-priority="low"] { border-color: #27ae60; background: #f0fdf4; }
.product {
display: inline-block;
width: 120px;
text-align: center;
padding: 10px;
margin: 5px;
background: #fff;
border: 2px solid #ddd;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s;
}
.product:hover { transform: translateY(-3px); box-shadow: 0 4px 12px rgba(0,0,0,0.15); }
/* 通过 data-price 显示价格 */
.product .price-tag::before {
content: '¥' attr(data-price);
font-weight: bold;
color: #e74c3c;
font-size: 18px;
}
code { background: #f4f4f4; padding: 2px 6px; border-radius: 3px; font-size: 13px; }
pre { background: #2d2d2d; color: #f8f8f2; padding: 15px; border-radius: 6px; overflow-x: auto; font-size: 13px; line-height: 1.5; }
.output { background: #e8f5e9; padding: 10px; border-radius: 4px; margin-top: 10px; font-family: monospace; }
button { padding: 8px 16px; margin: 5px; border: none; border-radius: 4px; cursor: pointer; background: #3498db; color: white; }
button:hover { background: #2980b9; }
</style>
</head>
<body>
<h1>📦 data-* 自定义数据属性演示</h1>
<!-- 示例1:CSS attr() 引用 -->
<div class="section">
<h2>1. CSS attr() 函数引用 data 属性</h2>
<p>鼠标悬停查看 tooltip(通过 <code>data-tooltip</code> 属性):</p>
<div class="card" data-priority="high" data-tooltip="紧急任务!需要立即处理">
<strong>高优先级任务</strong> - 系统升级维护
</div>
<div class="card" data-priority="medium" data-tooltip="本周内完成即可">
<strong>中优先级任务</strong> - 文档更新
</div>
<div class="card" data-priority="low" data-tooltip="有空再做">
<strong>低优先级任务</strong> - 整理代码注释
</div>
</div>
<!-- 示例2:商品价格展示 -->
<div class="section">
<h2>2. 商品卡片(data-price + CSS attr)</h2>
<div class="product" data-id="001" data-category="electronics" data-price="2999">
<div>智能手机</div>
<div class="price-tag"></div>
</div>
<div class="product" data-id="002" data-category="electronics" data-price="4999">
<div>笔记本电脑</div>
<div class="price-tag"></div>
</div>
<div class="product" data-id="003" data-category="accessories" data-price="199">
<div>无线耳机</div>
<div class="price-tag"></div>
</div>
</div>
<!-- 示例3:JavaScript dataset API -->
<div class="section">
<h2>3. JavaScript dataset API 读写操作</h2>
<div id="user-card"
data-user-id="10086"
data-user-name="张三"
data-user-email="zhangsan@example.com"
data-user-role="admin"
data-login-count="42"
data-preferences='{"theme":"dark","lang":"zh-CN"}'
style="padding:15px; background:#f0f4ff; border-radius:8px;">
<strong>用户信息卡</strong>
</div>
<br>
<button onclick="readDataset()">读取所有 data 属性</button>
<button onclick="modifyDataset()">修改数据</button>
<button onclick="addNewData()">添加新属性</button>
<button onclick="deleteData()">删除属性</button>
<pre id="dataset-output">// 点击按钮查看结果</pre>
</div>
<!-- 示例4:动态样式控制 -->
<div class="section">
<h2>4. 通过 data 属性动态控制样式</h2>
<div id="theme-demo" data-theme="light" data-font-size="16" style="padding:20px; border-radius:8px; transition:all 0.3s;">
<p>这是一段可自定义样式的文本。</p>
<p>通过修改 data 属性来改变外观。</p>
</div>
<br>
<button onclick="changeTheme('light')">浅色主题</button>
<button onclick="changeTheme('dark')">深色主题</button>
<button onclick="changeTheme('blue')">蓝色主题</button>
<button onclick="changeFontSize(14)">小字体</button>
<button onclick="changeFontSize(18)">大字体</button>
</div>
<script>
// 读取 dataset
function readDataset() {
const el = document.getElementById('user-card');
const output = document.getElementById('dataset-output');
output.textContent = `
// dataset 对象(自动转换命名规则)
const data = el.dataset;
// 读取结果:
userId = "${el.dataset.userId}" // data-user-id → userId
userName = "${el.dataset.userName}" // data-user-name → userName
userEmail = "${el.dataset.userEmail}" // data-user-email → userEmail
userRole = "${el.dataset.userRole}" // data-user-role → role
loginCount = "${el.dataset.loginCount}" // data-login-count → loginCount (数字字符串)
preferences= ${el.dataset.preferences} // JSON 字符串
// 原始 attributes 方式访问:
el.getAttribute('data-user-id') = "${el.getAttribute('data-user-id')}"
`;
}
// 修改 dataset
function modifyDataset() {
const el = document.getElementById('user-card');
el.dataset.userName = '李四';
el.dataset.userEmail = 'lisi@example.com';
el.dataset.loginCount = '100';
el.dataset.userRole = 'super-admin';
const output = document.getElementById('dataset-output');
output.textContent = `// 修改后的值:
el.dataset.userName = "${el.dataset.userName}"
el.dataset.userEmail = "${el.dataset.userEmail}"
el.dataset.loginCount = "${el.dataset.loginCount}"
el.dataset.userRole = "${el.dataset.userRole}"
// 注意:DOM 属性会同步更新:
el.getAttribute('data-user-name') = "${el.getAttribute('data-user-name')}"
el.getAttribute('data-user-role') = "${el.getAttribute('data-user-role')}"`
}
// 添加新属性
function addNewData() {
const el = document.getElementById('user-card');
el.dataset.lastLogin = '2024-01-15T08:30:00';
el.dataset.isActive = 'true';
el.dataset.score = '98.5';
const output = document.getElementById('dataset-output');
output.textContent = `// 添加的新属性:
el.dataset.lastLogin = "${el.dataset.lastLogin}"
el.dataset.isActive = "${el.dataset.isActive}"
el.dataset.score = "${el.dataset.score}"
// HTML 中已添加:
// data-last-login="2024-01-15T08:30:00"
// data-is-active="true"
// data-score="98.5"`
}
// 删除属性
function deleteData() {
const el = document.getElementById('user-card');
delete el.dataset.userRole;
delete el.dataset.preferences;
const output = document.getElementById('dataset-output');
output.textContent = `// 删除后:
el.dataset.userRole = ${el.dataset.userRole} // undefined
el.dataset.preferences = ${el.dataset.preferences} // undefined
el.hasAttribute('data-user-role') = ${el.hasAttribute('data-user-role')}
el.hasAttribute('data-preferences') = ${el.hasAttribute('data-preferences')}`;
}
// 动态主题切换
function changeTheme(theme) {
const el = document.getElementById('theme-demo');
el.dataset.theme = theme;
const themes = {
light: { bg: '#ffffff', color: '#333', border: '#ddd' },
dark: { bg: '#1a1a2e', color: '#eee', border: '#16213e' },
blue: { bg: '#e3f2fd', color: '#1565c0', border: '#90caf9' }
};
const t = themes[theme];
el.style.background = t.bg;
el.style.color = t.color;
el.style.border = `2px solid ${t.border}`;
}
function changeFontSize(size) {
const el = document.getElementById('theme-demo');
el.dataset.fontSize = size;
el.style.fontSize = size + 'px';
}
</script>
</body>
</html><!DOCTYPE html>
<!--
来源:基础知识/7-全局属性.md
演示:title 属性悬停提示(tooltip 效果对比原生 title)、tabindex 键盘导航
-->
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>title 与 tabindex 属性演示</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, sans-serif; padding: 30px; background: #f5f7fa; }
h1 { color: #2c3e50; margin-bottom: 25px; }
.section { background: white; padding: 25px; margin-bottom: 25px; border-radius: 10px; box-shadow: 0 2px 12px rgba(0,0,0,0.08); }
h2 { color: #34495e; margin-bottom: 15px; font-size: 17px; padding-bottom: 8px; border-bottom: 2px solid #ecf0f1; }
/* Tooltip 样式 */
.tooltip-wrap { position: relative; display: inline-block; margin: 10px; }
.tooltip-wrap .custom-tooltip {
visibility: hidden;
position: absolute;
bottom: 125%;
left: 50%;
transform: translateX(-50%);
background: #2c3e50;
color: white;
padding: 8px 14px;
border-radius: 6px;
font-size: 13px;
white-space: nowrap;
opacity: 0;
transition: opacity 0.3s, visibility 0.3s;
z-index: 100;
pointer-events: none;
}
.tooltip-wrap .custom-tooltip::after {
content: '';
position: absolute;
top: 100%;
left: 50%;
margin-left: -6px;
border-width: 6px;
border-style: solid;
border-color: #2c3e50 transparent transparent transparent;
}
.tooltip-wrap:hover .custom-tooltip,
.tooltip-wrap:focus .custom-tooltip {
visibility: visible;
opacity: 1;
}
.native-title { padding: 12px 20px; background: #ecf0f1; border-radius: 6px; cursor: help; }
.custom-title { padding: 12px 20px; background: #3498db; color: white; border-radius: 6px; cursor: pointer; border: none; }
/* tabindex 演示 */
.tab-item {
display: inline-block;
padding: 15px 25px;
margin: 8px;
background: white;
border: 2px solid #bdc3c7;
border-radius: 8px;
cursor: pointer;
transition: all 0.2s;
font-size: 14px;
}
.tab-item:focus {
outline: 3px solid #3498db;
outline-offset: 2px;
border-color: #3498db;
background: #ebf5fb;
transform: scale(1.05);
}
.tab-item.tab-focusable { border-color: #27ae60; }
.tab-item.tab-negative { border-color: #e74c3c; opacity: 0.7; }
.tab-item.tab-positive { border-color: #f39c12; }
code { background: #f4f4f4; padding: 2px 6px; border-radius: 3px; font-size: 13px; }
pre { background: #282c34; color: #abb2bf; padding: 15px; border-radius: 6px; overflow-x: auto; font-size: 13px; line-height: 1.5; }
table { width: 100%; border-collapse: collapse; margin: 15px 0; font-size: 14px; }
th, td { padding: 10px 12px; text-align: left; border: 1px solid #ddd; }
th { background: #34495e; color: white; }
tr:nth-child(even) { background: #f9f9f9; }
.focus-indicator {
position: fixed;
top: 10px; right: 10px;
background: #2c3e50;
color: white;
padding: 10px 15px;
border-radius: 6px;
font-size: 13px;
z-index: 999;
}
</style>
</head>
<body>
<h1>💡 title 提示 & tabindex 键盘导航</h1>
<div class="focus-indicator" id="focus-indicator">当前焦点:无</div>
<!-- ========== title 属性演示 ========== -->
<div class="section">
<h2>1. title 属性 — 原生 vs 自定义 Tooltip 对比</h2>
<p><strong>原生 title 属性(鼠标悬停查看,样式由浏览器决定):</strong></p>
<div style="display:flex; gap:15px; flex-wrap:wrap; margin:15px 0;">
<span class="native-title" title="这是浏览器原生的提示框,样式不可自定义,延迟约1-2秒才显示">
🖱️ 悬停我(原生 title)
</span>
<span class="native-title" title="支持多行文本 第二行内容 第三行内容">
📝 多行提示(原生)
</span>
<input type="text" placeholder="输入框的 title" title="请输入您的用户名,长度3-20个字符" style="padding:10px;border-radius:6px;border:1px solid #ccc;">
</div>
<p><strong>自定义 Tooltip(基于 title 数据 + CSS/JS 实现):</strong></p>
<div style="display:flex; gap:15px; flex-wrap:wrap; margin:15px 0;">
<div class="tooltip-wrap">
<button class="custom-title" data-tooltip="✨ 自定义样式!立即显示、支持富文本、完全可控">
🎨 悬停我(自定义)
</button>
<span class="custom-tooltip">✨ 自定义样式!立即显示、支持富文本、完全可控</span>
</div>
<div class="tooltip-wrap">
<button class="custom-title" data-tooltip="⚠️ 警告:此操作不可撤销!" style="background:#e74c3c;">
⚠️ 危险操作
</button>
<span class="custom-tooltip" style="background:#c0392b;">⚠️ 警告:此操作不可撤销!</span>
</div>
<div class="tooltip-wrap">
<span style="padding:12px 20px;background:#9b59b6;color:white;border-radius:6px;cursor:pointer;display:inline-block;"
data-tooltip="ℹ️ 信息提示:支持 HTML 内容">
ℹ️ 信息图标
</span>
<span class="custom-tooltip" style="background:#8e44ad;">ℹ️ 信息提示:支持 HTML 内容</span>
</div>
</div>
<table>
<tr><th>特性</th><th>原生 title</th><th>自定义 Tooltip</th></tr>
<tr><td>显示延迟</td><td>~1-2 秒</td><td>即时(CSS transition)</td></tr>
<tr><td>样式控制</td><td>❌ 浏览器决定</td><td>✅ 完全自定义</td></tr>
<tr><td>富文本支持</td><td>⚠️ 仅纯文本</td><td>✅ 支持 HTML</td></tr>
<tr><td>无障碍</td><td>✅ 屏幕阅读器识别</td><td>需额外添加 aria-label</td></tr>
<tr><td>移动端</td><td>❌ 不触发(无 hover)</td><td>✅ 可适配 click/tap</td></tr>
<tr><td>实现复杂度</td><td>零</td><td>需 CSS + JS</td></tr>
</table>
</div>
<!-- ========== tabindex 演示 ========== -->
<div class="section">
<h2>2. tabindex 属性 — 键盘导航顺序控制</h2>
<p><strong>按 Tab 键测试焦点顺序(注意观察焦点框和右上角指示器):</strong></p>
<div style="margin:20px 0;">
<p style="margin-bottom:10px;color:#666;">▼ 正序 tabindex(1, 2, 3...)— 不推荐,会打乱自然顺序:</p>
<div class="tab-item tab-positive" tabindex="3">第三位 (tabindex=3)</div>
<div class="tab-item tab-positive" tabindex="1">第一位 (tabindex=1)</div>
<div class="tab-item tab-positive" tabindex="2">第二位 (tabindex=2)</div>
</div>
<div style="margin:20px 0;">
<p style="margin-bottom:10px;color:#666;">▼ tabindex="0" — 加入正常 Tab 流(推荐):</p>
<div class="tab-item tab-focusable" tabindex="0">普通可聚焦元素 A</div>
<div class="tab-item tab-focusable" tabindex="0">普通可聚焦元素 B</div>
<div class="tab-item tab-focusable" tabindex="0">普通可聚焦元素 C</div>
</div>
<div style="margin:20px 0;">
<p style="margin-bottom:10px;color:#666;">▼ tabindex="-1" — 只能编程聚焦,不参与 Tab 导航:</p>
<div class="tab-item tab-negative" tabindex="-1" id="special-focus">特殊元素(Tab 跳过)</div>
<button class="tab-item" style="background:#3498db;color:white;border:none;" onclick="focusSpecial()">
👆 点击让我聚焦到上面的特殊元素
</button>
</div>
<table>
<tr><th>tabindex 值</th><th>行为</th><th>使用场景</th><th>推荐度</th></tr>
<tr><td><code>正数 (1,2,3...)</code></td><td>按数值从小到大聚焦</td><td> legacy 代码兼容</td><td>❌ 不推荐</td></tr>
<tr><td><code>0</code></td><td>加入文档顺序的 Tab 流</td><td>使 div/span 可聚焦</td><td>✅ 推荐</td></tr>
<tr><td><code>-1</code></td><td>不参与 Tab,仅编程聚焦</td><td>模态框、跳转目标</td><td>✅ 特定场景</td></tr>
<tr><td><code>(不设置)</code></td><td>默认行为</td><td>交互元素天然可聚焦</td><td>✅ 默认</td></tr>
</table>
</div>
<!-- ========== accesskey 演示 ========== -->
<div class="section">
<h2>3. accesskey 快捷键属性</h2>
<p><strong>快捷键说明:</strong>不同操作系统组合键不同(Windows: Alt+key, Mac: Ctrl+Option+key)</p>
<div style="margin:15px 0;">
<button accesskey="s" class="tab-item tab-focusable" style="border-color:#3498db;">💾 保存 (Alt+S)</button>
<button accesskey="n" class="tab-item tab-focusable" style="border-color:#27ae60;">➕ 新建 (Alt+N)</button>
<button accesskey="h" class="tab-item tab-focusable" style="border-color:#e74c3c;">❓ 帮助 (Alt+H)</button>
<a href="#" accesskey="t" class="tab-item tab-focusable" style="border-color:#9b59b6;text-decoration:none;">⬆️ 返回顶部 (Alt+T)</a>
</div>
<pre style="font-size:12px;">// 浏览器通常会在菜单或工具栏显示可用快捷键列表
// 实际使用率较低,但有助于无障碍访问</pre>
</div>
<script>
// 焦点跟踪
document.addEventListener('focusin', (e) => {
const indicator = document.getElementById('focus-indicator');
const tag = e.target.tagName.toLowerCase();
const id = e.target.id ? `#${e.target.id}` : '';
const cls = e.target.className ? `.${e.target.className.split(' ')[0]}` : '';
const tabIndex = e.target.getAttribute('tabindex');
indicator.textContent = `当前焦点:<${tag}${id}${cls}> tabindex=${tabIndex ?? '默认'}`;
// 高亮当前元素
e.target.style.transition = 'box-shadow 0.2s';
});
// 编程聚焦到 tabindex="-1" 元素
function focusSpecial() {
const el = document.getElementById('special-focus');
el.focus();
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
</script>
</body>
</html><!DOCTYPE html>
<!--
来源:基础知识/7-全局属性.md
演示:role ARIA 角色属性、aria-label / aria-labelledby / aria-describedby 无障碍属性
-->
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>ARIA 无障碍属性演示</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, sans-serif; padding: 30px; background: #fafafa; }
h1 { color: #1a1a1a; margin-bottom: 25px; }
.section { background: white; padding: 25px; margin-bottom: 25px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.06); }
h2 { color: #333; margin-bottom: 15px; font-size: 17px; border-bottom: 2px solid #eee; padding-bottom: 8px; }
/* ARIA 角色演示样式 */
[role="navigation"] { background: #2c3e50; color: white; padding: 15px 20px; border-radius: 8px; }
[role="navigation"] a { color: #3498db; margin-right: 20px; text-decoration: none; }
[role="navigation"] a:hover { text-decoration: underline; }
[role="main"] { min-height: 200px; background: #f8f9fa; padding: 20px; border-radius: 8px; border: 2px dashed #dee2e6; }
[role="banner"] { background: linear-gradient(135deg, #667eea, #764ba2); color: white; padding: 30px; border-radius: 10px; text-align: center; }
[role="complementary"] { background: #fff3cd; padding: 20px; border-radius: 8px; border-left: 4px solid #ffc107; }
[role="contentinfo"] { background: #343a40; color: #adb5bd; padding: 20px; border-radius: 8px; text-align: center; }
[role="search"] { display: flex; gap: 10px; max-width: 500px; }
[role="search"] input { flex: 1; padding: 10px 15px; border: 2px solid #dee2e6; border-radius: 6px; font-size: 14px; }
[role="search"] button { padding: 10px 20px; background: #007bff; color: white; border: none; border-radius: 6px; cursor: pointer; }
[role="button"] { display: inline-flex; align-items: center; gap: 8px; padding: 10px 20px; background: #28a745; color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; }
[role="button"]:hover { background: #218838; }
[role="button"]:focus { outline: 3px solid #28a745; outline-offset: 2px; }
[role="alert"] { background: #f8d7da; color: #721c24; padding: 15px; border-radius: 6px; border-left: 4px solid #dc3545; margin: 10px 0; }
[role="status"] { background: #d4edda; color: #155724; padding: 15px; border-radius: 6px; border-left: 4px solid #28a745; margin: 10px 0; }
[role="progressbar"] { width: 100%; height: 20px; background: #e9ecef; border-radius: 10px; overflow: hidden; }
[role="progressbar"] .bar { height: 100%; background: linear-gradient(90deg, #28a745, #20c997); width: 0%; transition: width 1s; }
[role="dialog"] { display: none; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 30px; border-radius: 12px; box-shadow: 0 10px 40px rgba(0,0,0,0.3); z-index: 1000; min-width: 400px; }
[role="dialog"].show { display: block; }
[role="dialog"] [role="document"] { outline: none; }
code { background: #f4f4f4; padding: 2px 6px; border-radius: 3px; font-size: 13px; color: #d63384; }
pre { background: #1e1e1e; color: #d4d4d4; padding: 15px; border-radius: 6px; overflow-x: auto; font-size: 12px; line-height: 1.6; }
.sr-only {
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}
table { width: 100%; border-collapse: collapse; font-size: 13px; margin: 15px 0; }
th, td { padding: 10px; text-align: left; border: 1px solid #dee2e6; }
th { background: #f8f9fa; font-weight: 600; }
button { padding: 8px 16px; margin: 5px; border: none; border-radius: 6px; cursor: pointer; background: #6c757d; color: white; font-size: 13px; }
button:hover { opacity: 0.85; }
</style>
</head>
<body>
<h1>♿ ARIA 无障碍属性完整演示</h1>
<!-- ========== 1. 语义化角色 landmarks ========== -->
<div class="section">
<h2>1. ARIA Landmark 角色(页面结构语义化)</h2>
<div role="banner">
<h1>🏛️ role="banner" — 页面横幅/头部</h1>
<p>屏幕阅读器用户可快速导航到此区域</p>
</div>
<div role="navigation" aria-label="主导航" style="margin:15px 0;">
<a href="#">首页</a>
<a href="#">产品</a>
<a href="#">关于</a>
<a href="#">联系</a>
<span style="float:right;color:#95a5a6;">role="navigation" + aria-label="主导航"</span>
</div>
<div style="display:flex;gap:20px;margin:15px 0;">
<div role="main" style="flex:1;">
<h2>📄 role="main" — 主要内容区</h2>
<p>页面的核心内容放在这里。每个页面只能有一个 main 角色。</p>
<p>屏幕阅读器提供了直接跳转到主内容的快捷键(如 NVDA 的 M 键)。</p>
</div>
<div role="complementary" style="width:250px;">
<h3>📎 role="complementary"</h3>
<p>辅助内容区域,如侧边栏、广告位等。</p>
</div>
</div>
<div role="contentinfo">
<p>© 2024 role="contentinfo" — 页脚/版权信息</p>
</div>
</div>
<!-- ========== 2. 交互组件角色 ========== -->
<div class="section">
<h2>2. 组件级 ARIA 角色</h2>
<div style="display:flex; flex-wrap:wrap; gap:15px; align-items:flex-start;">
<!-- 按钮 -->
<div>
<p style="margin-bottom:8px;font-size:13px;color:#666;">role="button"(让 div 表现得像按钮):</p>
<div role="button" tabindex="0" aria-label="点击下载文件" onclick="alert('下载!')" onkeydown="if(event.key==='Enter'||event.key===' ')this.click()">
📥 下载文件
</div>
</div>
<!-- 搜索 -->
<div>
<p style="margin-bottom:8px;font-size:13px;color:#666;">role="search":</p>
<div role="search" aria-label="站内搜索">
<input type="text" aria-label="搜索关键词" placeholder="搜索...">
<button>搜索</button>
</div>
</div>
<!-- 提示 -->
<div style="flex:1;min-width:300px;">
<p style="margin-bottom:8px;font-size:13px;color:#666;">实时反馈角色:</p>
<div role="alert" aria-live="assertive" id="alert-box">
⚠️ 这是一个 alert 提示(会被朗读)
</div>
<div role="status" aria-live="polite" id="status-box">
✅ 这是一个 status 状态(空闲时朗读)
</div>
<div style="margin-top:10px;">
<label>进度:</label>
<div role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" aria-label="加载进度" id="progress-bar">
<div class="bar"></div>
</div>
</div>
<button onclick="simulateProgress()" style="margin-top:8px;background:#007bff;">模拟进度更新</button>
</div>
</div>
</div>
<!-- ========== 3. aria-label / labelledby / describedby ========== -->
<div class="section">
<h2>3. 无障碍标签属性对比</h2>
<div style="display:flex;gap:20px;flex-wrap:wrap;">
<!-- aria-label -->
<div style="flex:1;min-width:280px;padding:15px;background:#e3f2fd;border-radius:8px;">
<h3 style="color:#1565c0;margin-bottom:10px;">aria-label</h3>
<p style="font-size:13px;margin-bottom:10px;">直接提供标签文本(不可见):</p>
<button aria-label="关闭对话框" style="padding:15px 20px;font-size:20px;background:#ef5350;color:white;border:none;border-radius:6px;cursor:pointer;">×</button>
<pre style="margin-top:10px;font-size:11px;"><button aria-label="关闭对话框">
×
</button>
// 屏幕阅读器读作:"关闭对话框 按钮"</pre>
</div>
<!-- aria-labelledby -->
<div style="flex:1;min-width:280px;padding:15px;background:#fce4ec;border-radius:8px;">
<h3 style="color:#c62828;margin-bottom:10px;">aria-labelledby</h3>
<p style="font-size:13px;margin-bottom:10px;">引用其他元素的文本作为标签:</p>
<h3 id="form-title" style="font-size:14px;">用户注册表单</h3>
<form aria-labelledby="form-title" style="background:white;padding:15px;border-radius:6px;">
<input type="text" placeholder="用户名" style="padding:8px;width:100%;margin-bottom:8px;border:1px solid #ddd;border-radius:4px;">
</form>
<pre style="margin-top:10px;font-size:11px;"><h3 id="form-title">用户注册表单</h3>
<form aria-labelledby="form-title">
// 屏幕阅读器:"用户注册表单 表单"</pre>
</div>
<!-- aria-describedby -->
<div style="flex:1;min-width:280px;padding:15px;background:#e8f5e9;border-radius:8px;">
<h3 style="color:#2e7d32;margin-bottom:10px;">aria-describedby</h3>
<p style="font-size:13px;margin-bottom:10px;">引用描述文本(补充说明):</p>
<label for="pwd" style="font-size:13px;">密码:</label><br>
<input type="password" id="pwd" aria-describedby="pwd-help" style="padding:8px;width:100%;margin-top:5px;border:1px solid #ddd;border-radius:4px;">
<small id="pwd-help" style="color:#666;font-size:12px;">密码长度至少8位,需包含字母和数字</small>
<pre style="margin-top:10px;font-size:11px;"><input aria-describedby="pwd-help">
<small id="pwd-help">描述文字</small>
// 读作:"密码 编辑文本 密码长度至少..."</pre>
</div>
</div>
<table style="margin-top:20px;">
<tr><th>属性</th><th>作用</th><th>何时使用</th></tr>
<tr><td><code>aria-label</code></td><td>直接设置标签文本</td><td>没有可见标签时;图标按钮</td></tr>
<tr><td><code>aria-labelledby</code></td><td>引用可见元素的文本</td><td>已有标题/标签元素时</td></tr>
<tr><td><code>aria-describedby</code></td><td>引用描述性文本</td><td>需要额外说明/帮助文本时</td></tr>
</table>
</div>
<!-- ========== 4. 对话框与焦点陷阱 ========== -->
<div class="section">
<h2>4. role="dialog" 模态对话框</h2>
<button onclick="openDialog()" style="background:#6f42c1;">打开对话框</button>
<div role="dialog" aria-modal="true" aria-labelledby="dialog-title" id="my-dialog">
<div role="document">
<h2 id="dialog-title">确认删除</h2>
<p id="dialog-desc">此操作无法撤销,确定要继续吗?</p>
<div style="margin-top:20px;display:flex;justify-content:flex-end;gap:10px;">
<button onclick="closeDialog()" style="background:#6c757d;">取消</button>
<button onclick="closeDialog()" style="background:#dc3545;">确认删除</button>
</div>
</div>
</div>
<div id="dialog-overlay" style="display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);z-index:999;"></div>
</div>
<!-- ========== 5. 屏幕阅读器检测面板 ========== -->
<div class="section">
<h2>5. ARIA 属性检测器</h2>
<p>点击下方按钮扫描页面中的 ARIA 属性:</p>
<button onclick="scanAriaAttributes()" style="background:#17a2b8;">🔍 扫描 ARIA 属性</button>
<pre id="aria-scan-result" style="max-height:300px;margin-top:10px;">// 扫描结果将显示在这里...</pre>
</div>
<script>
// 对话框控制
function openDialog() {
document.getElementById('my-dialog').classList.add('show');
document.getElementById('dialog-overlay').style.display = 'block';
document.getElementById('my-dialog').querySelector('[role="document"]').focus();
}
function closeDialog() {
document.getElementById('my-dialog').classList.remove('show');
document.getElementById('dialog-overlay').style.display = 'none';
}
// 进度条模拟
function simulateProgress() {
const bar = document.querySelector('.bar');
const progress = document.getElementById('progress-bar');
let val = 0;
bar.style.width = '0%';
progress.setAttribute('aria-valuenow', 0);
const timer = setInterval(() => {
val += Math.random() * 15;
if (val >= 100) { val = 100; clearInterval(timer); }
bar.style.width = val + '%';
progress.setAttribute('aria-valuenow', Math.round(val));
document.getElementById('status-box').textContent = `✅ 加载进度:${Math.round(val)}%`;
}, 300);
document.getElementById('alert-box').textContent = '⚠️ 开始加载...';
setTimeout(() => {
document.getElementById('alert-box').textContent = '✅ 加载完成!';
}, 2500);
}
// 扫描 ARIA 属性
function scanAriaAttributes() {
const allElements = document.querySelectorAll('*');
const results = [];
allElements.forEach(el => {
const attrs = [];
// 检查 role
if (el.getAttribute('role')) attrs.push(`role="${el.getAttribute('role')}"`);
// 检查 aria-* 属性
Array.from(el.attributes).forEach(attr => {
if (attr.name.startsWith('aria-')) {
attrs.push(`${attr.name}="${attr.value}"`);
}
});
if (attrs.length > 0) {
const tag = el.tagName.toLowerCase();
const id = el.id ? `#${el.id}` : '';
const cls = el.className && typeof el.className === 'string' ? `.${el.className.split(' ')[0]}` : '';
results.push(`<${tag}${id}${cls} ${attrs.join(' ')}>`);
}
});
const output = document.getElementById('aria-scan-result');
output.innerHTML = `// 共发现 ${results.length} 个带 ARIA 属性的元素\n\n` +
results.map((r, i) => `[${i + 1}] ${r}`).join('\n') +
'\n\n// 这些属性对屏幕阅读器用户至关重要!';
}
// ESC 关闭对话框
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') closeDialog();
});
</script>
</body>
</html><!DOCTYPE html>
<!--
来源:基础知识/7-全局属性.md
演示:hidden / contenteditable / draggable / spellcheck / translate / dir / lang 等实用全局属性
-->
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>实用全局属性综合演示</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, sans-serif; padding: 30px; background: #f8f9fa; }
h1 { color: #2c3e50; margin-bottom: 25px; }
.section { background: white; padding: 25px; margin-bottom: 25px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.06); }
h2 { color: #34495e; margin-bottom: 15px; font-size: 17px; border-bottom: 2px solid #ecf0f1; padding-bottom: 8px; }
/* hidden 演示 */
[hidden] { display: none !important; }
.hidden-demo { padding: 15px; margin: 8px 0; border-radius: 6px; border: 2px solid transparent; transition: all 0.3s; }
.hidden-demo.visible { border-color: #27ae60; background: #eafaf1; }
.hidden-demo.hidden-state { border-color: #e74c3c; background: #fdedec; opacity: 0.5; }
/* contenteditable 演示 */
.editor-area {
min-height: 150px; padding: 15px; border: 2px solid #bdc3c7; border-radius: 8px;
background: #fff; line-height: 1.6; font-size: 14px;
}
.editor-area:focus { outline: none; border-color: #3498db; box-shadow: 0 0 0 3px rgba(52,152,219,0.2); }
.toolbar { display: flex; gap: 8px; margin-bottom: 10px; flex-wrap: wrap; }
.toolbar button { padding: 6px 12px; border: 1px solid #ddd; background: white; border-radius: 4px; cursor: pointer; font-size: 13px; }
.toolbar button:hover { background: #f0f0f0; }
/* draggable 演示 */
.drag-item {
padding: 15px 20px; margin: 8px; background: linear-gradient(135deg, #667eea, #764ba2);
color: white; border-radius: 8px; cursor: grab; user-select: none;
display: inline-block; transition: transform 0.2s, box-shadow 0.2s;
}
.drag-item:active { cursor: grabbing; transform: scale(1.05); box-shadow: 0 8px 20px rgba(0,0,0,0.3); }
.drop-zone {
min-height: 120px; padding: 20px; border: 3px dashed #bdc3c7; border-radius: 10px;
display: flex; flex-wrap: wrap; gap: 10px; align-content: flex-start; transition: all 0.3s;
}
.drop-zone.drag-over { border-color: #3498db; background: #ebf5fb; }
/* spellcheck 演示 */
.spellcheck-on { padding: 15px; border: 2px solid #27ae60; border-radius: 8px; min-height: 80px; }
.spellcheck-off { padding: 15px; border: 2px solid #e74c3c; border-radius: 8px; min-height: 80px; }
/* translate / dir / lang */
.rtl-demo { direction: rtl; text-align: right; background: #fff3cd; padding: 15px; border-radius: 8px; margin: 10px 0; }
.ltr-demo { direction: ltr; text-align: left; background: #d1ecf1; padding: 15px; border-radius: 8px; margin: 10px 0; }
code { background: #f4f4f4; padding: 2px 6px; border-radius: 3px; font-size: 13px; color: #e74c3c; }
pre { background: #282c34; color: #abb2bf; padding: 15px; border-radius: 6px; overflow-x: auto; font-size: 12px; line-height: 1.5; }
button { padding: 8px 16px; margin: 5px; border: none; border-radius: 6px; cursor: pointer; background: #3498db; color: white; font-size: 13px; }
button:hover { background: #2980b9; }
table { width: 100%; border-collapse: collapse; font-size: 13px; margin: 10px 0; }
th, td { padding: 8px 12px; text-align: left; border: 1px solid #dee2e6; }
th { background: #f8f9fa; }
</style>
</head>
<body>
<h1>🔧 实用全局属性综合演示</h1>
<!-- ========== 1. hidden 属性 ========== -->
<div class="section">
<h2>1. hidden 属性 — 条件显示/隐藏</h2>
<p><code>hidden</code> 是布尔属性,存在即隐藏。比 <code>display:none</code> 更语义化。</p>
<div id="hidden-el-1" class="hidden-demo visible">
<strong>元素 A</strong> — 当前可见(点击按钮切换)
</div>
<div id="hidden-el-2" class="hidden-demo visible">
<strong>元素 B</strong> — 当前可见(点击按钮切换)
</div>
<div id="hidden-el-3" class="hidden-demo hidden-state" hidden>
<strong>元素 C</strong> — 初始隐藏(HTML 中自带 hidden 属性)
</div>
<div style="margin-top:15px;">
<button onclick="toggleHidden('hidden-el-1')">切换 A</button>
<button onclick="toggleHidden('hidden-el-2')">切换 B</button>
<button onclick="toggleHidden('hidden-el-3')">切换 C</button>
<button onclick="showAll()">全部显示</button>
<button onclick="hideAll()">全部隐藏</button>
</div>
<pre id="hidden-log" class="log" style="background:#f8f9fa;padding:10px;border-radius:4px;margin-top:10px;font-family:monospace;font-size:12px;">// 操作日志</pre>
</div>
<!-- ========== 2. contenteditable 富文本编辑 ========== -->
<div class="section">
<h2>2. contenteditable — 富文本编辑器</h2>
<p>使任意元素变为可编辑区域,无需 textarea/input。</p>
<div class="toolbar">
<button onclick="execCmd('bold')"><b>B</b> 加粗</button>
<button onclick="execCmd('italic')"><i>I</i> 斜体</button>
<button onclick="execCmd('underline')"><u>U</u> 下划线</button>
<button onclick="execCmd('strikeThrough')"><s>S</s> 删除线</button>
<span style="width:1px;background:#ddd;"></span>
<button onclick="execCmd('insertUnorderedList')">• 列表</button>
<button onclick="execCmd('insertOrderedList')">1. 有序列表</button>
<span style="width:1px;background:#ddd;"></span>
<button onclick="execCmd('justifyLeft')">左对齐</button>
<button onclick="execCmd('justifyCenter')">居中</button>
<button onclick="execCmd('justifyRight')">右对齐</button>
<span style="width:1px;background:#ddd;"></span>
<button onclick="execCmd('undo') ↩ 撤销</button>
<button onclick="execCmd('redo') ↪ 重做</button>
<span style="width:1px;background:#ddd;"></span>
<button onclick="getHTML()" style="background:#27ae60;">获取 HTML</button>
<button onclick="getText()" style="background:#e67e22;">获取纯文本</button>
</div>
<div contenteditable="true" id="rich-editor" class="editor-area" spellcheck="true">
<p>这是一个 <b>可编辑</b> 的区域,试试:</p>
<ul>
<li>选中文字后点击工具栏按钮</li>
<li>直接输入和删除</li>
<li>粘贴富文本内容</li>
</ul>
</div>
<pre id="editor-output" style="margin-top:10px;max-height:150px;">// 输出将显示在此处...</pre>
</div>
<!-- ========== 3. draggable 拖拽 ========== -->
<div class="section">
<h2>3. draggable 属性 — 拖拽功能开关</h2>
<div style="margin-bottom:15px;">
<strong>拖拽源(draggable="true"):</strong>
<div draggable="true" id="drag-1" class="drag-item" data-value="苹果">🍎 苹果</div>
<div draggable="true" id="drag-2" class="drag-item" data-value="香蕉">🍌 香蕉</div>
<div draggable="true" id="drag-3" class="drag-item" data-value="葡萄">🍇 葡萄</div>
<div draggable="false" id="drag-4" class="drag-item" style="background:linear-gradient(135deg,#95a5a6,#7f8c8d);opacity:0.6;cursor:not-allowed;">🚫 不可拖拽 (draggable=false)</div>
</div>
<strong>放置目标:</strong>
<div class="drop-zone" id="drop-zone">
<span style="color:#999;width:100%;text-align:center;padding:20px;">将水果拖到这里</span>
</div>
<p id="drag-log" style="margin-top:10px;font-size:13px;color:#666;">拖拽事件日志将显示在这里...</p>
</div>
<!-- ========== 4. spellcheck 拼写检查 ========== -->
<div class="section">
<h2>4. spellcheck 属性 — 拼写检查控制</h2>
<div style="display:flex;gap:20px;flex-wrap:wrap;">
<div style="flex:1;min-width:280px;">
<p style="margin-bottom:8px;"><code>spellcheck="true"</code> ✅ 启用拼写检查(会有红色波浪线):</p>
<div contenteditable="true" spellcheck="true" class="spellcheck-on">
Thiss is a testt with speling errrors. 请尝试输入错误单词。
</div>
</div>
<div style="flex:1;min-width:280px;">
<p style="margin-bottom:8px;"><code>spellcheck="false"</code> ❌ 禁用拼写检查:</p>
<div contenteditable="true" spellcheck="false" class="spellcheck-off">
Thiss is a testt with speling errrors. 即使有错误也不会标红。
</div>
</div>
</div>
<p style="margin-top:10px;font-size:13px;color:#666;">💡 提示:在支持的浏览器中,启用拼写检查的文字下方会出现波浪虚线。</p>
</div>
<!-- ========== 5. translate / dir / lang 国际化 ========== -->
<div class="section">
<h2>5. translate / dir / lang — 国际化属性</h2>
<h3 style="margin:15px 0 10px;">translate 属性(控制翻译)</h3>
<div style="display:flex;gap:15px;flex-wrap:wrap;">
<div style="flex:1;padding:15px;background:#e8f5e9;border-radius:8px;">
<p><code>translate="yes"</code> (默认,允许翻译):</p>
<p translate="yes">Hello World — 这段文字可以被翻译工具翻译</p>
</div>
<div style="flex:1;padding:15px;background:#ffebee;border-radius:8px;">
<p><code>translate="no"</code> (禁止翻译):</p>
<p translate="no">HTML5 — 品牌名称、代码、专有名词应禁止自动翻译</p>
</div>
</div>
<h3 style="margin:15px 0 10px;">dir 文本方向</h3>
<div class="ltr-demo">
<strong>dir="ltr" (从左到右):</strong> Hello World! 你好世界!这是正常的从左到右排列。
</div>
<div class="rtl-demo">
<strong>dir="rtl" (从右到左):</strong> مرحبا بالعالم! 这是阿拉伯语/希伯来语等 RTL 语言的书写方向。
</div>
<h3 style="margin:15px 0 10px;">lang 语言属性</h3>
<table>
<tr><th>元素</th><th>lang 值</th><th>作用</th></tr>
<tr><td><code><html lang="zh-CN"></code></td><td>中文-中国大陆</td><td>告诉浏览器/搜索引擎页面语言</td></tr>
<tr><td><code><p lang="en">Hello</p></code></td><td>英语段落</td><td>混合语言场景下的语言标注</td></tr>
<tr><td><code><abbr lang="fr" title="République Française">RF</abbr></code></td><td>法语缩写</td><td>正确发音和翻译</td></tr>
</table>
</div>
<script>
// ===== hidden 操作 =====
function toggleHidden(id) {
const el = document.getElementById(id);
const isHidden = el.hidden;
el.hidden = !isHidden;
el.classList.toggle('visible', isHidden);
el.classList.toggle('hidden-state', !isHidden);
logHidden(`"${id}" hidden → ${!isHidden}`);
}
function showAll() {
['hidden-el-1','hidden-el-2','hidden-el-3'].forEach(id => {
const el = document.getElementById(id);
el.hidden = false;
el.classList.add('visible');
el.classList.remove('hidden-state');
});
logHidden('全部设为可见');
}
function hideAll() {
['hidden-el-1','hidden-el-2','hidden-el-3'].forEach(id => {
const el = document.getElementById(id);
el.hidden = true;
el.classList.remove('visible');
el.classList.add('hidden-state');
});
logHidden('全部设为隐藏');
}
function logHidden(msg) {
const log = document.getElementById('hidden-log');
const time = new Date().toLocaleTimeString();
log.textContent = `[${time}] ${msg}\nel.hidden = ${document.getElementById('hidden-el-1').hidden}, ${
document.getElementById('hidden-el-2').hidden}, ${document.getElementById('hidden-el-3').hidden}`;
}
// ===== contenteditable 操作 =====
function execCmd(cmd) {
document.execCommand(cmd, false, null);
document.getElementById('rich-editor').focus();
}
function getHTML() {
const html = document.getElementById('rich-editor').innerHTML;
document.getElementById('editor-output').textContent = `// innerHTML:\n${html}`;
}
function getText() {
const text = document.getElementById('rich-editor').innerText;
document.getElementById('editor-output').textContent = `// innerText (纯文本):\n${text}`;
}
// ===== draggable 操作 =====
const dragItems = document.querySelectorAll('.drag-item[draggable="true"]');
const dropZone = document.getElementById('drop-zone');
const dragLog = document.getElementById('drag-log');
dragItems.forEach(item => {
item.addEventListener('dragstart', (e) => {
e.dataTransfer.setData('text/plain', item.dataset.value);
e.dataTransfer.effectAllowed = 'move';
item.style.opacity = '0.5';
dragLog.textContent = `🖱️ 开始拖拽: ${item.dataset.value}`;
});
item.addEventListener('dragend', () => {
item.style.opacity = '1';
dropZone.classList.remove('drag-over');
});
});
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
e.dataTransfer.dropEffect = 'move';
dropZone.classList.add('drag-over');
dragLog.textContent = '📍 拖拽经过放置区域...';
});
dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('drag-over');
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
dropZone.classList.remove('drag-over');
const value = e.dataTransfer.getData('text/plain');
if (dropZone.querySelector('span')) {
dropZone.innerHTML = '';
}
const badge = document.createElement('div');
badge.className = 'drag-item';
badge.style.cssText = 'background:linear-gradient(135deg,#27ae60,#2ecc71);padding:8px 16px;font-size:13px;';
badge.textContent = value + ' ✓';
dropZone.appendChild(badge);
dragLog.textContent = `✅ 放置成功: ${value}`;
});
</script>
</body>
</html><!DOCTYPE html>
<!--
来源:基础知识/7-全局属性.md
演示:移动端优化属性(inputmode / enterkeyhint / autocapitalize / autofocus)
以及 popover 弹出层属性
-->
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>移动端优化 & popover 属性演示</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, sans-serif; padding: 30px; background: #f5f7fa; max-width: 900px; margin: 0 auto; }
h1 { color: #1a1a1a; margin-bottom: 25px; }
.section { background: white; padding: 25px; margin-bottom: 25px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.06); }
h2 { color: #333; margin-bottom: 15px; font-size: 17px; border-bottom: 2px solid #eee; padding-bottom: 8px; }
.form-group { margin: 15px 0; }
.form-group label { display: block; font-weight: 600; margin-bottom: 6px; font-size: 14px; color: #555; }
.form-group input, .form-group textarea {
width: 100%; padding: 12px 15px; border: 2px solid #e0e0e0; border-radius: 8px;
font-size: 15px; transition: border-color 0.2s;
}
.form-group input:focus, .form-group textarea:focus {
outline: none; border-color: #3498db; box-shadow: 0 0 0 3px rgba(52,152,219,0.15);
}
.hint { font-size: 12px; color: #888; margin-top: 5px; }
.attr-tag { display: inline-block; background: #e8f4fd; color: #1976d2; padding: 2px 8px; border-radius: 4px; font-size: 12px; font-family: monospace; }
/* popover 演示 */
[popover] {
padding: 20px; border: none; border-radius: 12px; box-shadow: 0 10px 40px rgba(0,0,0,0.2);
background: white; max-width: 350px;
}
.pop-btn {
padding: 10px 20px; border: 2px solid #3498db; background: white; color: #3498db;
border-radius: 8px; cursor: pointer; font-size: 14px; font-weight: 600; transition: all 0.2s;
}
.pop-btn:hover { background: #3498db; color: white; }
table { width: 100%; border-collapse: collapse; font-size: 13px; margin: 15px 0; }
th, td { padding: 10px 12px; text-align: left; border: 1px solid #dee2e6; }
th { background: #f8f9fa; font-weight: 600; }
code { background: #f4f4f4; padding: 2px 6px; border-radius: 3px; font-size: 13px; color: #e74c3c; }
pre { background: #263238; color: #eceff1; padding: 15px; border-radius: 6px; overflow-x: auto; font-size: 12px; line-height: 1.6; }
.keyboard-preview {
display: inline-flex; gap: 3px; padding: 8px 12px; background: #222; border-radius: 8px;
vertical-align: middle; margin-left: 10px;
}
.key { width: 28px; height: 36px; background: #444; border-radius: 4px; display: flex; align-items: center; justify-content: center; color: #aaa; font-size: 11px; }
.key.enter { width: 50px; background: #3498db; color: white; font-weight: bold; }
.key.space { width: 80px; }
</style>
</head>
<body>
<h1>📱 移动端优化 & popover 弹出层属性</h1>
<!-- ========== 1. inputmode 虚拟键盘 ========== -->
<div class="section">
<h2>1. inputmode — 控制虚拟键盘类型</h2>
<p>在移动设备上,不同 <code>inputmode</code> 会调出不同的虚拟键盘布局:</p>
<div class="form-group">
<label>inputmode="text" <span class="attr-tag">默认</span></label>
<input type="text" inputmode="text" placeholder="标准文本键盘">
<div class="hint">标准全键盘,适合一般文本输入</div>
<div class="keyboard-preview" title="键盘预览">
<div class="key">Q</div><div class="key">W</div><div class="key">E</div><div class="key">R</div>
<div class="key">T</div><div class="key">Y</div><div class="key enter">搜索</div>
</div>
</div>
<div class="form-group">
<label>inputmode="tel" <span class="attr-tag">电话号码</span></label>
<input type="text" inputmode="tel" placeholder="请输入手机号">
<div class="hint">纯数字键盘 + *# 符号,适合电话号码</div>
<div class="keyboard-preview">
<div class="key">1</div><div class="key">2</div><div class="key">3</div>
<div class="key">4</div><div class="key">5</div><div class="key">6</div><div class="key">7</div><div class="key">8</div><div class="key">9</div><div class="key">0</div>
</div>
</div>
<div class="form-group">
<label>inputmode="email" <span class="attr-tag">邮箱</span></label>
<input type="text" inputmode="email" placeholder="example@mail.com">
<div class="hint">包含 @ 和 . 的邮箱专用键盘</div>
</div>
<div class="form-group">
<label>inputmode="url" <span class="attr-tag">网址</span></label>
<input type="text" inputmode="url" placeholder="https://...">
<div class="hint">包含 / : . 等URL字符的键盘</div>
</div>
<div class="form-group">
<label>inputmode="numeric" <span class="attr-tag">纯数字</span></label>
<input type="text" inputmode="numeric" placeholder="0-9 数字">
<div class="hint">纯数字键盘(不含符号),适合验证码、数量等</div>
</div>
<div class="form-group">
<label>inputmode="decimal" <span class="attr-tag">小数</span></label>
<input type="text" inputmode="decimal" placeholder="金额、坐标等">
<div class="hint">数字 + 小数点,适合金额、测量值</div>
</div>
<div class="form-group">
<label>inputmode="search" <span class="attr-tag">搜索</span></label>
<input type="text" inputmode="search" placeholder="搜索关键词...">
<div class="hint">回车键变成"搜索"按钮</div>
</div>
<table>
<tr><th>inputmode 值</th><th>键盘特点</th><th>适用场景</th></tr>
<tr><td><code>text</code></td><td>标准全键盘</td><td>通用文本输入</td></tr>
<tr><td><code>tel</code></td><td>数字 + *#</td><td>电话号码</td></tr>
<tr><td><code>email</code></td><td>含 @ .</td><td>邮箱地址</td></tr>
<tr><td><code>url</code></td><td>含 / : .</td><td>网址输入</td></tr>
<tr><td><code>numeric</code></td><td>纯数字</td><td>验证码、PIN</td></tr>
<tr><td><code>decimal</code></td><td>数字 + 小数点</td><td>金额、坐标</td></tr>
<tr><td><code>search</code></td><td>回车→搜索</td><td>搜索框</td></tr>
<tr><td><code>none</code></td><td>不弹出键盘</td><td>只读日期选择器等</td></tr>
</table>
</div>
<!-- ========== 2. enterkeyhint 回车键提示 ========== -->
<div class="section">
<h2>2. enterkeyhint — 回车键文案提示</h2>
<p>控制移动端虚拟键盘回车键上显示的文字:</p>
<div style="display:flex;gap:15px;flex-wrap:wrap;">
<div class="form-group" style="flex:1;min-width:200px;">
<label>enterkeyhint="enter" <span class="attr-tag">换行</span></label>
<input type="text" enterkeyhint="enter" placeholder="多行输入场景">
</div>
<div class="form-group" style="flex:1;min-width:200px;">
<label>enterkeyhint="done" <span class="attr-tag">完成</span></label>
<input type="text" enterkeyhint="done" placeholder="表单最后一项">
</div>
<div class="form-group" style="flex:1;min-width:200px;">
<label>enterkeyhint="go" <span class="attr-tag">前往</span></label>
<input type="text" enterkeyhint="go" placeholder="URL 输入框">
</div>
<div class="form-group" style="flex:1;min-width:200px;">
<label>enterkeyhint="next" <span class="attr-tag">下一项</span></label>
<input type="text" enterkeyhint="next" placeholder="表单中间项">
</div>
<div class="form-group" style="flex:1;min-width:200px;">
<label>enterkeyhint="search" <span class="attr-tag">搜索</span></label>
<input type="text" enterkeyhint="search" placeholder="搜索框">
</div>
<div class="form-group" style="flex:1;min-width:200px;">
<label>enterkeyhint="send" <span class="attr-tag">发送</span></label>
<input type="text" enterkeyhint="send" placeholder="聊天输入框">
</div>
</div>
<pre style="font-size:11px;">// enterkeyhint 可选值:
// "enter" → 换行符(多行文本)
// "done" → 完成(表单末尾)
// "go" → 前往(URL 导航)
// "next" → 下一项(表单中间)
// "search" → 搜索
// "send" → 发送(消息)
// "previous" → 上一项(罕见)</pre>
</div>
<!-- ========== 3. autocapitalize 自动大小写 ========== -->
<div class="section">
<h2>3. autocapitalize — 自动大小写控制</h2>
<div style="display:flex;gap:15px;flex-wrap:wrap;">
<div class="form-group" style="flex:1;min-width:220px;">
<label>autocapitalize="sentences" <span class="attr-tag">句子</span></label>
<input type="text" autocapitalize="sentences" placeholder="每句首字母大写">
<div class="hint">"hello world" → "Hello world"</div>
</div>
<div class="form-group" style="flex:1;min-width:220px;">
<label>autocapitalize="words" <span class="attr-tag">单词</span></label>
<input type="text" autocapitalize="words" placeholder="每个单词首字母大写">
<div class="hint">"john doe" → "John Doe"</div>
</div>
<div class="form-group" style="flex:1;min-width:220px;">
<label>autocapitalize="characters" <span class="attr-tag">全部大写</span></label>
<input type="text" autocapitalize="characters" placeholder="全部转为大写">
<div class="hint">"hello" → "HELLO"</div>
</div>
<div class="form-group" style="flex:1;min-width:220px;">
<label>autocapitalize="none" <span class="attr-tag">不自动</span></label>
<input type="text" autocapitalize="none" placeholder="保持原始输入">
<div class="hint">不做任何自动大小写转换</div>
</div>
</div>
</div>
<!-- ========== 4. autofocus 自动聚焦 ========== -->
<div class="section">
<h2>4. autofocus — 页面加载自动聚焦</h2>
<p>页面加载时自动获得焦点的输入框(本页面的第一个输入框可能已被抢占焦点 😄):</p>
<div class="form-group">
<label>autofocus 演示 <span class="attr-tag">已激活</span></label>
<input type="text" autofocus placeholder="这个输入框会自动获得焦点">
<div class="hint">⚠️ 一个页面只能有一个 autofocus 元素生效(第一个声明的)</div>
</div>
</div>
<!-- ========== 5. popover 原生弹出层 ========== -->
<div class="section">
<h2>5. popover — 原生弹出层属性</h2>
<p>HTML 新增的原生弹出层 API,无需 JS 即可实现轻量弹窗:</p>
<div style="display:flex;gap:15px;flex-wrap:wrap;margin:20px 0;">
<!-- 默认 popover -->
<div>
<button popovertarget="pop-default" class="pop-btn">▶ 默认弹出层</button>
<div id="pop-default" popover>
<h3 style="margin-bottom:10px;">默认弹出层</h3>
<p style="font-size:13px;color:#666;line-height:1.6;">点击外部区域或按 Esc 可关闭。<br>这是最基础的 popover 用法。</p>
</div>
</div>
<!-- 手动 popover -->
<div>
<button popovertarget="pop-manual" class="pop-btn" style="border-color:#e74c3c;color:#e74c3c;">▶ 手动模式</button>
<div id="pop-manual" popover="manual" style="border:2px solid #e74c3c;">
<h3 style="margin-bottom:10px;color:#e74c3c;">手动模式 (manual)</h3>
<p style="font-size:13px;color:#666;line-height:1.6;">手动模式不会在点击外部时自动关闭!<br><br>
<button onclick="document.getElementById('pop-manual').hidePopover()" style="padding:6px 14px;background:#e74c3c;color:white;border:none;border-radius:4px;cursor:pointer;">手动关闭</button></p>
</div>
</div>
<!-- 提示框式 popover -->
<div>
<button popovertarget="pop-hint" class="pop-btn" style="border-color:#f39c12;color:#f39c12;">💡 提示框</button>
<div id="pop-hint" popover="auto" style="max-width:260px;background:#fff8e1;border:1px solid #ffecb3;">
<strong style="color:#f57f17;">💡 小提示</strong>
<p style="font-size:12px;color:#666;margin-top:8px;">popover 属性可以用来做 tooltip、下拉菜单、确认框等各种弹出层组件!</p>
</div>
</div>
<!-- 表单 popover -->
<div>
<button popovertarget="pop-form" class="pop-btn" style="border-color:#27ae60;color:#27ae60;">📝 表单弹出</button>
<div id="pop-form" popover>
<h3 style="margin-bottom:12px;">快速登录</h3>
<input type="text" placeholder="用户名" style="width:100%;padding:8px;border:1px solid #ddd;border-radius:4px;margin-bottom:8px;font-size:13px;">
<input type="password" placeholder="密码" style="width:100%;padding:8px;border:1px solid #ddd;border-radius:4px;margin-bottom:10px;font-size:13px;">
<button style="width:100%;padding:8px;background:#27ae60;color:white;border:none;border-radius:4px;cursor:pointer;font-size:13px;">登录</button>
</div>
</div>
</div>
<table>
<tr><th>popover 值</th><th>关闭方式</th><th>适用场景</th></tr>
<tr><td><code>auto</code>(默认)</td><td>点击外部、Esc、调用 hidePopover()</td><td>Tooltip、菜单、提示框</td></tr>
<tr><td><code>manual</code></td><td>只能通过 JS 关闭</td><td>模态框、持续显示的通知</td></tr>
</table>
<h3 style="margin:20px 0 10px;">JavaScript API</h3>
<pre>// Popover API
const popover = document.getElementById('pop-default');
popover.showPopover(); // 打开
popover.hidePopover(); // 关闭
popover.togglePopover(); // 切换
// 事件监听
popover.addEventListener('toggle', (e) => {
console.log('状态:', e.newState); // "open" | "closed"
});
// 检测浏览器支持
if (typeof HTMLElement.prototype.showPopover === 'function') {
console.log('✅ 支持 popover API');
}</pre>
</div>
</body>
</html>