英文:
How to use Flex with multiple levels of list item and maintain accessible content
问题
我正在使用原生 HTML、CSS 和 JS 构建一个巨大的菜单组件。这个巨大的菜单有三个层次的内容,第一个沿横幅的方向是 row
。
然而,我在尝试使用 Flex 进行布局时遇到了问题,因为我无法保持正确的语义布局。第二层应该是一个列,位于左侧,而第三层应该在右侧显示为瓷砖。通常,我会使用网格布局来分割这两个部分,但在这种情况下我似乎无法做到这一点。
例如,我可以对第二层的 ul
应用 left:0
,但我需要该 ul
的子元素在右侧。
我附上了一张我想实现的类似设计的图片。有人可以提供建议吗?
英文:
I'm building a mega menu component using vanilla HTML, CSS & JS. The mega menu has 3 levels of content, with the first being in the direction of row
along the banner.
I'm struggling however to use Flex how I normally would whilst maintaining the correct semantic layout. Level 2 should be a column to the left with level 3 displayed as tiles on the right. Usually I would use a grid layout to split these two sections out but I can't seem to make that happen in this case.
For example, I can apply left:0
to the level 2 ul
but I need that ul
s children to be on the right?
I've attached an image of a similar design I'd love to implement. Can anyone advise?
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-css -->
.container {
background-color: lightgreen;
min-height: 40px;
}
.nav-menu {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
li {
list-style: none;
}
li ul {
position: absolute;
top: 152px;
}
<!-- language: lang-html -->
<div class="container">
<ul class="nav-menu">
<li>
<a href="#">Level 1</a>
<ul>
<li>
<a href="#">Level 2</a>
<ul>
<li><a href="#">Level 3</a></li>
<li><a href="#">Level 3</a></li>
<li><a href="#">Level 3</a></li>
<li><a href="#">Level 3</a></li>
<li><a href="#">Level 3</a></li>
</ul>
</li>
<li><a href="#">Level 2</a></li>
<li><a href="#">Level 2</a></li>
<li><a href="#">Level 2</a></li>
</ul>
</li>
<li><a href="#">Level 1</a></li>
<li><a href="#">Level 1</a></li>
<li><a href="#">Level 1</a></li>
<li><a href="#">Level 1</a></li>
</ul>
</div>
<!-- end snippet -->
答案1
得分: 0
以下是您要翻译的内容:
制作这个效果的关键是确保所有元素重叠,因此在悬停时,移动指针将始终确保它位于相关的li元素内部。然后,我们在悬停的父元素上使用position: relative
,在子元素上使用position: absolute
,以将菜单定位在我们想要的任何位置。position: absolute
的一个问题是很难使它们扩展到全屏宽度,因为包含块不是屏幕。
对于第3级,我将包含块保持为第1级元素,以使其定位在导航栏的正下方。
英文:
The trick to making this work is to ensure all your elements overlap so on hover, moving the pointer will always make sure it's inside the relevant li element. We then use position: relative
on the hovered parent and position: absolute
on the child to position the menu wherever we want. One issue with position: absolute
is that it's hard to make them expand to full screen width as the containing block is not the screen.
For level 3, I'm keeping the containing block as the level 1 element to keep it positioned just below the nav bar.
Annotated
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-css -->
/* pretty = no functional value, just makes it look nice */
li, ul, body {
margin:0; /*pretty*/
padding:0; /*pretty*/
}
.container {
--min-height-value: 40px;
background-color: lightgray; /*pretty*/
height:1px; /* added this to make .nav-menu height:100% work */
min-height: var(--min-height-value); /*pretty*/
}
.nav-menu {
position: relative; /* added this so we position descendants relative to the parent menu*/
display: flex;
flex-direction: row;
align-content: stretch; /* also make the child elements full height so the mouse hovers within the li at all times */
justify-content: space-between;
height: 100%; /* this height needs to match the parent so the hover remains inside the li at all times */
margin-inline:0.5rem;
}
.level2 {
--top-overhang: calc(var(--min-height-value) * 0.5); /* this positions the level 2 menu below the nav-menu */
position:absolute;
display: flex;
flex-direction:column;
top:50%;
left:0px;
padding-top:var(--top-overhang);
display:none;
background-color:#eee;
z-index:-1; /* make it appear below the nav bar, again, to keep the menu visible on hover*/
}
.nav-menu > li {
padding-inline: 1rem; /*pretty*/
}
.nav-menu > li:hover > .level2 {
display:block;
}
li:hover {
background:lightgray; /*pretty*/
}
.level2 > li {
padding-block:0.5rem; /*pretty*/
padding-inline:2rem; /*pretty*/
}
li {
list-style: none; /*pretty*/
}
a {
text-decoration:none; /*pretty*/
color:inherit; /*pretty*/
}
.nav-menu > li > a {
display:flex;
align-items:center;
height:100%;
}
.level3 {
position: absolute;
border: 1px solid lightgray; /*pretty*/
left:100%;
top:var(--top-overhang);
width:max-content;
display:none;
grid-template-columns: 1fr 1fr;
grid-auto-rows: 1fr;
gap: 0.5rem;
}
.level2 > li:hover > .level3 {
display: grid;
}
.level3 > li {
width: 10rem; /*pretty*/
aspect-ratio: 1/1; /*pretty*/
position: relative;
display:flex;
align-items: flex-end;
padding: 0.5rem; /*pretty*/
}
.level3 > li::before {
position:absolute; /*pretty*/
content:"";
inset:0.5rem; /*pretty*/
bottom: 1.5rem; /*pretty*/
border-radius: 0.5rem; /*pretty*/
background-color:gray; /*pretty*/
}
<!-- language: lang-html -->
<div class="container">
<ul class="nav-menu">
<li>
<a href="#">Level 1</a>
<ul class='level2'>
<li>
<a href="#">Level 2a</a>
<ul class='level3'>
<li><a href="#">Level 3a</a></li>
<li><a href="#">Level 3b</a></li>
<li><a href="#">Level 3c</a></li>
<li><a href="#">Level 3d</a></li>
<li><a href="#">Level 3e</a></li>
</ul>
</li>
<li><a href="#">Level 2b</a></li>
<li><a href="#">Level 2c</a>
<ul class='level3'>
<li><a href="#">Level 3f</a></li>
<li><a href="#">Level 3g</a></li>
<li><a href="#">Level 3h</a></li>
<li><a href="#">Level 3i</a></li>
</ul>
</li>
<li><a href="#">Level 2d</a></li>
</ul>
</li>
<li><a href="#">Level 1</a></li>
<li><a href="#">Level 1</a></li>
<li><a href="#">Level 1</a></li>
<li><a href="#">Level 1</a></li>
</ul>
</div>
<!-- end snippet -->
code below:
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论