hidden 隐藏
hidden 是一个布尔型全局属性,用于指示元素尚未相关或不再相关,浏览器不会渲染该元素。与 display: none 不同的是,hidden 具有语义含义,它告诉浏览器和辅助技术:这个元素存在但不应该被显示或被用户感知。
前置知识
阅读本节前,建议先了解:role 与 aria-* 无障碍
基础概念
什么是 hidden 属性
hidden 属性是 HTML5 引入的全局布尔属性,当它存在于元素上时,浏览器会像 display: none 一样不渲染该元素,但同时它还携带语义信息:元素不相关或暂时不需要显示。
html
<!-- 基本用法 -->
<div hidden>
这段内容不会显示
</div>
<!-- 使用 until-found 值(Chrome 120+) -->
<div hidden="until-found">
这段内容在通过搜索找到之前不会显示
</div>hidden 的三种隐藏方式对比
| 隐藏方式 | CSS 渲染 | 语义含义 | 辅助技术 | 搜索引擎 | 适用场景 |
|---|---|---|---|---|---|
hidden | 不渲染 | 元素不相关 | 会被跳过 | 不索引 | 条件性隐藏内容 |
display: none | 不渲染 | 仅样式控制 | 会被跳过 | 不索引 | 纯视觉隐藏 |
aria-hidden="true" | 正常渲染 | 辅助技术隐藏 | 会被跳过 | 可能索引 | 装饰性元素 |
visibility: hidden | 占空间不渲染 | 仅样式控制 | 可能读出 | 可能索引 | 保留布局的隐藏 |
语法
布尔属性用法
html
<!-- 有值和无值等效 -->
<div hidden>隐藏内容</div>
<div hidden="">隐藏内容</div>
<div hidden="hidden">隐藏内容</div>
<!-- hidden="until-found" 适用于搜索时显示 -->
<div hidden="until-found">
<h2>常见问题解答</h2>
<p>这部分内容在用户搜索匹配之前保持隐藏。</p>
</div>通过 CSS 控制
css
/* 所有 hidden 元素默认不显示 */
[hidden] {
display: none;
}
/* hidden="until-found" 元素显示时高亮 */
[hidden="until-found"]:not(:popover-open) {
display: none;
}
[hidden="until-found"]:target {
display: block;
animation: highlight 1s ease;
}
@keyframes highlight {
0% { background-color: #fef08a; }
100% { background-color: transparent; }
}详细说明
hidden vs display: none
虽然效果类似,但两者有本质区别:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>hidden vs display:none</title>
<style>
/* 错误:不要覆盖 hidden 的默认行为 */
/* [hidden] { display: block; } */ /* 不要这样做! */
/* 如果需要让 hidden 元素显示,应该移除 hidden 属性 */
.visible {
/* hidden 元素需要显示时,移除 hidden 属性而非修改样式 */
}
/* display: none 是纯样式,可以随时通过 CSS 切换 */
.css-hidden {
display: none;
}
.css-visible {
display: block;
}
</style>
</head>
<body>
<!-- 语义隐藏:内容暂时不相关 -->
<div id="success-msg" hidden>
<p>操作成功!您的数据已保存。</p>
</div>
<!-- 样式隐藏:仅用于视觉切换 -->
<div id="toggle-panel" class="css-hidden">
<p>这是可以切换显示/隐藏的面板。</p>
</div>
<script>
// 显示 hidden 元素:移除 hidden 属性
function showSuccess() {
document.getElementById('success-msg').hidden = false;
}
// 隐藏 hidden 元素:添加 hidden 属性
function hideSuccess() {
document.getElementById('success-msg').hidden = true;
}
// CSS 隐藏:切换类名
function togglePanel() {
const panel = document.getElementById('toggle-panel');
panel.classList.toggle('css-hidden');
panel.classList.toggle('css-visible');
}
</script>
</body>
</html>hidden vs aria-hidden
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>hidden vs aria-hidden</title>
<style>
.decorative {
position: absolute;
width: 100px;
height: 100px;
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 50%;
}
</style>
</head>
<body>
<!-- hidden:元素完全不渲染,所有用户都无法感知 -->
<div hidden>
<p>这段文字所有人都看不到,包括屏幕阅读器用户。</p>
</div>
<!-- aria-hidden="true":元素视觉可见但辅助技术会跳过 -->
<div aria-hidden="true">
<span class="decorative"></span>
<!-- 装饰性图标,屏幕阅读器不需要读出 -->
<svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
<path d="M12 2L2 7l10 5 10-5-10-5z"/>
</svg>
<!-- 视觉可见的装饰,但屏幕阅读器不读 -->
</div>
<!-- 不要对可见的交互元素使用 aria-hidden -->
<!-- 错误示例 -->
<!-- <button aria-hidden="true">点击我</button> -->
</body>
</html>何时使用 hidden
推荐使用 hidden 的场景:
html
<!-- 1. 条件性内容 - 根据用户状态显示不同内容 -->
<div id="login-prompt">
<p>请先登录查看内容。</p>
</div>
<div id="user-content" hidden>
<h2>欢迎回来,用户!</h2>
<p>这是登录后才能看到的内容。</p>
</div>
<!-- 2. 表单步骤 - 多步骤表单中隐藏未激活的步骤 -->
<form id="multi-step-form">
<fieldset id="step-1">
<legend>第一步:基本信息</legend>
<!-- 表单字段 -->
</fieldset>
<fieldset id="step-2" hidden>
<legend>第二步:详细资料</legend>
<!-- 表单字段 -->
</fieldset>
<fieldset id="step-3" hidden>
<legend>第三步:确认提交</legend>
<!-- 表单字段 -->
</fieldset>
</form>
<!-- 3. 消息提示 - 操作后的反馈信息 -->
<div id="toast-success" hidden role="alert">
操作成功完成!
</div>
<div id="toast-error" hidden role="alert">
操作失败,请重试。
</div>
<!-- 4. 搜索结果面板 -->
<div id="search-results" hidden>
<h2>搜索结果</h2>
<ul id="results-list"></ul>
</div>
<!-- 5. 对话框 - 使用 hidden 而非 display:none -->
<dialog id="my-dialog" hidden>
<h3>确认操作</h3>
<p>确定要执行此操作吗?</p>
<button onclick="this.closest('dialog').close()">取消</button>
<button onclick="this.closest('dialog').close()">确定</button>
</dialog>不适合使用 hidden 的场景:
html
<!-- 1. CSS 动画过渡 - 需要 hidden 到 visible 的动画效果 -->
<!-- 使用 opacity + visibility + pointer-events 代替 -->
<style>
.fade-enter {
opacity: 0;
visibility: hidden;
transition: opacity 0.3s, visibility 0.3s;
}
.fade-enter.active {
opacity: 1;
visibility: visible;
}
</style>
<!-- 2. 折叠面板 - 需要高度过渡动画 -->
<!-- 使用 max-height + overflow 代替 -->
<style>
.collapsible-content {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
}
.collapsible-content.open {
max-height: 500px;
}
</style>
<!-- 3. 标签页面板 - 需要键盘焦点管理 -->
<!-- 使用 aria-selected + display 控制代替 -->hidden="until-found"
hidden="until-found" 是一个较新的值(Chrome 120+),允许内容在用户通过浏览器搜索(Ctrl+F / Cmd+F)找到匹配文本时自动显示:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>hidden=until-found 示例</title>
<style>
/* 页面导航 - 折叠的章节 */
.chapter-content {
padding: 20px;
border: 1px solid #e2e8f0;
border-radius: 8px;
margin-bottom: 10px;
}
/* until-found 隐藏的内容被找到时会高亮 */
:target {
animation: flash-highlight 2s ease;
}
@keyframes flash-highlight {
0%, 100% { background-color: transparent; }
50% { background-color: #fef08a; }
}
</style>
</head>
<body>
<h1>产品文档</h1>
<!-- 目录 -->
<nav>
<a href="#ch1">第一章 安装</a>
<a href="#ch2">第二章 配置</a>
<a href="#ch3">第三章 部署</a>
</nav>
<!-- 使用 hidden="until-found" 折叠章节 -->
<section id="ch1" class="chapter" hidden="until-found">
<h2>第一章:安装指南</h2>
<p>请按照以下步骤安装应用程序...</p>
<p>包含环境变量配置、依赖安装等关键信息。</p>
</section>
<section id="ch2" class="chapter" hidden="until-found">
<h2>第二章:配置详解</h2>
<p>配置文件位于 /etc/app/config.yaml...</p>
<p>包含数据库连接、缓存配置等详细说明。</p>
</section>
<section id="ch3" class="chapter" hidden="until-found">
<h2>第三章:部署流程</h2>
<p>使用 Docker 部署的步骤如下...</p>
<p>包含负载均衡、健康检查等生产环境配置。</p>
</section>
<script>
// 监听 hidden 元素变为可见
document.querySelectorAll('[hidden="until-found"]').forEach(section => {
const observer = new MutationObserver(() => {
if (!section.hidden) {
console.log(`章节 ${section.id} 因搜索匹配而被展开`);
}
});
observer.observe(section, { attributes: true, attributeFilter: ['hidden'] });
});
</script>
</body>
</html>实战示例
多步骤表单
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>多步骤表单</title>
<style>
.form-step {
padding: 20px;
border: 1px solid #e2e8f0;
border-radius: 8px;
}
[hidden] {
display: none;
}
.progress-bar {
display: flex;
gap: 8px;
margin-bottom: 20px;
}
.progress-step {
flex: 1;
height: 4px;
background: #e2e8f0;
border-radius: 2px;
}
.progress-step.active {
background: #3b82f6;
}
.progress-step.done {
background: #22c55e;
}
</style>
</head>
<body>
<h1>用户注册</h1>
<!-- 进度指示 -->
<div class="progress-bar">
<div class="progress-step active" data-step="1"></div>
<div class="progress-step" data-step="2"></div>
<div class="progress-step" data-step="3"></div>
</div>
<form id="register-form">
<!-- 步骤 1:基本信息 -->
<fieldset id="step-1" class="form-step">
<legend>基本信息</legend>
<label>
用户名:<input type="text" name="username" required>
</label>
<label>
邮箱:<input type="email" name="email" required>
</label>
<button type="button" onclick="nextStep(2)">下一步</button>
</fieldset>
<!-- 步骤 2:详细资料 -->
<fieldset id="step-2" class="form-step" hidden>
<legend>详细资料</legend>
<label>
手机号:<input type="tel" name="phone">
</label>
<label>
职业:<input type="text" name="occupation">
</label>
<button type="button" onclick="prevStep(1)">上一步</button>
<button type="button" onclick="nextStep(3)">下一步</button>
</fieldset>
<!-- 步骤 3:确认 -->
<fieldset id="step-3" class="form-step" hidden>
<legend>确认信息</legend>
<div id="summary"></div>
<button type="button" onclick="prevStep(2)">上一步</button>
<button type="submit">提交注册</button>
</fieldset>
</form>
<script>
let currentStep = 1;
function showStep(step) {
// 隐藏所有步骤
document.querySelectorAll('.form-step').forEach(el => {
el.hidden = true;
});
// 显示目标步骤
document.getElementById(`step-${step}`).hidden = false;
// 更新进度条
document.querySelectorAll('.progress-step').forEach(el => {
const s = Number(el.dataset.step);
el.classList.toggle('active', s === step);
el.classList.toggle('done', s < step);
});
currentStep = step;
}
function nextStep(step) {
showStep(step);
if (step === 3) {
// 生成摘要
const form = document.getElementById('register-form');
const summary = document.getElementById('summary');
summary.innerHTML = `
<p>用户名:${form.username.value}</p>
<p>邮箱:${form.email.value}</p>
<p>手机号:${form.phone.value || '未填写'}</p>
<p>职业:${form.occupation.value || '未填写'}</p>
`;
}
}
function prevStep(step) {
showStep(step);
}
</script>
</body>
</html>注意事项
不要用 CSS 覆盖 hidden
css
/* 危险:不要这样做!这会破坏 hidden 的语义 */
[hidden] {
display: block !important;
}
/* 正确做法:需要显示时,移除 hidden 属性 */
element.hidden = false;
// 或
element.removeAttribute('hidden');表单中的 hidden 输入
html
<!-- 注意:<input type="hidden"> 和 hidden 属性不同 -->
<!-- type="hidden" 是表单隐藏字段,会随表单提交 -->
<input type="hidden" name="token" value="abc123">
<!-- hidden 属性是隐藏整个元素 -->
<div hidden>
<input name="field" value="value">
<!-- 被隐藏的表单字段不会随表单提交 -->
</div>hidden 不等于 inert
html
<!-- hidden:完全不可见、不可交互 -->
<div hidden>
<button>这个按钮不可见也不可点击</button>
</div>
<!-- inert:可见但不可交互 -->
<div inert>
<button>这个按钮可见但不可点击</button>
<p>这段文字可见但不可选择</p>
</div>最佳实践
1. 优先使用 hidden 而非 display: none
当元素需要条件性显示/隐藏时,优先使用 hidden 属性,因为它携带语义:
javascript
// 推荐:使用 hidden 属性
element.hidden = true; // 隐藏
element.hidden = false; // 显示
// 不推荐:操作 style
element.style.display = 'none'; // 隐藏
element.style.display = ''; // 显示2. 结合 ARIA 属性
html
<!-- 使用 hidden 隐藏时,配合 aria-live 实现动态通知 -->
<div id="notification" hidden aria-live="polite"></div>
<script>
function showNotification(message) {
const el = document.getElementById('notification');
el.textContent = message;
el.hidden = false;
// 3 秒后自动隐藏
setTimeout(() => {
el.hidden = true;
}, 3000);
}
</script>3. 过渡动画的处理
当需要显示/隐藏过渡动画时,使用 requestAnimationFrame 在移除 hidden 后添加动画类:
javascript
function showWithAnimation(element) {
element.hidden = false;
// 触发重排后添加动画类
requestAnimationFrame(() => {
element.classList.add('fade-in');
});
}
function hideWithAnimation(element) {
element.classList.add('fade-out');
element.addEventListener('animationend', () => {
element.hidden = true;
element.classList.remove('fade-out', 'fade-in');
}, { once: true });
}下一节
继续学习:translate 翻译控制