如何用纯CSS解决含子UL的LI边距布局问题?无::text伪元素
纯CSS实现菜单布局需求
现有菜单布局中,Item 4和4.2包含子UL。希望仅为文本节点应用padding,但CSS暂不支持::text伪元素。尝试为子列表设置负margin,却无法让子列表横向填满LI元素。需要实现以下三点:
- 所有菜单项宽度匹配最大可见项;
- 所有水平内边距为
--menu-padding; - 包含子UL的LI无水平内边距,但文本与其边框有视觉上等于
--menu-padding的间距。
寻求纯CSS解决方案,不使用JavaScript且不修改HTML。目标效果示意图:
现有代码
CSS
:root { --menu-clr-item-bk:#84744dff; --menu-clr-item-tx:#ddddddff; --menu-clr-item-tx-hov:#84744dff; --menu-clr-item-bk-hov:#ddddddff; --menu-clr-item-tx-clildhov:#ccccccff; --menu-clr-item-bk-clildhov:#847b65ff; --menu-h-clr-tile-bk:#ddddddff; --menu-h-clr-item-bk:var(--menu-clr-item-bk); --menu-h-clr-item-bk-hov:var(--menu-clr-item-bk-hov); --menu-h-clr-item-tx:var(--menu-clr-item-tx); --menu-h-clr-item-tx-hov:var(--menu-clr-item-tx-hov); --menu-h-separator:0; --menu-h-shadow:none; --menu-v-clr-tile-bk:#ddddddff; --menu-v-clr-item-bk:var(--menu-clr-item-bk); --menu-v-clr-item-bk-hov:var(--menu-clr-item-bk-hov); --menu-v-clr-item-tx:var(--menu-clr-item-tx); --menu-v-clr-item-tx-hov:var(--menu-clr-item-tx-hov); --menu-v-separator:1px; --menu-v-shadow:0.1em 0.1em 0.1em #0008; --menu-padding:0.5em; --menu-checkmargin:0.5em; --menu-checksize:1em; } * { box-sizing: border-box; } body, html { height: 100%; } nav > h1 { display: none; } /* all menues and items */ nav ul { position: absolute; white-space: nowrap; display: flex; width: fit-content; list-style-type: none; margin: 0; padding: 0; } nav ul > li:has(> a) { padding: 0; } nav ul > li:has(> a) > a { display: inline-block; padding: var(--menu-padding); } nav ul li:focus-within > ul { display: flex; } nav li { position: relative; align-items: left; display: flex; box-sizing: border-box; justify-content: space-between; line-height: 1; padding: var(--menu-padding); } nav li:has(ul:hover):hover { color: #999; background: #444; } nav li:hover { color: #fff; background: #000; } nav ul:not(nav ul ul) { position: relative; text-align: left; flex-direction: column; white-space-collapse: collapse; left: 0; row-gap: var(--menu-v-separator); background: var(--menu-v-clr-tile-bk); box-shadow: var(--menu-v-shadow); } nav ul:not(nav ul ul) > li:focus-within:has(ul) { flex-direction: column; justify-content: space-between; } nav ul:not(nav ul ul) > li { display: flex; } nav ul:not(nav ul ul) > li > ul { bottom: calc(-1 * var(--menu-padding)); } nav ul:not(nav ul ul) ul { position: relative; } nav ul:not(nav ul ul) > li { width: 100%; color: var(--menu-v-clr-item-tx); background: var(--menu-v-clr-item-bk); } nav ul ul { display: none; position: relative; text-align: left; flex-direction: column; white-space-collapse: collapse; row-gap: var(--menu-v-separator); background: var(--menu-v-clr-tile-bk); box-shadow: var(--menu-v-shadow); } nav ul ul > li:focus-within:has(ul) { flex-direction: column; justify-content: space-between; } nav ul ul > li { display: flex; } nav ul ul > li > ul { bottom: calc(-1 * var(--menu-padding)); } nav ul ul ul { position: relative; } nav li li { color: var(--menu-v-clr-item-tx); background: var(--menu-v-clr-item-bk); }
HTML
<!DOCTYPE html> <html><head> </head><body> <nav> <ul> <li tabindex="0">[Item 1]</li> <li tabindex="0">[Item 2] <ul> <li tabindex="0">[Item 2.1]</li> <li>[Item 2.2] some</li> <li tabindex="0">[Item 2.3]</li> </ul> </li> <li tabindex="0">[Item 3]</li> <li tabindex="0">[Item 4] <ul tabindex="-1"> <li tabindex="0">[Item 4.1] some</li> <li tabindex="0">[Item 4.2] <ul tabindex="-1"> <li tabindex="0">[Item 4.2.1] <ul tabindex="-1"> <li tabindex="0">[Item 4.2.1.1]</li> <li tabindex="0">[Item 4.2.1.2]</li> <li tabindex="0">[Item 4.2.1.3]</li> </ul> <li tabindex="0">[Item 4.2.2]</li> <li><a href="">[Item 4.2.3]</a></li> </ul> </li> <li tabindex="0">[Item 4.3]</li> </ul> </li> <li tabindex="0">[Item 5]</li> <li tabindex="0">[Item 6] </li> </ul> </nav> </body></html>
解决方案
通过Grid布局+伪间距列+负margin组合实现,无需修改HTML结构:
修改后的完整CSS
:root { --menu-clr-item-bk:#84744dff; --menu-clr-item-tx:#ddddddff; --menu-clr-item-tx-hov:#84744dff; --menu-clr-item-bk-hov:#ddddddff; --menu-clr-item-tx-clildhov:#ccccccff; --menu-clr-item-bk-clildhov:#847b65ff; --menu-h-clr-tile-bk:#ddddddff; --menu-h-clr-item-bk:var(--menu-clr-item-bk); --menu-h-clr-item-bk-hov:var(--menu-clr-item-bk-hov); --menu-h-clr-item-tx:var(--menu-clr-item-tx); --menu-h-clr-item-tx-hov:var(--menu-clr-item-tx-hov); --menu-h-separator:0; --menu-h-shadow:none; --menu-v-clr-tile-bk:#ddddddff; --menu-v-clr-item-bk:var(--menu-clr-item-bk); --menu-v-clr-item-bk-hov:var(--menu-clr-item-bk-hov); --menu-v-clr-item-tx:var(--menu-clr-item-tx); --menu-v-clr-item-tx-hov:var(--menu-clr-item-tx-hov); --menu-v-separator:1px; --menu-v-shadow:0.1em 0.1em 0.1em #0008; --menu-padding:0.5em; --menu-checkmargin:0.5em; --menu-checksize:1em; } * { box-sizing: border-box; } body, html { height: 100%; } nav > h1 { display: none; } /* 改用Grid确保菜单项宽度自动匹配最大项 */ nav ul { position: absolute; white-space: nowrap; display: grid; grid-template-columns: 1fr; width: fit-content; list-style-type: none; margin: 0; padding: 0; } nav ul > li:has(> a) { padding: 0; } nav ul > li:has(> a) > a { display: block; padding: var(--menu-padding); } nav ul li:focus-within > ul { display: grid; } /* 基础LI样式 */ nav li { position: relative; align-items: center; display: grid; box-sizing: border-box; line-height: 1; padding: var(--menu-padding); } /* 含子菜单的LI:移除水平内边距,用三列网格模拟文本间距 */ nav li:has(> ul) { padding-left: 0; padding-right: 0; grid-template-columns: var(--menu-padding) 1fr var(--menu-padding); } nav li:has(> ul) > :first-child:not(ul) { grid-column: 2; } /* 子菜单横向填满父LI */ nav li > ul { grid-column: 1 / -1; margin-left: calc(-1 * var(--menu-padding)); margin-right: calc(-1 * var(--menu-padding)); width: calc(100% + 2 * var(--menu-padding)); } /* 保留原有hover交互 */ nav li:has(ul:hover):hover { color: #999; background: #444; } nav li:hover { color: #fff; background: #000; } nav ul:not(nav ul ul) { position: relative; text-align: left; white-space-collapse: collapse; left: 0; row-gap: var(--menu-v-separator); background: var(--menu-v-clr-tile-bk); box-shadow: var(--menu-v-shadow); } nav ul:not(nav ul ul) > li:focus-within:has(ul) { grid-template-columns: var(--menu-padding) 1fr var(--menu-padding); } nav ul:not(nav ul ul) > li { display: grid; } nav ul:not(nav ul ul) > li > ul { bottom: calc(-1 * var(--menu-padding)); } nav ul:not(nav ul ul) ul { position: relative; } nav ul:not(nav ul ul) > li { width: 100%; color: var(--menu-v-clr-item-tx); background: var(--menu-v-clr-item-bk); } nav ul ul { display: none; position: relative; text-align: left; grid-template-columns: 1fr; white-space-collapse: collapse; row-gap: var(--menu-v-separator); background: var(--menu-v-clr-tile-bk); box-shadow: var(--menu-v-shadow); } nav ul ul > li:focus-within:has(ul) { grid-template-columns: var(--menu-padding) 1fr var(--menu-padding); } nav ul ul > li { display: grid; } nav ul ul > li > ul { bottom: calc(-1 * var(--menu-padding)); } nav ul ul ul { position: relative; } nav li li { color: var(--menu-v-clr-item-tx); background: var(--menu-v-clr-item-bk); }
关键调整说明
- 宽度统一:所有菜单容器改用
display: grid+grid-template-columns: 1fr,自动让菜单项宽度匹配最大内容项; - 文本间距模拟:给含子UL的LI设置三列网格,文本放在中间列,左右列宽度等于
--menu-padding,替代直接给LI加内边距; - 子菜单填满宽度:子菜单设置
grid-column: 1 / -1跨越所有列,再通过负margin和宽度计算横向填满父容器。
内容的提问来源于stack exchange,提问作者rhavin
相关产品推荐
相关产品推荐

