提问人:Dog 提问时间:7/14/2022 最后编辑:Dog 更新时间:7/16/2022 访问量:163
如何让 flex 容器中的项目允许其嵌套的子项出现在其下方?
How to have items in a flex container allow their nested children to appear below it onClick?
问:
我有一个带有 flex 方向行的 flex 容器,其中包含一系列呈现到屏幕的项目,如下图所示:
单击其中一个项目后,我需要一系列项目出现在它下面。例如,单击“顶级项目 1”后,以下项目应如下图所示:
虽然我知道可以将两个容器(一个包含所有顶级项目,另一个包含所有最低级别项目)彼此叠放在一起,并简单地更新底部的容器 onClick,但这会导致对齐问题添加更多项目或将非项目添加到顶层。
相反,我正在尝试找到一种方法,将底层项目嵌套在其关联的顶级项目中。这种方法有效,但显然它会向下推边界,如下图所示:
如何实现图像 2 onClick 的布局,但仍将单个底层项目 div 嵌套在其关联的顶级项目 div 中?
例如,以类似于以下方式的方式:
<div>
<div>Top Level Item 1</div>
<div>
<div>bottom Level Item 1</div>
<div>bottom Level Item 2</div>
</div>
</div>
如果边框导航上有高度,并且项目居中,则下拉项目上的绝对位置将不起作用。因此,我也在尝试找到一种方法来解释该用例。例如,如果在容器上放置高度,并在下拉列表项上放置 position: absolute,就会发生这种情况。
感谢您的任何见解!
答:
使用确实是解决方案(我开始研究它并看到一个更快的评论)。position:absolute
我仍然想做挑战,让我知道这是否是你想要的结果。
document.querySelectorAll('.level').forEach(el => {
el.addEventListener('click', (ev) => {
const toggleNode = [...el.childNodes].filter(child => child.classList && child.classList.contains('secondary'));
if (toggleNode.length) {
toggleNode[0].classList[toggleNode[0].classList.contains('hidden') ? 'remove' : 'add']('hidden');
}
})
})
.flex {
position: relative;
display: flex;
align-items: center;
border: 1px solid black;
height: 130px;
}
.level {
flex-basis: 33%;
flex-grow: 1;
}
.secondary {
position: absolute;
top: 130px;
}
.hidden {
display: none;
}
<div class="flex">
<div class="level">
<div>Top Level Item 1</div>
<div class="secondary hidden">
<div>bottom Level Item 1</div>
<div>bottom Level Item 2</div>
</div>
</div>
<div class="level">
<div>Top Level Item 2</div>
<div class="secondary hidden">
<div>bottom Level Item 1</div>
<div>bottom Level Item 2</div>
</div>
</div>
<div class="level">
<div>Top Level Item 3</div>
<div class="secondary hidden">
<div>bottom Level Item 1</div>
<div>bottom Level Item 2</div>
</div>
</div>
</div>
评论
top
height: 100px
top: 100px;
position: absolute
网格手风琴 (NO 位置: 绝对位置)
下拉列表是没有的,因此在展开时,它们下面的内容会在页面中向下推。position: absolute
要获得切换开关周围的实心边框,请在每个切换开关周围添加顶部和底部边框,然后使用伪选择器将左边框添加到和将右边框添加到:first-of-type
:last-of-type
您可以通过将以下内容添加到任何父元素来设置希望顶级项使用的行数:.items
style: --topLevelLines: [line number];
切换开关中超过行限制的文本将被截断。行限制下的文本将垂直居中。不设置值将默认为单行。
JavaScript 将检查是否有用于添加点击事件侦听器的下拉预设。如果没有下拉列表,则将获得一个类,并相应地应用一些样式.item
.nodrop
const lists = Array.from(document.getElementsByClassName('items'));
lists.forEach( list => {
const items = Array.from(list.children);
items.forEach( item => {
const toggle = item.getElementsByClassName('toggle')[0];
const dropdown = item.getElementsByClassName('dropdown')[0];
dropdown ? toggle.addEventListener('click', function () { item.classList.toggle('active') }) : toggle.classList.add('nodrop');
});
});
* { box-sizing: border-box } body { font: 16px sans-serif; margin: 0 }
.items {
display: flex;
margin: auto auto -1px;
max-width: 960px;
}
.items:last-of-type { margin: auto }
.item {
display: grid;
grid-template-rows: auto 1fr;
flex: 1 0 0%;
transition: background-color .25s ease-in-out;
}
.toggle {
border-top: 1px solid currentColor;
border-bottom: 1px solid currentColor;
cursor: pointer;
display: grid;
height: calc( var(--topLevelLines, 1) * 1.25rem + ( .5rem * 2 ) );
line-height: 1.25rem;
padding: .5rem;
place-items: center center;
transition: padding .25s ease-in-out;
}
.toggle.nodrop { cursor: not-allowed }
.toggle:not(.nodrop)::after {
content: "⬇️";
font: 1em 'Segoe UI Emoji';
grid-area: 1/1/-1/-1;
margin-left: auto;
opacity: 0;
transition: transform .25s ease-in-out;
}
.toggle:not(.nodrop):hover { background-color: rgba(0, 0, 0, 0.125) }
.toggle:not(.nodrop):hover::after { opacity: 1 }
.toggle > span {
grid-area: 1/1/-1/-1;
display: -webkit-box;
-webkit-line-clamp: var(--topLevelLines, 1);
-webkit-box-orient: vertical;
overflow: hidden;
margin-right: auto;
}
.toggle.nodrop > span { opacity: 0.5 }
.dropdown {
overflow: hidden;
transition: background-color .25s ease-in-out;
}
.dropdown > div {
cursor: crosshair;
max-height: 0;
opacity: 0;
overflow: hidden;
padding: 0 .5rem;
transform: translateY(-100%);
transition: opacity .25s ease-in-out, padding .25s ease-in-out, transform .25s ease-in-out;
}
.dropdown > div:hover { background-color: rgba(0, 0, 0, 0.125) }
.active { box-shadow: inset 0 0 0 .5px rgba( 0, 0, 0, 0.25 ) }
.active .toggle { background-color: rgba(0, 0, 0, 0.25) }
.active .toggle::after { transform: scaleY(-1) }
.active .dropdown {
background-color: rgba(0, 0, 0, 0.125);
padding: .25rem 0
}
.active:hover { background-color: rgba(125,255,125,0.25) }
.active .dropdown > div {
opacity: 1;
padding: .25rem .5rem;
max-height: none;
transform: translateY(0);
}
.active .dropdown > div:last-of-type { margin-bottom: 0 }
.item:first-of-type .toggle { border-left: 1px solid currentColor }
.item:last-of-type .toggle { border-right: 1px solid currentColor }
<div class="items">
<div class="item">
<div class="toggle"><span>Toggle 1</span></div>
<div class="dropdown">
<div>bottom Level Item 1</div>
<div>bottom Level Item 2</div>
</div>
</div>
<div class="item">
<div class="toggle"><span>Toggle 2 (no children)</span></div>
</div>
<div class="item">
<div class="toggle"><span>Toggle 3</span></div>
<div class="dropdown">
<div>bottom Level Item 6</div>
<div>bottom Level Item 7</div>
<div>bottom Level Item 8</div>
<div>bottom Level Item 9</div>
</div>
</div>
</div>
<div class="items" style="--topLevelLines: 2">
<div class="item">
<div class="toggle"><span>Toggle 4 with lots of text in it that will eventually cause it to overflow the line restrictions rhoncus arcu cum ac vestibulum volutpat a luctus parturient nascetur condimentum dui penatibus habitant vestibulum vestibulum euismod id parturient porta ullamcorper viverra ultricies per integer a. A non sit adipiscing dis orci eget ac mi mauris nunc vestibulum gravida nascetur a nisl proin sociis adipiscing netus sit. Per id est posuere a a varius habitasse imperdiet laoreet consectetur vestibulum vestibulum nec a sit euismod. Consectetur vel vitae interdum mollis dis integer etiam non adipiscing vestibulum tempor ligula ultricies laoreet semper libero ligula consequat mollis a scelerisque elit ac sed viverra. Orci per suspendisse a eleifend mus a primis a orci nam augue condimentum leo ullamcorper sem volutpat sit condimentum vestibulum a velit eros nibh non mi. Sodales lorem malesuada bibendum sem parturient ligula dis a vulputate orci suspendisse curabitur varius porttitor vestibulum adipiscing parturient nam nam cursus sagittis.</span></div>
<div class="dropdown">
<div>bottom Level Item 10</div>
<div>bottom Level Item 11</div>
</div>
</div>
<div class="item">
<div class="toggle"><span>Toggle 5</span></div>
<div class="dropdown">
<div>bottom Level Item 12</div>
<div>bottom Level Item 13</div>
<div>bottom Level Item 14</div>
</div>
</div>
<div class="item">
<div class="toggle"><span>Toggle 6</span></div>
<div class="dropdown">
<div>bottom Level Item 15</div>
<div>bottom Level Item 16</div>
<div>bottom Level Item 17</div>
<div>bottom Level Item 18</div>
</div>
</div>
<div class="item">
<div class="toggle"><span>Toggle 7<br>(no children)</span></div>
</div>
</div>
<div class="items" style="--topLevelLines: 3">
<div class="item">
<div class="toggle"><span>Toggle 8 with lots of text in it that will eventually cause it to overflow the line restrictions rhoncus arcu cum ac vestibulum volutpat a luctus parturient nascetur condimentum dui penatibus habitant vestibulum vestibulum euismod id parturient porta ullamcorper viverra ultricies per integer a. A non sit adipiscing dis orci eget ac mi mauris nunc vestibulum gravida nascetur a nisl proin sociis adipiscing netus sit. Per id est posuere a a varius habitasse imperdiet laoreet consectetur vestibulum vestibulum nec a sit euismod. Consectetur vel vitae interdum mollis dis integer etiam non adipiscing vestibulum tempor ligula ultricies laoreet semper libero ligula consequat mollis a scelerisque elit ac sed viverra. Orci per suspendisse a eleifend mus a primis a orci nam augue condimentum leo ullamcorper sem volutpat sit condimentum vestibulum a velit eros nibh non mi. Sodales lorem malesuada bibendum sem parturient ligula dis a vulputate orci suspendisse curabitur varius porttitor vestibulum adipiscing parturient nam nam cursus sagittis.</span></div>
<div class="dropdown">
<div>bottom Level Item 21</div>
<div>bottom Level Item 22</div>
<div>bottom Level Item 23</div>
</div>
</div>
<div class="item">
<div class="toggle"><span>Toggle 9</span></div>
<div class="dropdown">
<div>bottom Level Item 24</div>
<div>bottom Level Item 25</div>
<div>bottom Level Item 26</div>
<div>bottom Level Item 27</div>
</div>
</div>
</div>
评论
我在使用 (MDN: Details disclosure element) 时看到了这个问题,并想向您展示已接受答案的替代解决方案。<details><summary>
此示例使用一个元素作为由嵌套元素组成的三个主导航栏菜单的主父容器。由于这些元素已经包含打开/关闭机制,因此 Javascript 是可选的,并且仅在当前导航栏菜单应关闭时才需要,当用户选择另一个导航栏菜单时(“失去焦点”)。position: fixed
<nav>
<details><summary>
click.eventListener
Fixed/Absolute
定位仅用于在视口顶部附加导航栏(可选,也可以)并绘制其边框。relative
除了一些令人眼花缭乱的东西外,导航栏菜单还使用常规/默认的CSS值。
代码片段(使用可选的 JS 切换活动菜单):
var currentDetail;
// top summary of menu only
document.querySelectorAll('.navmenu>summary').forEach(el => {
// triggers before <details> 'toggle' event
el.addEventListener("click", event => {
// Parent of top summary
const closest = event.currentTarget.closest('.navmenu');
if (closest.open) {
currentDetail = null; // all menus closed
}
else { // not null and different menu
if ((currentDetail) && (currentDetail != closest)) {
currentDetail.removeAttribute('open'); // close current open menu
};
currentDetail = closest; // save new (to be 'toggled') opened menu
}
});
});
*, ::before, ::after { -webkit-box-sizing: border-box; box-sizing: border-box }
html, body { width: 100%; max-width: 100% }
body { min-height: 100vh; margin: 0 }
:root {
--doc-lh : 1.5; /* document line-height */
--nav-pad: .25rem; /* navbar padding */
--nav-bd : 1px; /* navbar border size */
--mnu-pad: 0.25rem; /* main menu padding */
--mnu-br : 1em; /* border radius */
}
nav {
top: 0; left: 0; z-index: 999;
position: fixed; /* or 'relative' */
width: 100%; /* fill parent width */
height: auto; /* just making sure */
display: flex; justify-content: space-evenly;
padding: var(--nav-pad);
}
nav::after {
position: absolute; inset: 0; content: ""; z-index: -1;
/* parent padding + navmenu padding + border + line-height */
height: calc(2 * var(--nav-pad) +
2 * var(--mnu-pad) +
2 * var(--nav-bd) +
var(--doc-lh) * 1em);/**/
border: var(--nav-bd) solid black;
}
nav details { align-self: flex-start }
.navmenu {
padding: var(--mnu-pad) calc(3 * var(--mnu-pad));
}
.navmenu[open]>summary { /* to move it below the navbar */
padding-bottom: calc( var(--nav-pad) + 2 * var(--mnu-pad) + var(--nav-bd) )
}
/******************/
/* Eye-candy only */
/******************/
details { width: min(14rem, 100%) }
summary { cursor: pointer }
details>details,
summary + * { padding-left: 1em } /* saw-tooth layout */
.navmenu {
background-color: hsl(0,0%,86.3%, .95); /* Gainsboro */
border: 1px solid hsl(0,0%,76.3%, 1); /* Gainsboro L-10 */
border-radius: var(--mnu-br);
}
<nav>
<details class="navmenu" id="det-01">
<summary>menu 1</summary>
<details>
<summary>option 1.1</summary>
<details>
<summary>option 1.1.1</summary>
<div>option text 1.1.1</div>
</details>
<details>
<summary>option 1.1.2</summary>
<div>option text 1.1.2</div>
</details>
</details>
<details>
<summary>option 1.2</summary>
<div>option text 1.2</div>
</details>
<details>
<summary>option 1.3</summary>
<div>option text 1.3</div>
</details>
</details>
<details class="navmenu" id="det-02">
<summary>menu 2</summary>
<details>
<summary>option 2.1</summary>
<details>
<summary>option 2.1.1</summary>
<div>option text 2.1.1</div>
</details>
<details>
<summary>option 2.1.2</summary>
<div>option text 2.1.2</div>
</details>
</details>
<details>
<summary>option 2.2</summary>
<div>option text 2.2</div>
</details>
<details>
<summary>option 2.3</summary>
<div>option text 2.3</div>
</details>
</details>
<details class="navmenu" id="det-03">
<summary>menu 3</summary>
<details>
<summary>option 3.1</summary>
<details>
<summary>option 3.1.1</summary>
<div>option text 3.1.1</div>
</details>
<details>
<summary>option 3.1.2</summary>
<div>option text 3.1.2</div>
</details>
</details>
<details>
<summary>option 3.2</summary>
<div>option text 3.2</div>
</details>
<details>
<summary>option 3.3</summary>
<div>option text 3.3</div>
</details>
</details>
</nav>
评论