I have this piece of code, my goal is to make custom select with smooth animation.
I've chosen to make my height by default 0 with overflow: hidden, and set height to auto when .active is added. But ran into problem that body is still shown. Problem seems to be with paddings
.select-header {
display: flex;
justify-content: space-between;
align-items: center;
height: 50px;
padding: 0 35px 0 20px;
background-color: #D6E7D2;
cursor: pointer;
transition: background 0.5s;
}
.select-body {
height: 0;
overflow: hidden;
padding-top: 27px;
padding-left: 20px;
padding-bottom: 31px;
background-color: #DCE9D9;
transition: all 0.5s;
}
.select.active .select-body {
height: auto;
}
.select-item {
line-height: 35px;
cursor: pointer;
color: #499A18;
}
.select-item:not(:last-child) {
padding-bottom: 12px;
}
<div class="select accordion">
<div class="select-header">
<span class="select-current">City</span>
<div class="select-icon"></div>
</div>
<div class="select-body">
<div class="select-item">Canandaigua, NY</div>
<div class="select-item">New York City</div>
<div class="select-item">Yonkers, NY</div>
<div class="select-item">Sherrill, NY</div>
</div>
</div>
I've tried putting body in container - didn't work. And to add padding when .active is added - this causes unexpected transition behavior.
The real problem here is that you cannot animate from height: 0; (size value) to height: auto; (keyword value), they must both be size values. You would need to know the exact expanded height. What you can do is animate the max-height from 0 to some really big size that should always be larger than the contents; in my example I use 200vh (twice the viewport height).
Also, just apply and remove the padding the same as you do with height.
const toggle = document.querySelector('.select-header')
toggle.addEventListener('click', () => toggle.parentElement.classList.toggle('active'))
.select-header {
display: flex;
justify-content: space-between;
align-items: center;
height: 50px;
padding: 0 35px 0 20px;
background-color: #D6E7D2;
cursor: pointer;
transition: background 0.5s;
}
.select-body {
height: auto;
max-height: 0;
overflow: hidden;
padding-top: 0;
padding-left: 20px;
padding-bottom: 0;
background-color: #DCE9D9;
transition: all 0.5s;
}
.select.active .select-body {
max-height: 200vh;
padding-top: 27px;
padding-bottom: 31px;
}
.select-item {
line-height: 35px;
cursor: pointer;
color: #499A18;
}
.select-item:not(:last-child) {
padding-bottom: 12px;
}
<div class="select accordion">
<div class="select-header">
<span class="select-current">City</span>
<div class="select-icon"></div>
</div>
<div class="select-body">
<div class="select-item">Canandaigua, NY</div>
<div class="select-item">New York City</div>
<div class="select-item">Yonkers, NY</div>
<div class="select-item">Sherrill, NY</div>
</div>
</div>
Related
So I am building this frontendmentor site and im having trouble positioning the shopping cart div on top of the rest of the elements. At first i tried using z-index but apparently that doesnt work with elements that have a position style. I used position relative on the parent and absolute on the child so it would always appear under the shopping cart icon. Is there any other way to force an element on top or should i change the way i position the div.
.right-container {
padding: 1rem;
}
.account-container {
display: flex;
align-items: center;
position: relative;
}
.cart-icon-container {
margin-right: 2rem;
}
.cart-icon-container img {
height: 30px;
width: 30px;
aspect-ratio: 1;
}
.shopping-cart-overlay {
position: absolute;
top: 5rem;
right: 100px;
width: 400px;
border-radius: 10px;
box-shadow: 0 1rem 10px rgba(0, 0, 0, 0.185);
}
.shopping-cart-content {
display: flex;
justify-content: center;
align-items: center;
padding: 2rem 1rem;
}
<div class="right-container">
<div class="account-container">
<div class="shopping-cart-overlay">
<div class="shopping-cart-header">
<h4>Cart</h4>
</div>
<div class="shopping-cart-content">
<p>Your cart is empty</p>
</div>
</div>
<a href="/index.html" class="cart-icon-container">
<img src="./images/icon-cart.svg" alt="cart">
</a>
<img class="avatar" src="./images/image-avatar.png" alt="profile-pic">
</div>
</div>
Well it does work. Read this to get a better understanding of how z-index works with position. You just couldn't notice it because the div was transparent.
Check out the snippet below. It's your exact code, but I changed the background and positioned the .shopping-cart-overlay element closer to the other elements, just to show you that it actually works.
.right-container {
padding: 1rem;
}
.account-container {
display: flex;
align-items: center;
position: relative;
}
.cart-icon-container {
margin-right: 2rem;
}
.cart-icon-container img {
height: 30px;
width: 30px;
aspect-ratio: 1;
}
.shopping-cart-overlay {
position: absolute;
//top: 5rem;
//right: 100px;
width: 400px;
border-radius: 10px;
box-shadow: 0 1rem 10px rgba(0, 0, 0, 0.185);
/* Changes */
top: 0;
left: 125px;
z-index: 99;
background-color: red;
}
.shopping-cart-content {
display: flex;
justify-content: center;
align-items: center;
padding: 2rem 1rem;
}
<div class="right-container">
<div class="account-container">
<div class="shopping-cart-overlay">
<div class="shopping-cart-header">
<h4>Cart</h4>
</div>
<div class="shopping-cart-content">
<p>Your cart is empty</p>
</div>
</div>
<a href="/index.html" class="cart-icon-container">
<img src="./images/icon-cart.svg" alt="cart">
</a>
<img class="avatar" src="./images/image-avatar.png" alt="profile-pic">
</div>
</div>
document.querySelectorAll('.sidebarCategory').forEach(el =>{
el.addEventListener('click', e =>{
let sub = el.nextElementSibling
if(sub.style.maxHeight){
el.classList.remove('opened')
sub.style.maxHeight=null
}
else{
document.querySelectorAll('.sidebarSubcategories').forEach(element => {
element.style.maxHeight=null
element.previousElementSibling.classList.remove('opened')
})
el.classList.add('opened')
sub.style.maxHeight = sub.scrollHeight + "px";
}
})
})
document.querySelectorAll('.sidebarSubcategories > div:not(.sidebarSubSubcategories)').forEach(el =>{
el.addEventListener('click', e =>{
let sub = el.nextElementSibling
if(sub!=null && sub.classList.contains('sidebarSubSubcategories'))
{
if(sub.style.maxHeight){
sub.style.maxHeight=null
sub.style.marginBottom=0
sub.style.padding='0 30px'
}
else{
sub.style.marginBottom='15px'
sub.style.padding='15px 30px'
sub.style.maxHeight = sub.scrollHeight + 30 + "px"
el.parentNode.style.maxHeight=el.parentNode.style.maxHeight.slice(0,el.parentNode.style.maxHeight.length-2) + sub.scrollHeight + 30 + "px"
}
}
})
})
.main{
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
width: 400px;
height: 300px;
overflow-y: auto;
background-color: aquamarine;
}
.sidebarCategory{
color: white;
position: relative;
height: 60px;
min-height: 60px;
display: flex;
align-items: center;
justify-content: flex-start;
font-size: 20px;
flex-wrap: wrap;
font-weight: 600;
fill: white!important;
cursor: pointer;
padding-left: 15px;
transition: 0.4s;
}
.sidebarCategory.opened{
background-color: rgba(3, 63, 109, 0.588);
}
.sidebarSubcategories{
max-width: 100%;
position: relative;
background-color: rgba(3, 63, 109, 0.588);
max-height: 0;
transition: 0.4s;
overflow-y: hidden;
}
.sidebarSubcategories > div:not(.sidebarSubSubcategories){
max-height: 45px;
/* height: 45px; */
position: relative;
min-height: 45px;
color: white;
cursor: pointer;
overflow-y: hidden;
display: flex;
align-items: center;
justify-content: flex-start;
flex-wrap: wrap;
padding: 0 30px;
border-bottom: 1px solid var(--blue);
}
.sidebarSubSubcategories{
max-height: 0;
overflow-y: hidden;
transition: 0.4s;
background-color: white;
border-radius: 5px;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
margin: 0 15px;
}
.sidebarSubSubcategories > div{
cursor: pointer;
transition: 0.2s;
display: flex;
justify-content: center;
border-radius: 5px;
align-items: center;
padding: 10px;
width: 49%;
}
.sidebarSubSubcategories > div:hover{
background-color: #f3f3f3;
}
<body>
<div class="main">
<div class="sidebarCategory">
Lorem ipsum dolor sit amet.
</div>
<div class="sidebarSubcategories">
<div>
Subcategory
</div>
<div class="sidebarSubSubcategories">
<div>Sub Subcategory</div>
<div>Sub Subcategory</div>
<div>Sub Subcategory</div>
<div>Sub Subcategory</div>
</div>
<div>
Subcategory
</div>
<div>
Subcategory
</div>
<div>
Subcategory
</div>
</div>
</div>
</body>
See the code, the first drop-down is not closing smoothly if I interact with the second drop-down menu.
I am changing the max-height property to create an accordion. I have placed another accordion inside the accordion. But when I interact with the inside accordion, the out-side accordion's transition does not work.
Looks like the issue is this line
el.parentNode.style.maxHeight.slice(0,el.parentNode.style.maxHeight.length-2) + sub.scrollHeight...."
Your adding a string and number which will always concatinate so '123' + 4 = the string '1234'. You need to parse to an int (you also won't need to slice off the px)
//Instead of
el.parentNode.style.maxHeight.slice(0,el.parentNode.style.maxHeight.length-2)
//Do this
parseInt(el.parentNode.style.maxHeight)
I think the problem is that the sidebarSubcategories element has no defined height. It has a max-heigth of 0 and then it is set to null via JavaScript.
The default value of height is auto, so, when you set maxHeight to null, you are setting max-height to auto, and transitions don't work on auto values.
That's because the browser doesn't know which value to transition to.
If you really need this and you can't a priori set each sidebarSubcategories' height, you'll need to store the innerHeight of each sidebarSubcategories (when it's expanded) and applying it back when expanded again.
I'm creating simple page with a <header> and a <section>. In the section I have 3 divs and I am positioning them with display: flex; and justify-content: space-between.
The problem is that I also use JS slideToggle() on two of them (extreme ones). It is changing the layout of my center div after they are going up. How can I do it so that my center div doesn't change position after one of the others is slid up?
$(document).ready(function() {
$('#playlist').click(function() {
$('#nav').slideToggle();
});
});
$(document).ready(function() {
$('#songs').click(function() {
$('#listSongs').slideToggle();
});
});
section {
display: flex;
justify-content: space-between;
}
#listSongs {
margin-top: 50px;
height: 550px;
background-color: black;
border-radius: 5px;
font-size: 25px;
width: 200px;
}
#listSongs p {
margin-top: 5px;
width: 200px;
text-align: center;
overflow: hidden;
height: 35px;
line-height: 50px;
color: white;
}
#player {
color: red;
}
#nav {
margin-top: 50px;
height: 550px;
background-color: black;
border-radius: 20px;
width: 200px;
}
.hidden {
margin-top: 50px;
height: 550px;
background-color: black;
border-radius: 20px;
width: 200px;
visibility: hidden;
}
#nav p {
text-align: center;
}
#nav ul {
list-style-type: none;
text-align: left;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section>
<div id="listSongs">
<p>Authors:</p>
<div class="after"></div>
</div>
<div id="player">
<p>something</p>
</div>
<div id="nav">
<p>something</p>
</div>
</section>
The issue is because when the slideUp/slideDown/slideToggle methods complete, they set display: none on the target element. This is what causes the layout of your page to shift.
To workaround, and improve the animation, you can use CSS instead. Use the transition property to animate the height setting. Then you can toggle a class which sets height: 0 on the target element. Try this:
$(document).ready(function() {
$('#playlist').click(function() {
$('#nav').toggleClass('hide');
});
$('#songs').click(function() {
$('#listSongs').toggleClass('hide');
});
});
body { background-color: #CCC; }
section {
display: flex;
justify-content: space-between;
}
section > div.panel {
margin-top: 50px;
height: 550px;
background-color: black;
border-radius: 5px;
font-size: 25px;
width: 200px;
transition: height 0.4s;
overflow: hidden;
}
section > div.panel.hide {
height: 0;
}
section > div.panel p {
margin-top: 5px;
width: 200px;
text-align: center;
overflow: hidden;
height: 35px;
line-height: 50px;
color: white;
}
#player {
color: red;
}
#nav {
border-radius: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="playlist">Playlist</button>
<button id="songs">Songs</button>
<section>
<div id="listSongs" class="panel">
<p>Authors:</p>
<p>lorem ipsum</p>
<div class="after"></div>
</div>
<div id="player">
<p>something</p>
</div>
<div id="nav" class="panel">
<p>something</p>
</div>
</section>
Note that I also rearranged some of the CSS to make it more generic with less repetition.
In jQuery, there is this method called .slideToggle() that magically slides up and down an element regardless of its height and padding; I want to achieve the same without using that library, so I made 2 experiments: One has an element that has height and the other one has an element that has no height set, only formed by padding.
Experiment 1 (has height):
document.querySelector('#experiment-1 > button').addEventListener('click', function() {
this.nextElementSibling.classList.toggle('slideToggle');
})
#experiment-1 > button {
display: block;
cursor: pointer;
}
#experiment-1 > .box {
background: #eee;
color: #aaa;
width: 200px;
height: 200px;
display: flex;
align-items: center;
justify-content: center;
transition: all 3s linear;
}
#experiment-1 > .box.slideToggle {
height: 0;
padding: 0;
overflow: 0;
visibility: hidden;
}
<section id="experiment-1">
<button>Slide Toggle</button>
<div class="box">
<span>Experiment 1</span>
</div>
</section>
Experiment 2 (padding only):
document.querySelector('#experiment-2 > button').addEventListener('click', function() {
this.nextElementSibling.classList.toggle('slideToggle');
})
#experiment-2 > button {
display: block;
cursor: pointer;
}
#experiment-2 > .box {
background: #eee;
color: #aaa;
display: inline-block;
padding: 5rem;
transition: all 3s linear;
}
#experiment-2 > .box.slideToggle {
padding: 0;
overflow: 0;
visibility: hidden;
}
<section id="experiment-2">
<button>Slide Toggle</button>
<div class="box">
<span>Experiment 2</span>
</div>
</section>
In experiment 1, I think it's almost perfect, only that when the box is almost closed, the text still has overflow, I set the trasition duration to 3 seconds to make it more obvious.
In experiment 2, the only problem is the box slides diagonally, not vertically.
How can I make these two work perfectly without using jQuery?
overflow: hidden
Adding overflow: hidden to #experiment-1 > .box will hide the <span>s text when the <div> is shorter than its height.
To fix the second example, simply change the line
padding: 0;
to read
padding: 0 5rem;
That will keep the box from getting narrower as it gets shorter.
You can do like this:
For first and second experiment change overflow:hidden form overflow:0
document.querySelector('#experiment-1 > button').addEventListener('click', function() {
this.nextElementSibling.classList.toggle('slideToggle');
})
#experiment-1 > button {
display: block;
cursor: pointer;
}
#experiment-1 > .box {
background: #eee;
color: #aaa;
width: 200px;
height: 200px;
display: flex;
align-items: center;
justify-content: center;
transition: all 3s linear;
}
#experiment-1 > .box.slideToggle {
height: 0;
padding: 0;
overflow: hidden;
visibility: hidden;
}
<section id="experiment-1">
<button>Slide Toggle</button>
<div class="box">
<span>Experiment 1</span>
</div>
</section>
For the second the padding:5rem 5rem reduce only the padding of top and bottom like padding:0 5rem
document.querySelector('#experiment-2 > button').addEventListener('click', function() {
this.nextElementSibling.classList.toggle('slideToggle');
})
#experiment-2 > button {
display: block;
cursor: pointer;
}
#experiment-2 > .box {
background: #eee;
color: #aaa;
display: inline-block;
padding: 5rem 5rem;
transition: all 3s linear;
}
#experiment-2 > .box.slideToggle {
padding: 0 5rem;
overflow: hidden;
visibility: hidden;
}
<section id="experiment-2">
<button>Slide Toggle</button>
<div class="box">
<span>Experiment 2</span>
</div>
</section>
I have 3 images side-by-side, I would like to know how to get some spacing between them. I have tried everything, margins, padding and I don't know what to do.
.content1 {
background-image: url("http://www.thefreeloves.com/prototype/test/wp-content/uploads/2014/02/album-title.jpg");
color: white;
text-align: center;
width: 100%;
height: 20%;
display: block;
float: left;
}
.text1 {
font-family: "Goudy Old Style", Optima, sans-serif;
font-size: 40px;
margin-bottom: 0;
margin-top: 45px;
}
.text2 {
font-size: 30px;
color: #6CB9D9;
}
.album1 {
float: left;
width: 31%;
text-align: center;
}
.album2 {
display: inline-block;
width: 31%;
text-align: center;
}
.album3 {
float: right;
width: 31%;
text-align: center;
}
.album {
width: 100%;
overflow: hidden;
background-color: #191919;
}
<div class="content1">
<h3 class="text1">Our Latest Album<span class="slash"> / </span><span class="text2">Fresh from the house of Music Club Band</span></h3>
</div>
<div class="album">
<div class="album1">
<img src="http://www.thefreeloves.com/prototype/test/wp-content/uploads/2015/02/FDA9133-650x385.jpg" alt="album1">
</div>
<div class="album2">
<img src="http://www.thefreeloves.com/prototype/test/wp-content/uploads/2015/02/FDA9099-650x385.jpg" alt="album2">
</div>
<div class="album3">
<img src="http://www.thefreeloves.com/prototype/test/wp-content/uploads/2015/02/FDA0373-650x385.jpg" alt="album3" class="album4">
</div>
</div>
You set your wrapping div's to 31% but you didn't change the size of your images so they were flowing outside the wrappers. If you set Overflow: hidden; on your album1, album2 and album3 div's you'll see that your margins are working on the divs but you'll only see part of your images. if you set the imgs themselves to a width of 100% as below you'll see it works.
.content1 {
background-image: url("http://www.thefreeloves.com/prototype/test/wp-content/uploads/2014/02/album-title.jpg");
color: white;
text-align: center;
width: 100%;
height: 20%;
display: block;
float: left;
}
.text1 {
font-family: "Goudy Old Style", Optima, sans-serif;
font-size: 40px;
margin-bottom: 0;
margin-top: 45px;
}
.text2 {
font-size: 30px;
color: #6CB9D9;
}
.album1 {
float: left;
width: 31%;
text-align: center;
margin: 1%;
}
.album2 {
display: inline-block;
width: 31%;
text-align: center;
margin: 1%;
}
.album3 {
float: right;
width: 31%;
text-align: center;
margin: 1%;
}
.album {
width: 100%;
overflow: hidden;
background-color: #191919;
}
.album img { width: 100%; }
<div class="content1">
<h3 class="text1">Our Latest Album<span class="slash"> / </span><span class="text2">Fresh from the house of Music Club Band</span></h3>
</div>
<div class="album">
<div class="album1">
<img src="http://www.thefreeloves.com/prototype/test/wp-content/uploads/2015/02/FDA9133-650x385.jpg" alt="album1">
</div>
<div class="album2">
<img src="http://www.thefreeloves.com/prototype/test/wp-content/uploads/2015/02/FDA9099-650x385.jpg" alt="album2">
</div>
<div class="album3">
<img src="http://www.thefreeloves.com/prototype/test/wp-content/uploads/2015/02/FDA0373-650x385.jpg" alt="album3" class="album4">
</div>
</div>
In each div tag, just place the following style = "margin-right: 20px;". So for example, for the first image, change it ot this
div style = "margin-right: 20px;" class="album1">
You don't need to use float, you can simply set the display of the albums to inline-block, and set the text-align of their parent to center. Note that the three images there are too wide to be in one line, so you'll have to adjust that yourself.
.album1 img, .album2 img, .album3 img {
//set img width and height here
}
.album1 {
display: inline-block;
//add padding/margin here
}
.album2 {
display: inline-block;
//add padding/margin here
}
.album3 {
display: inline-block;
//add padding/margin here
}
.album {
width: 100%;
overflow: hidden;
background-color: #191919;
text-align:center;
}