I want to layout list items evenly across a vertical container. All items have the same height (the last one has padding).
Normally I would use flex, but here is the challenge: The container expands with a transition to its final height - which is exactly the size of all li items stacked up with no overlap. In other words, flex is not relevant since the container height will forever be <= height of each item * the number of items.
What I need, is for the items to always spread out evenly, overlapping each other while filling up the container (starting from a complete overlap until they finally are stacked as they would with no intervention).
Here is a sandbox link to the setup of the problem: https://codesandbox.io/s/holy-water-uln898?file=/index.html
Would appreciate your help!
const expand = () => {
document.getElementsByTagName("ul")[0].classList.add("expanded");
lis = document.getElementsByTagName("li")
for (let i = 1; i < lis.length; i ){
lis[i].style.top= i*25 "px"
lis[i].classList.add("expand");
}
};
ul {
padding: 0;
border: 3px solid;
height: calc(25px 8px);
transition: height 2s;
position:relative;
}
ul.expanded {
height: calc((25px * 3) 8px);
}
li {
list-style: none;
height: 25px;
position:absolute;
left:0;
top:0;
width:100%
}
li:last-child {
padding-bottom: 8px;
}
li.item1 {
background: red;
color: darkred;
}
li.item2 {
background: green;
color: darkgreen;
}
li.item3 {
background: yellow;
color: orange;
}
button {
position: absolute;
top: 150px;
}
li.expand{
transition:top 2s;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Static Template</title>
</head>
<body>
<ul>
<li >
item1
</li>
<li >
item2
</li>
<li >
item3
</li>
</ul>
<button onClick="expand();">Click</button>
</body>
</html>
CodePudding user response:
You can accomplish this with a bunch of wrappers. The list items are wrapped in an item with 0 height. Flex won't stack all the 0-height elements on top of each other unless its height is also 0 (because we must justify-content: space-between to distribute them when height is greater than 0), so the inner flex container has a height of 0 when closed, and the outer visible container (with the border) just adds a single items-worth of height to flex-container. The javascript is only for the demo.
This only works if every item has a known, static height. Transitioning height to auto
is not easy by itself, let alone after you throw in some fancy overlapping.
let fullHeight = true
const button = document.querySelector(".button")
const container = document.querySelector(".container-restraint")
button.addEventListener("click", e => {
if (fullHeight) container.className = "container-restraint closed"
else container.className = "container-restraint"
fullHeight = !fullHeight
})
.list-item {
height: 100px; /* ITEM-HEIGHT */
width: 200px; /* ITEM-WIDTH */
}
.item-1 { background-color: #ff000066; }
.item-2 { background-color: #00ff0066; }
.item-3 { background-color: #0000ff66; }
.item-4 { background-color: #ffff0066; }
.container-restraint {
height: 300px; /* ITEM-HEIGHT * (N_ITEMS - 1) */
width: 200px; /* ITEM-WIDTH */
display: flex;
flex-flow: column;
transition: height 3s;
overflow: visible;
justify-content: space-between;
}
.container-restraint.closed {
height: 0;
}
.expanding-container {
width: max-content;
border: 3px solid black;
overflow: hidden;
padding-bottom: 100px; /* ITEM_HEIGHT */
}
.wrapper {
height: 0;
overflow: visible;
padding: 0;
}
<button >transition</button>
<div >
<div >
<div >
<div ></div>
</div>
<div >
<div ></div>
</div>
<div >
<div ></div>
</div>
<div >
<div ></div>
</div>
</div>
</div>
CodePudding user response:
You could use position: absolute;
and jQuery animations to achieve the overlapping effect, like this:
const expand = () => {
document.getElementsByTagName("ul")[0].classList.add("expanded");
$('li').each(function(index, li){
$(li).animate({
top: (index * 25) 'px'
}, 2000);
});
};
ul {
padding: 0;
border: 3px solid;
height: calc(25px 8px);
transition: height 2s;
position: relative;
}
ul.expanded {
height: calc((25px * 3) 8px);
}
li {
list-style: none;
height: 25px;
position: absolute;
top: 0;
left: 0;
right: 0;
}
li:last-child {
padding-bottom: 8px;
}
li.item1 {
background: red;
color: darkred;
}
li.item2 {
background: green;
color: darkgreen;
}
li.item3 {
background: yellow;
color: orange;
}
button {
position: absolute;
top: 150px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
<li >
item1
</li>
<li >
item2
</li>
<li >
item3
</li>
</ul>
<button onClick="expand();">Click</button>