Animate the overflow property
Solution 1
The solution is to use AnimationEvent listeners. Here's my raw implementation:
CSS
• 2 animations (open, close)
• 2 classes (opened, closed)
• 2 states (overflow hidden/visible)
opened and closed are always toggled at animationstart, while hidden/visible states are differently worked out on animationend.
Note: you'll see a #main-menu element: it's an UL with transitioned translations on y-axis, because the whole thing is a menu slide-down/up effect.
@include keyframes(open) {
0% {
height:0;
}
100% {
height:$main_menu_height;
}
}
@include keyframes(close) {
0% {
height:$main_menu_height;
}
100% {
height:0;
}
}
#main-menu-box{
overflow-y:hidden;
height:0; // js
&.closed{
@include animation('close 200ms ease-out 0s');
}
&.opened{
@include animation('open 200ms ease-out 0s 1');
//#main-menu{
// @include translate(0, 0);
//}
}
&.overflow-hidden{
overflow-y:hidden;
}
&.overflow-visible{
overflow-y:visible;
}
}
JS
• hamburger is a simple on/off button
• for now I had to use both jquery and vanilla selectors..
function poly_event_listener(element, type, callback) {
var pfx = ['webkit', 'moz', 'MS', 'o', ''];
for(var i=0; i<pfx.length; i++) {
if (pfx[i] === '') type = type.toLowerCase();
element.addEventListener(pfx[i]+type, callback, false);
}
}
var hamburger = $('header .hamburger');
var main_menu_box = $('#main-menu-box');
var main_menu_box_std = document.querySelector('#main-menu-box');
var init_menu = true;
hamburger.click(function(){
if(init_menu){
main_menu_box.addClass('opened');
init_menu = false;
return;
}
main_menu_box.toggleClass('opened closed');
});
poly_event_listener(main_menu_box_std,'AnimationStart',function(e){
main_menu_box.addClass('overflow-hidden');
main_menu_box.removeClass('overflow-visible');
});
poly_event_listener(main_menu_box_std,'AnimationEnd',function(e){
// in all the other cases I want hidden:true, visible:false
// if class == closed, since animationend comes after animationstart, the state will already be hidden:true, visible:false
// so non need to check for 'closed' here
if(main_menu_box.hasClass('opened')){
main_menu_box.addClass('overflow-visible');
main_menu_box.removeClass('overflow-hidden');
}
});
This works for me.
Solution 2
In most modern browsers, clip-path
(prefixed with -webkit-
in Safari) is an animatable property which can sometimes be used as an alternative to overflow
.
Given the original example, the closest way to use clip-path
to emulate flipping overflow
on the last frame would look something like this:
@include keyframes(open) {
0% {
height: 0;
clip-path: inset(0);
}
99.99999% {
clip-path: inset(0);
}
100% {
height: $main_menu_height;
clip-path: inset(-100vh -100vw);
}
}
#main-menu-box {
clip-path: inset(0);
height: 0;
&.opened {
@include animation('open 200ms ease-out 0s 1 normal forwards');
}
}
Since this animation is a simple linear animation it could even be replaced with a regular CSS transition:
#main-menu-box {
clip-path: inset(0);
height: 0;
transition: clip-path 0s ease-out, height 200ms ease-out;
&.opened {
height: $main_menu_height;
clip-path: inset(-100vh -100vw);
transition-delay: 200ms, 0s;
}
}
However, there are two notable differences between clip-path
and overflow
which make it not suitable in all cases.
First, unlike an element with overflow: visible
, an element with any clip-path
has a stacking context, so the way that overflowing content is rendered will be different—though in the case of a menu with overflowing content, you probably wanted this anyway!
Second, unlike overflow
which only clips children, clip-path
clips the entire element. This means if you have borders, box shadows, etc., those are going to get clipped, too. Depending upon the design of the container, this can sometimes be worked around by applying the clip to a child wrapper element instead.
Solution 3
The overflow property can't be animated with CSS. See the W3 specs here : overflow properties
Animatable: no
You can also see a list of animatable properties on MDN : Animated properties
Workaround :
If you need to change the overflow property, you can use JS. Here is an example with the jQuery library.
The overflow property changes with a class toggle. It is fired when the div is clicked :
$('#w').click(function() {
$(this).toggleClass('open');
});
#w {
width: 100px;
height: 100px;
border: 1px solid red;
overflow: hidden;
}
#w.open {
overflow: visible;
}
#w div {
height: 200px;
width: 50px;
background: gold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="w">
<div></div>
</div>
Luca Reghellin
I'm a full stack web developer since 1999. I'm an advanced html, css, javascript, php, wordpress and prestashop programmer and if needed, I can fully manage all projects sides, including design and art direction, and coordinate all the assets involved.
Updated on April 25, 2020Comments
-
Luca Reghellin about 4 years
I need to animate height, and set
overflow: hidden
for the first keyframe, andoverflow: visible
(and keep it) for the last one.I'm trying this, but at the end,
overflow
is stillhidden
.How can I solve this issue?
The 2 includes are merely SCSS polifill mixins.
@include keyframes(open) { 0% { height: 0; overflow: hidden; } 100% { height: $main_menu_height; overflow: visible; } } #main-menu-box { overflow: hidden; height: 0; &.opened{ @include animation('open 200ms ease-out 0s 1 normal forwards'); } }