I have a container as a header of a collapsible dropdown with a max-height of 30px. I'm toggling an ".open" class that changes the max-height to 30000px onClick in Javascript.
I'm trying to add a CSS transition to make this more fluid via transition: 1s; to the container class. The resulting effect is clunky and seems wrong: the div will collapse instantly (no ease-in/out) after a timeout equal to the 1s in the css element. I.e. if I add transition: 5s;, nothing will happen on the page until 5s, then the div will snap closed.
Hope this makes sense, thank you!
-- Sharing code for clarity:
html:
<section class="project">
<div class="project-head">
imgs/text with flexbox/grids etc
</div>
</section>
css:
section.project {
max-height: 30px;
overflow: hidden;
transition: all 0.5s ease-in-out;
}
.project-head {
display: grid;
height: 50px;
width: 95vw;
transition: .01s;
}
.open {
max-height: 100000px !important;
}
js:
document.querySelectorAll('.project-head').forEach(project => {
project.addEventListener('click', function () {
this.parentElement.classList.toggle("open");
});
})
The code is doing what it has been asked to do - transitioning the max-height from something very big to something pretty small. It's only the last part of that that you will actually see and it looks instant because most of the transition time has been taken up with getting the huge max height down to the actual height of the div.
This snippet demonstrates a mitigation of the problem by putting the max-height to something that is not nearly so high. The outer div is set to have a different color on the smaller max height from the larger one so you can see what is happening:
document.querySelectorAll('.project-head').forEach(project => {
project.addEventListener('click', function() {
this.parentElement.classList.toggle("open");
});
})
section.project {
max-height: 30px;
overflow: hidden;
transition: all 5s ease-in-out;
background-color: rgba(255, 0, 0, 0.5);
}
.project-head {
display: grid;
height: 50px;
width: 95vw;
transition: .01s;
}
section.project.open {
max-height: 100px;
background-color: cyan;
}
<section class="project">
<div class="project-head">
imgs/text with flexbox/grids etc
</div>
</section>
Now try changing the max-height to something much larger and you will see it's transformation not being so noticable, because mostly it's off screen.
document.querySelectorAll('.project-head').forEach(project => {
project.addEventListener('click', function() {
this.parentElement.classList.toggle("open");
});
})
section.project {
max-height: 30px;
overflow: hidden;
transition: all 5s ease-in-out;
background-color: rgba(255, 0, 0, 0.5);
}
.project-head {
display: grid;
height: 50px;
width: 95vw;
transition: .01s;
}
section.project.open {
max-height: 10000px;
background-color: cyan;
}
<section class="project">
<div class="project-head">
imgs/text with flexbox/grids etc
</div>
</section>
The expansion seems to be much quicker, and the contraction much slower.
What to do in practice? As you rightly say you can't transition from/to auto so the max-height way is sort of a way round it. There seems to be no way other than choosing a sensible max height that is likely to be just more than the total height you want to expose. This is probably doable for an item in a menu as it will consist of a (very) few lines of text. However if you have lots of images this may be more difficult to judge. I suppose one way would be to get JS to calculate the heights at run time, but that then rather defeats the object of using the max height method.
Related
Result looked for:
MODE 1: when the window is large (say >465px) the TOC items is displayed to the left of the content page
MODE 2: when the window's width gets smaller than 465px, reduce the width of the TOC item using transition
MODE 3: when the window's width gets greater than 465px, increase the width of the TOC item using a transition
finally, when the window's width < 465px and that the TOC is therefore hidden as a result of the mechanism described above, show some text on top that users can click on. When they click on this text, display the TOC item as an overlay. When you click on this text again, hide the TOC item as an overlay.
How to see the problem I try to get rid of:
increase the window to a large width and then back to small width. See the transitions when you go from one to the other. This is good.
make the window small so that the "Show Table of Content" text shows up. Click on the text. The TOC is displayed as an overlay. This is good. Then click again, to HIDE the TOC as an overlay. The cyan TOC disappears, but a transition is played right after. That's the problem. I want to get rid of this transition.
This behavior doesn't make sense to me, because the media query specifies that when the window < 465px the width of the TOC is 0. So why it is reset to 150px is a mystery to me. But the most important for me is, how do I get rid of this unwanted transition when the TOC as an overlay is removed (when the the 'overlay' class is toggled (off)?
function showMenuAsOverlay(caller) {
var node = document.getElementById("toc");
node.classList.toggle('overlay');
if (node.classList.contains('overlay'))
caller.innerHTML = "Hide Table of Content";
else
caller.innerHTML = "Show Table of Content";
}
.wrapper {
display: flex;
flex-direction: row;
border: 3px solid black;
z-index: -1;
position: relative;
}
.container-left {}
#toc {
border: 1px solid green;
flex: 0 0 auto;
white-space: pre;
z-index: -1;
width: 150px;
background-color: red;
transition: width 1s ease-out;
box-sizing: border-box;
}
.container-right {
display: flex;
border: 1px solid red;
flex 1 1 auto;
max-width: 400px;
background-color: white;
z-index:-1;
box-sizing: border-box;
}
.myicon {
cursor: pointer;
visibility: hidden;
}
#media
screen and (max-width: 465px) {
#toc {
width: 0;
background-color: purple;
transition: width 1s ease-out;
}
#toc.overlay {
z-index: 999;
position: absolute;
left: 0px;
background-color: cyan;
bottom: 0;
top: 0;
width: 150px;
transition: left 1s ease-out;
}
.myicon {
visibility: visible;
}
}
<body>
<div onclick="showMenuAsOverlay(this)" class="myicon" id="myicon">Show Table of Content</div>
<div class="wrapper">
<div class="container-left" id="toc" data-state="0">This is some text in the TOC</div>
<div class="container-right">
This is some content this is some content this is some more content, and this is content again and again.
</div>
</div>
</body>
The transition is happening when <div id="toc"> loses the class overlay.
That means you go from applying this rule:
#toc.overlay {
z-index: 999;
position: absolute;
left: 0px;
background-color: cyan;
bottom: 0;
top: 0;
width: 150px;
transition: left 1s ease-out;
}
to applying this rule:
#toc {
width: 0;
background-color: purple;
transition: width 1s ease-out;
}
This makes it clear why the transition is happening. You're going from width: 150px to width: 0 with this transition applied from #toc: width 1s ease-out;
Also, you've got this applied without a media query:
#toc {
border: 1px solid green;
flex: 0 0 auto;
white-space: pre;
z-index: -1;
width: 150px;
background-color: red;
transition: width 1s ease-out;
box-sizing: border-box;
}
This means the transition applies whatever the screen size. I don't think that's what you want. Put a media query around that block to only apply when you really want it to.
With the following CSS, I am preparing my segment message to slide across the viewport:
.Segment {
position: absolute;
width: 100%;
overflow: hidden;
top: -5px;
top: 0;
outline: 1px solid orange;
}
.Segment__message {
display: inline-block;
margin-top: 15px;
left: 100%;
transform: translateX(0);
position: relative;
padding-left: 10px;
will-change: transform;
font-size: 30px;
}
If I apply the following styles dynamically, I am getting some very slight jank:
var message = document.querySelector(".Segment__message");
message.style = "transition: all 20s linear; transform: translateX(calc(-100vw - 100%))"
It is pretty subtle, but is much more noticeable on the 75" screen this will be displayed on.
Using Chrome's perf tools, I can see some FPS degradation, with it dropping to 8 FPS at one point. Is there anything I can do to smooth this out further?
https://codepen.io/anon/pen/OrOvdP
I removed the position property from the .Segment__message, and positioned it using only transform.
I've also used translate3d, which forces hardware acceleration and has improved animation performance for me in the past.
I don't see jank in Firefox, Chrome, or Safari with the code below.
var link = document.querySelector(".slide");
var message = document.querySelector(".Segment__message");
var styleStr = `transition: all 10s linear; transform: translate3d(-100%, 0, 0)`;
link.onclick = () => {
message.style = styleStr;
}
.Segment {
position: absolute;
width: 100%;
overflow: hidden;
top: 0;
outline: 1px solid orange;
}
.Segment__message {
display: inline-block;
margin-top: 15px;
transform: translate3d(100vw, 0, 0);
padding-left: 10px;
will-change: transform;
font-size: 30px;
}
.Segment__message::after {
content: "/";
color: blue;
display: block;
float: right;
padding-left: 15px;
}
.slide {
display: block;
margin-top: 50px;
}
<div class="Segment">
<div class="Segment__message">I am a message</div>
</div>
<a class="slide" href="#">Slide left</a>
You could do some enhancements to make sure your message will be drawn on a new, separate layer, like:
.Segment {
// ...
perspective: 600px;
z-index:2;
}
.Segment__message {
// ...
z-index:3;
will-change: transform;
transform-style: preserve-3d;
font-size: 30px;
}
But there is one little nasty trick that you can do along with will-change property, if you will apply some really small delay (like 0.1s) your animation will be prerendered before it fires, thus should be smoother:
message.style = "transition: all 10s linear .1s; transform: translateX(calc(-100vw - 100%))"
On first view, it could be the calc() section with vw and %. This mix caused sometimes trouble in my projects, for you get non-integers, which will be rounded automatically by the browser. So I changed the 100% to 100vw in your codepen. The result was a much smoother animation - at least in Chrome.
In addition to using translate3d instead of translateX as pointed out by #sol, I was able to improve the performance by using position: absolute and a fixed width for .Segment__message (plus a fixed height for the .Segment).
On my machine the performance degradation is very minor (even with 6x CPU slowdown) so it was difficult to test accurately, however my guess is that since an item is positioned using position: relative; (or position: static as per #sol's example) then it might cause some style recalculations as the item's (and the adjacent DOM element - in this cause a pseudo element) position shifts within it's parent container.
https://codepen.io/anon/pen/XoZRwr
I'd like to have it when I click an image that it centers in the viewport (like a lightbox effect).
I've set up a pen here http://codepen.io/emilychews/pen/OpXKGd and tried work out the best way to do this but I seem to have hit a wall.
I've included multiple elements in the demo because I'd like it so it uses the window as it's centering container, not just the parent element. I'll be using this on a wordpress site so saying just add a wrapper isn't viable for me.
Also if you look at the demo, at the moment the elements scale up smoothly and i'd like to have it align centrally in the window object as part of the transition when it scales up.
I appreciate this may only be possible with JS / jQuery and i have included some in my example.
My code for quick reference is:
HTML
<div class="wrapper">
<div class="holder image1">Image 1</div>
<div class="holder image2">Image 2</div>
<div class="holder image3">Image 3</div>
<div class="holder image4">Image 4</div>
<div class="holder image5">Image 5</div>
</div>
CSS:
.holder {
width: 20vw;
height: 400px;
background: red;
position: relative;
display: inline-block;
margin-bottom: 5px;
transition: all .75s ease-out;
}
// ======== THIS IS THE CLASS THAT IS ADDED WITH JQUERY
.fullsize {
background: blue;
z-index: 2;
position: relative;
transform: scale(1.75);
transform-origin: center center;
transition: all .75s ease-out;
}
jQuery:
$(document).ready (function(){
$('.holder').click(function() {
$( this ).toggleClass('fullsize');
$( this ).css('z-index', '+=1');
});
});
Any help / solution would be amazing.
Emily :)
I believe what you're looking for is to set a left property on the full size image. Note that you will also need to use position: absolute in order to offset each element by the same amount (centralising them).
.fullsize {
position: absolute;
left: 40vw;
}
I've created an updated CodePen showcasing this here.
Note that you also may want to give them a higher z-index, as the .fullsize elements are sometimes obscured behind the regular images.
Hope this helps! :)
Try this. It's a little jumpy and needs some fiddling, but it gives you what you want.
Change .holder to:
.holder {
width: 20vw;
height: 100px;
background: red;
position: static;
display: inline-block;
margin-bottom: 5px;
transition: all .75s ease-out;
z-index:0;
transform-origin: top left;
}
Change .fullsize to:
.holder.fullsize {
background: blue;
z-index: 200;
transform: scale(1.75);
transition: all .75s ease-out;
position:absolute;
}
and change your JQuery to
$(document).ready (function(){
$('.holder').click(function() {
var $scaleFactor = 1.75;
$( this ).toggleClass('fullsize');
var $winWidth = $(window).width();
var $myWidth = $(this).width();
var $newWidth = $myWidth*$scaleFactor;
var $left = $winWidth/2-$newWidth/2;
$(".holder").text($left);
$(this).animate({
left:$left+"px"
},200);
});
});
Setting the scaling in the JQuery instead might give you a smoother transition.
One quick question.If I set a div to display: none, the following Elements in the DOM will jump up on the place where the div was.How can I animate that jump, that it just moves gently up?Thnaks in advance for each help!
You cannot. Instead of display: none; make it with height: 0; and transition: height .5s; overflow: hidden;
Then you will have slightly move of other divs next to others. :)
If you wanna, I can made a simple codepen.
Use slideUp() instead of hide(); if you use jquery
$('.hidden').slideUp();
Just animate it. jQuery has many ways of doing it, e.g:
$('#myDiv').hide(200); // 200ms
To avoid to keep an hidden element with a width that could interfere with other elements on the page, you should simply:
$('yourElement').slideUp();
That way you will make the yourElement height animate and, finally, get it hidden actually removing it from the elements flow.
Edit:
Thanks A. Wolff for the comment. Removed un-needed .hide() function after .slideUp().
Got even a better solution for you. You can show and hide the blue box with every time you click on the pink box.
$('.pinkBox').click(function() {
$('.pinkBox').addClass('show');
$('.blueBox').addClass('displayed');
});
$('.blueBox').click(function() {
$('.pinkBox').removeClass('show');
setTimeout(function() {
$('.blueBox').removeClass('displayed');
}, 300);
});
.blueBox {
height: 100px;
width: 100px;
background-color: blue;
transition: all .3s ease;
position: absolute;
display: none;
}
.pinkBox {
height: 100px;
width: 100px;
background-color: pink;
transition: all .3s ease;
position: absolute;
}
.show {
transform: translateY(100px);
}
.displayed {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='blueBox'>
</div>
<div class='pinkBox'>
</div>
Could someone have a look at my code. what it's suppose to do is animate the img tags using fadeIn and fadeOut but it only fades out the first img and doesn't fade in the second img. I think my css could be wrong and that's why the second image isn't showing Im not getting any errors
its an image on top of another image
jQuery
$(document).ready(function() {
$('.social-media a').on('mouseenter', function(e) {
$(this).find("img:nth-child(2)").fadeIn();
$(this).find("img:nth-child(1)").fadeOut()
});
})
HTML
<div class="social-media">
<a title="Share On Twitter" href="#">
<img alt="" src="images/icon_twitter.png" />
<img class="test" alt="" src="images/icon_twitter_active.png" />
</a>
</div>
CSS
.social-media {
padding-top: 20px;
width: 166px;
margin: 0 auto 10px auto;
}
.social-media a {
position: relative;
width: 55px;
height: 51px;
}
.social-media a img:nth-child(1) {
opacity: 1;
}
.social-media a img:nth-child(2) {
position: absolute;
left: 0; top: -33px;
opacity: 0;
z-index: 2;
}
Instead of hiding the second <img> element with zero opacity, you should use display: none instead:
.social-media a img:nth-child(2) {
position: absolute;
left: 0; top: -33px;
display: none;
z-index: 2;
}
http://jsfiddle.net/8vH4E/
However, I would strongly recommend using a simple CSS image sprite to achieve this effect, which doesn't require JS.
Update: Since OP asked if it is possible to do with CSS, I have modified the Fiddle to exclude the use of JS and simply rely on the use of CSS and pseudo-elements: http://jsfiddle.net/8vH4E/2/
.social-media a {
display: block;
position: relative;
width: 100px;
height: 100px;
background-image: url(http://placehold.it/200x200);
background-size: cover;
}
.social-media a::before {
background-image: url(http://placehold.it/200x200/4a7298/eeeeee);
background-size: cover;
content: '';
display: block;
opacity: 0;
pointer-events: none;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
-webkit-transition: all .25s ease-in-out;
transition: all .25s ease-in-out;
}
.social-media a:hover::before {
opacity: 1;
}
My strategy is rather simple:
Use background images instead. For sizing, I have used cover but you are free to use any sizing (absolute pixel/point sizes, relative percentage sizes or dynamically-computed sizes like cover, contain)
For the hover state, use an absolutely-positioned pseudo element that covers the entire <a> (by positioning it absolutely and with zero offset from all four directions). We don't need pointer events on the pseudo element, so we set it to pointer-events: none
When the <a> element is hovered on (targeted with the :hover selector), we toggle the opacity of the pseudo-element from 0 to 1. We declare the transition property on the pseudo-element to allow for smooth, browser-computed and JS-agnostic transition.
the sprite is good but does not give smooth fading animation (think that was the main reason, KDM, wasn't it?).
So let's fix existing code:
as the fadeOut() turns the element to the display: none; state, as the fadeIn() starts working when the element is display: none;. So let's turn the 2nd image in display: none; first;
We can omit the opacity at all for both images (relying on 1.0 as default); $.fadeIn/Out() use the opacity from the CSS as the start/end point of the animation. Of course you can set the opacity explicitly for each image if it's designed in such way;
display: inlibe-block; for the <a> is a good point because it contains inline elements which possibly can disappear (display: none;); that causes the the whole <a> disappearing and the mouseleave event firing with unexpected UI bugs.
Enjoy http://jsfiddle.net/8vH4E/1/ and thanks to Terry for the fiddle :)