display: contents 详解
<h1 data-id="heading-0">🧑💻 写在开头</h1><p>点赞 + 收藏 === 学会🤣🤣🤣</p>
<blockquote>
<p><code>display: contents</code> 是一个相对较新的 CSS 属性值,它会让元素自身不生成任何盒子,但它的子元素和伪元素仍然正常生成。简单说:元素本身从渲染树中消失,但它的孩子还在。</p>
</blockquote>
<h2 data-id="heading-0">基本概念</h2>
<h3 data-id="heading-1">工作原理</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;"><div class="parent">
<div class="child">内容</div>
</div></pre>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">/* 正常情况下 */
.parent {
display: block;/* parent 生成一个块级盒子 */
}
/* 使用 contents 后 */
.parent {
display: contents;/* parent 不生成盒子,child 直接"上升"到 parent 的位置 */
}</pre>
</div>
<h3 data-id="heading-2">直观对比</h3>
<p>应用前:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;"><main>
<div class="grid-container"><!-- 这个元素只是个包装 -->
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</div>
</main></pre>
</div>
<p>应用后:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;"><main>
<!-- grid-container 元素消失了,但它的子元素还在 -->
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</main></pre>
</div>
<h2 data-id="heading-3">主要用途</h2>
<h3 data-id="heading-4">1. 语义化与布局分离</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;"><!-- 想要使用 ul,但又需要 flex 布局 -->
<ul style="display: contents;">
<li>项目1</li>
<li>项目2</li>
<li>项目3</li>
</ul>
</pre>
</div>
<p> </p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">ul {
display: contents;/* ul 本身不生成盒子 */
}
/* li 直接参与父容器的布局 */
.parent-of-ul {
display: flex;/* li 会成为 flex 项目,而不是 ul */
}</pre>
</div>
<h3 data-id="heading-5">2. 网格布局中的包装器</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">.grid-container {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
/* 包装器不破坏网格布局 */
.wrapper {
display: contents;
/* 这个元素不生成盒子,它的子元素直接成为 grid 项目 */
}</pre>
</div>
<p>html</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;"><div class="grid-container">
<div>直接项目1</div>
<div class="wrapper"><!-- 这个元素不占位置 -->
<div>包装的项目2</div>
<div>包装的项目3</div>
</div>
<div>直接项目4</div>
</div></pre>
</div>
<h3 data-id="heading-6">3. Flexbox 中的包装器</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">.flex-container {
display: flex;
}
.group {
display: contents;/* 子元素直接成为 flex 项目 */
}
</pre>
</div>
<p> </p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;"><div class="flex-container">
<div>项目1</div>
<div class="group"><!-- 这个 div 不生成盒子 -->
<div>组内项目1</div><!-- 直接成为 flex 项目 -->
<div>组内项目2</div><!-- 直接成为 flex 项目 -->
</div>
<div>项目3</div>
</div></pre>
</div>
<h2 data-id="heading-7">实际应用场景</h2>
<h3 data-id="heading-8">场景1:表格布局优化</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;"><table>
<tr style="display: contents;"><!-- tr 不生成盒子 -->
<td>单元格1</td>
<td>单元格2</td>
<td>单元格3</td>
<!-- td 直接成为 table 的子元素 -->
</tr>
</table></pre>
</div>
<h3 data-id="heading-9">场景2:避免多余的 DOM 层级</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">/* 原本需要额外 div 来添加样式 */
.card {
display: flex;
}
.card-extra {
display: contents;/* 这个 div 只用于逻辑分组,不影响布局 */
}
/* 现在可以更灵活地组织代码 */</pre>
</div>
<h3 data-id="heading-10">场景3:响应式设计中的重组</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;"><div class="responsive-grid">
<!-- 移动端:堆叠显示 -->
<!-- 桌面端:网格显示 -->
<div class="group" style="display: contents;">
<div>项目A</div>
<div>项目B</div>
</div>
</div></pre>
</div>
<h2 data-id="heading-11">注意事项和限制</h2>
<h3 data-id="heading-12">1. 对可访问性的影响</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">/* ⚠️ 注意:元素本身消失,但语义还在吗? */
.button-group {
display: contents;
role: group;/* 虽然设置了 ARIA 角色,但元素不生成盒子,可能无效 */
}</pre>
</div>
<h3 data-id="heading-13">2. 对事件处理的影响</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">// ⚠️ 元素不生成盒子,点击事件可能无法在元素上触发
document.querySelector('.contents-element').addEventListener('click', () => {
// 这个元素在视觉上不存在,点击区域是子元素的
});</pre>
</div>
<h3 data-id="heading-14">3. 对背景和边框的影响</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">.contents-element {
display: contents;
background: red; /* ❌ 不会显示,因为元素没有盒子 */
border: 1px solid;/* ❌ 不会显示 */
padding: 10px; /* ❌ 不会显示 */
margin: 10px; /* ❌ 不会显示 */
width: 100px; /* ❌ 不会显示 */
height: 100px; /* ❌ 不会显示 */
}</pre>
</div>
<h3 data-id="heading-15">4. 对伪元素的影响</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">.contents-element {
display: contents;
}
.contents-element::before {
content: "✨";/* ✅ 伪元素仍然会显示,成为第一个子元素 */
}</pre>
</div>
<h3 data-id="heading-16">5. 浏览器兼容性</h3>
<ul>
<li>Chrome/Edge: 完全支持</li>
<li>Firefox: 完全支持</li>
<li>Safari: 支持但有部分问题</li>
<li>IE: 不支持</li>
</ul>
<h2 data-id="heading-17">调试技巧</h2>
<h3 data-id="heading-18">如何检查 display: contents 的效果</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">/* 在开发者工具中检查元素布局 */
.contents-element {
display: contents;
outline: 2px solid red;/* 不会显示,帮助理解元素确实消失了 */
}</pre>
</div>
<h3 data-id="heading-19">临时禁用调试</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">.contents-element {
display: contents;
/* 临时查看元素范围 */
display: block;/* 临时改为 block 查看原始位置 */
}</pre>
</div>
<h2 data-id="heading-20">实际案例</h2>
<h3 data-id="heading-21">案例:卡片布局</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;"><div class="card-grid">
<!-- 想要分组但不破坏网格 -->
<div class="card-group" style="display: contents;">
<div class="card">卡片1</div>
<div class="card">卡片2</div>
</div>
<div class="card-group" style="display: contents;">
<div class="card">卡片3</div>
<div class="card">卡片4</div>
</div>
</div></pre>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">.card-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
}
/* card-group 不破坏网格,所有卡片直接成为 grid 项目 */</pre>
</div>
<div>
<div>
<h2 data-id="heading-22">总结</h2>
<p><strong>优点:</strong></p>
<ul>
<li>✅ 保持 HTML 语义化</li>
<li>✅ 避免多余的包装元素</li>
<li>✅ 更灵活的布局控制</li>
<li>✅ 减少不必要的 DOM 层级</li>
</ul>
<p><strong>缺点:</strong></p>
<ul>
<li>❌ 元素样式(背景、边框等)失效</li>
<li>❌ 事件绑定可能受影响</li>
<li>❌ 可访问性需要考虑</li>
<li>❌ 调试相对困难</li>
</ul>
<p><strong>最佳实践:</strong></p>
<ul>
<li>主要用于布局分组</li>
<li>配合 Grid/Flex 使用效果最好</li>
<li>注意不要依赖元素的样式属性</li>
<li>测试可访问性表现</li>
</ul>
</div>
<div>
<h3 id="tid-D8HBxE">如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。</h3>
</div>
<p><em><img src="https://img2024.cnblogs.com/blog/2149129/202501/2149129-20250122165814748-630765389.png" alt="" loading="lazy"></em></p>
</div><br><br>
来源:https://www.cnblogs.com/smileZAZ/p/19652567
頁:
[1]