Display:inline-block before transition with pure Javascript - javascript

I am trying to animate a hidden menu box to appear and slide down when the mouse is over the button. It works properly, but when adding the transition display:inline-block -> display:none -> display:inline-block the transition disappears.
I already saw a few posts with solutions in jQuery, but none is pure JavaScript, if not for one using EventListener (that I was not able to apply to my case).
I tried to separate the JS in two functions, one to show the block, and and one to change the class. It did not work. One way would be to add something like an EventListener that would call a function after the display property is changed (just adding it in JS seems not sufficient). Any help is appreciated.
Here the code:
function overbutton(divname) {
element = document.getElementById(divname);
if (element.className == "menu_hidden menu_about") {
//element.style.display ="inline-block";
element.className = "menu_shown menu_about";
} else {
//element.style.display ="none";
element.className = "menu_hidden menu_about";
}
}
function onmenu(divname) {
//element.style.display ="inline-block";
element = document.getElementById(divname);
element.className = "menu_shown menu_about";
}
function outmenu(divname) {
//element.style.display ="none";
element = document.getElementById(divname);
element.className = "menu_hidden menu_about";
}
.menu_about {
background-color: white;
height: 100px;
width: 100px;
top: 20px;
left: 50px;
border-radius: 10px;
box-shadow: 0px 0px 10px #2B2B2B40;
-webkit-box-shadow: 0px 0px 10px #2B2B2B40;
-moz-box-shadow: 0px 0px 10px #2B2B2B40;
position: absolute;
padding: 20px;
}
.menu_hidden {
-webkit-transform: translate(0%, -5px);
transform: translate(0%, -5px);
transition: transform 0.3s;
-webkit-transition: -webkit-transform 0.3s;
}
.menu_shown {
-webkit-transform: translate(0%, 5px);
transform: translate(0%, 10px);
transition: transform 0.3s;
-webkit-transition: -webkit-transform 0.3s;
}
<div style="padding-left:100px">
<button class="button-menu" onmouseover="overbutton('about_menu_div')" onmouseout="overbutton('about_menu_div')">
menu Button
</button>
<!-- add style="display:none;"-->
<div id="about_menu_div" class="menu_hidden menu_about" onmouseover="onmenu('about_menu_div')" onmouseout="outmenu('about_menu_div')">
Menu
</div>
</div>
JSfiddle: https://jsfiddle.net/g0ktx574/

No need for any JavaScript in this case. You can solve this by using CSS, and more specifically, the :hover pseudo-class and the + adjacent sibling selector.
Also, the display property cannot be animated. Instead use opacity in combination with visibility.
.wrapper {
position: relative;
}
.menu_about {
position: absolute;
top: 100%;
left: 0;
background-color: white;
height: 100px;
width: 100px;
padding: 20px;
border-radius: 10px;
box-shadow: 0px 0px 10px #2B2B2B40;
opacity: 0;
visibility: hidden;
transition-property: opacity visibility transform;
transition: 0.3s;
}
.button-menu:hover + .menu_about,
.menu_about:hover {
opacity: 1;
visibility: visible;
transform: translate(0%, 5px);
}
<div class="wrapper">
<button class="button-menu">
menu Button
</button>
<div class="menu_about">
Menu
</div>
</div>

With reference to the solution posted here Transition translate animation immediately after display block, I have modified your code accordingly. One thing to keep in mind is that you cannot animate display property. So the below solution makes use of setTimeout function to achieve the start and end point of the transform property. I have also removed some css as well. You can have a look. You can adjust the transition timing, and transform property according to your need.
function overbutton(divname) {
element = document.getElementById(divname);
if (element.className == "menu_hidden menu_about") {
element.style.display ="inline-block";
element.className = "menu_shown menu_about";
element.style.transform = "translate(0%, 10px)";
element.style.transition = "transform .7s";
setTimeout(function() {
element.style.transform = 'translate(0%, 30px)';
element.style.transition = "transform .7s";
}, 0)
}else{
element.style.display ="none";
element.className = "menu_hidden menu_about";
}
}
function onmenu(divname) {
element.style.display ="inline-block";
element = document.getElementById(divname);
element.className = "menu_shown menu_about";
}
function outmenu(divname) {
element.style.display ="none";
element = document.getElementById(divname);
element.className = "menu_hidden menu_about";
}
.menu_about{
background-color:white;
height:100px;
width:100px;
top:20px;
left:50px;
border-radius: 10px;
box-shadow: 0px 0px 10px #2B2B2B40;
-webkit-box-shadow: 0px 0px 10px #2B2B2B40;
-moz-box-shadow: 0px 0px 10px #2B2B2B40;
position: absolute;
padding: 20px;
}
/* hide menu initially */
.menu_hidden {
display: none;
}
<div style="padding-left:100px">
<button class="button-menu" onmouseover="overbutton('about_menu_div')" onmouseout="overbutton('about_menu_div')">
menu Button
</button>
<!-- add style="display:none;"-->
<div id="about_menu_div" class="menu_hidden menu_about" onmouseover="onmenu('about_menu_div')" onmouseout="outmenu('about_menu_div')">
Menu
</div>
</div>

Related

JQuery add class after another one was added and is visible

I have a button. I want to add to this button class: space and after this class was added and is visible in browser I want to add another class: spinner
I have tried with:
$("button").on("click", function(){
$(this).addClass("space");
$(this).addClass("spinner");
}
CSS:
.spacer{
transition: .3s !important;
padding-right: 3.1rem !important;
}
.spinner{
border: 5px solid #f3f3f3;
border-radius: 50%;
border-top: 5px solid #3498db;
width: 10px;
height: 10px;
animation: spin 2s linear infinite;
}
But it, obviously, doesn't work. Why?
Can a class be added to an element only after a class was added and has made its effect?
you could add the second class with a short timeout.this gives you also the possibility to add some animations if needed.
window.setTimeout(function() {
button.addClass("spinner");
},500);
promises will work to
You can add event listener to check if the transition is completed.
Consider the code below:
var el = document.getElementById('someelement');
debugger;
function transitionCallback(){
var t;
var transitions = {
'transition':'transitionend',
'OTransition':'oTransitionEnd',
'MozTransition':'transitionend',
'WebkitTransition':'webkitTransitionEnd'
}
for(t in transitions){
if( el.style[t] !== undefined ){
return transitions[t];
}
}
}
/* Listen for transition */
var transitionEvent = transitionCallback();
transitionEvent && el.addEventListener(transitionEvent, function() {
console.log('Transition complete.');
});
/*transition example is from w3schools*/
#someelement {
width: 100px;
height: 100px;
background: red;
transition: width 2s;
-webkit-transition: width 2s; /* Safari 3.1 to 6.0 */
}
#someelement:hover {
width: 300px;
}
<html>
<head>
</head>
<body>
<div id="someelement"></div>
</body>
</html>
Use animation-delay to set a delay before it starts to run. I set it to a big number just so you can see the delay.
document.querySelector('button')
.addEventListener('click', event => {
event.preventDefault()
const classList = event.target.classList
classList.toggle('spacer')
classList.toggle('spinner')
})
.spacer{
transition: .3s !important;
padding-right: 3.1rem !important;
}
.spinner{
border: 5px solid #f3f3f3;
border-radius: 50%;
border-top: 5px solid #3498db;
width: 10px;
height: 10px;
animation: spin 2s linear infinite;
animation-delay: 2s;
}
#keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
<button></button>

Keyframe animation is lagging when there is a lot of content on the page

I want to add animated circle, when I'm clicking to some div.
So I'm handling clicks to the element and adding zero-sized div to the click coordinates. Also, I added keyframe animation for this div to increase its width and height. Finally, here is animation-div styles:
div.clickEffect {
position: fixed;
box-sizing: border-box;
border-radius: 50%;
z-index: 99999;
}
Adding this div in react:
var d = document.createElement('div');
d.className = 'clickEffect';
d.style.top = `${y}px`;
d.style.left = `${x}px`;
const animation = keyframes`
0% {
opacity: 1;
width: ${minSize}px;
height: ${minSize}px;
transform: translate(-50%, -50%);
box-shadow: 0 0 7px 0px #ff00ff;
}
100% {
opacity: 0.4;
width: ${minSize + radius}px;
height: ${minSize + radius}px;
transform: translate(-50%, -50%);
box-shadow: 0 0 25px 0px #ff00ff;
}
`;
d.style.animation = `${animation} 1s ease-out`;
This amimation is smooth when there is not much content on the page. But it's lagging when there is a lot of content on the page. Is there any way to make this anmation always smooth?

Trigger CSS Animations in JavaScript

I don't know how to use JQuery, so I need a method which could trigger an animation using JavaScript only.
I need to call/trigger CSS Animation when the user scrolls the page.
function start() {
document.getElementById('logo').style.animation = "anim 2s 2s forward";
document.getElementById('earthlogo').style.animation = "anim2 2s 2s forward";
}
* {
margin: 0px;
}
#logo {
position: fixed;
top: 200px;
height: 200px;
width: 1000px;
left: 5%;
z-index: 4;
opacity: 0.8;
}
#earthlogo {
position: fixed;
top: 200px;
height: 120px;
align-self: center;
left: 5%;
margin-left: 870px;
margin-top: 60px;
z-index: 4;
opacity: 0.9;
}
#keyframes anim {
50% {
filter: blur(10px);
transform: rotate(-15deg);
box-shadow: 0px 0px 10px 3px;
}
100% {
height: 100px;
width: 500px;
left: 10px;
top: 10px;
box-shadow: 0px 0px 15px 5px rgba(0, 0, 0, 0.7);
background-color: rgba(0, 0, 1, 0.3);
opacity: 0.7;
}
}
#keyframes anim2 {
50% {
filter: blur(40px);
transform: rotate(-15deg);
}
100% {
height: 60px;
width: 60px;
left: 10px;
top: 10px;
margin-left: 435px;
margin-top: 30px;
opacity: 0.8;
}
}
#backstar {
position: fixed;
width: 100%;
z-index: 1;
}
#earth {
position: absolute;
width: 100%;
z-index: 2;
top: 300px;
}
<img src="logo.png" id="logo" onclick="start();">
<img src="earthlogo.gif" id="earthlogo" onscroll="start();">
<img src="earth.png" id="earth">
<img src="stars.jpg" id="backstar">
The simplest method to trigger CSS animations is by adding or removing a class - how to do this with pure Javascript you can read here:
How do I add a class to a given element?
If you DO use jQuery (which should really be easy to learn in basic usage) you do it simply with addClass / removeClass.
All you have to do then is set a transition to a given element like this:
.el {
width:10px;
transition: all 2s;
}
And then change its state if the element has a class:
.el.addedclass {
width:20px;
}
Note: This example was with transition. But for animations its the same: Just add the animation on the element which has a class on it.
There is a similar question here: Trigger a CSS keyframe animation via scroll
This is how you can use vanilla JavaScript to change/trigger an animation associated with an HTML element.
First, you define your animations in CSS.
#keyframes spin1 { 100% { transform:rotate(360deg); } }
#keyframes spin2 { 100% { transform:rotate(-360deg); } }
#keyframes idle { 100% {} }
Then you use javascript to switch between animations.
document.getElementById('yourElement').style.animation="spin2 4s linear infinite";
Note: 'yourElement' is the target HTML element that you wish to
animate.
For example: <div id="yourElement"> ... </div>
Adding and removing the animation class does not work in a function. The delay is simply too little. As suggested by this article you can request the browser to reflow and then add the class. The delay isn't an issue in that case. Hence, you can use this code:
element.classList.remove("animation")
element.offsetWidth
element.classList.add("animation")
The best thing is, this works everywhere. All credit goes to the article.
A more idiomatic solution is to use the Web Animations API.
Here is the example from MDN:
document.getElementById("alice").animate(
[
{ transform: 'rotate(0) translate3D(-50%, -50%, 0)', color: '#000' },
{ color: '#431236', offset: 0.3 },
{ transform: 'rotate(360deg) translate3D(-50%, -50%, 0)', color: '#000' }
], {
duration: 3000,
iterations: Infinity
}
);
OP's example:
document.getElementById('logo').animate(
[
{},
{
filter: 'blur(10px)',
transform: 'rotate(-15deg)',
box-shadow: '0px 0px 10px 3px',
},
{
height: '100px',
width: '500px',
left: '10px',
top: '10px',
box-shadow: '0px 0px 15px 5px rgba(0, 0, 0, 0.7)',
background-color: 'rgba(0, 0, 1, 0.3)',
opacity: '0.7',
},
],
{
duration: 2000,
delay: 2000,
fill: 'forwards',
},
)
At the time of writing, it's supported in all major browsers except IE.
Supported browsers
I have a similar problem.
The best answer didn’t work for me, but when I added the delay it worked.
The following is my solution.
CSS
.circle_ani1,
.circle_ani2 {
animation-duration: 1s;
animation-iteration-count: 1;
}
.circle_ani1 {
animation-name: circle1;
}
.circle_ani2 {
animation-name: circle2;
}
JS
let temp_circle1 = $('.TimeCountdown_circle1').removeClass('circle_ani1');
let temp_circle2 = $('.TimeCountdown_circle2').removeClass('circle_ani2');
window.setTimeout(function() {
temp_circle1.addClass('circle_ani1');
temp_circle2.addClass('circle_ani2');
}, 50);
Vanilla JS version
document.getElementById('logo').classList.add("anim");
document.getElementById('earthlogo').classList.add("anim2");
You could use CSS to hide the image / animation and show when the user scrolls. This would work like this:
CSS:
div {
border: 1px solid black;
width: 200px;
height: 100px;
overflow: scroll;
}
#demo{
display: none;
}
HTML:
<div id="myDIV"> </div>
<div id="demo">
<img src="earthlogo.gif" id="earthlogo" alt="Thanks for scrolling. Now you see me">
</div>
Your javascript just needs to include an eventListener to call the function which triggers the display of your animation.
JS:
document.getElementById("myDIV").addEventListener("scroll", start);
function start() {
document.getElementById('demo').style.display='block';
}
You could use animation-play-state (Mdn docs) like this
element.style.animationPlayState = "paused/running"
Code snippet:
function play() {
document.getElementById("div").style.animationPlayState = "running";
}
function pause() {
document.getElementById("div").style.animationPlayState = "paused";
}
.animation {
width: 50px;
height: 50px;
background-color: red;
position: relative;
animation-name: example;
animation-duration: 2s;
animation-play-state: paused;
animation-iteration-count: infinite;
}
#keyframes example {
0% {
background-color: red;
left: 0px;
top: 0px;
}
25% {
background-color: yellow;
left: 50px;
top: 0px;
}
50% {
background-color: blue;
left: 50px;
top: 50px;
}
75% {
background-color: green;
left: 0px;
top: 50px;
}
100% {
background-color: red;
left: 0px;
top: 0px;
}
}
<button onclick="play()">Play</button>
<button onclick="pause()">Pause</button><br><br>
<div id="div" class="animation"></div>
Here's the main code:
HTML:
<img id="myImg">
CSS:
#myImg {
//properties
animation: animate 2s linear infinite //infinite is important!
}
#keyframes animate {
//animation base
}
JS:
document.getElementById("myImg").style.webkitAnimationPlayState = "paused";
window.addEventListener("scroll", function() {
document.getElementById("myImg").style.webkitAnimationPlayState = "running";
setTimeout(function() {
document.getElementById("myImg").style.webkitAnimationPlayState = "paused";
}, 2000);
});
If you want Animations i recommend you create a CSS class which you toggle on a Condition whit JS:
CSS
.animation {
animation: anim 2s ease infinite;
transition: .2s
}
JS
// Select your Element
$element.document.querySelector(".yourElement");
$element.addEventListner('click', () => {
$element.classList.toggle("animation")
})

Hard transition of other properties when display property is involved

I want to fade between two differently sized elements within a container overlaying each other. The first element should be faded out, then the container resized and finally the other element faded in.
Here's the related snippet:
var layer1 = document.getElementById("layer1");
var layer2 = document.getElementById("layer2");
function switchLayers() {
layer1.addEventListener("transitionend", function() {
layer2.classList.add("fadein");
});
layer1.classList.add("fadeout");
}
#container {
position: relative;
background-color: yellow;
padding: 10px;
overflow: hidden;
}
.layer {
position: relative;
width: 400px;
}
#layer1 {
height: 100px;
float: left;
background-color: blue;
}
#layer2 {
height: 150px;
background-color: red;
display: none;
opacity: 0;
}
#layer1.fadeout {
opacity: 0;
transition: opacity 1s ease-out;
}
#layer2.fadein {
display: block;
opacity: 1;
transition: opacity 1s ease-out;
}
<button onclick="switchLayers()">Switch layers</button>
<div id="container">
<div id="layer1" class="layer"></div>
<div id="layer2" class="layer"></div>
</div>
When the second layer's display property is set to block it works as expected, i.e. the opacity is changed from 0 to 1 within a second. Though if it's set to none, the transition suddenly is discrete.
I've tried to set all within the transition value to transition all properties and also tried to include the display property in the transition like this:
transition: display 0s, opacity 1s ease-out;
Though without success. Note that because the container should resize to the size of the currently displayed layer, the visibility property can't be used (as it hides the element but still lets it occupy the space).
How to made this work?
Try using the visibility property instead of display.
For more information regarding the state changes in visibility and display, refer article.
For transitioning the parent height, you have to manually change the height property of the #container. Using display: block & display: none will never transition the parent.
Refer code:
var layer1 = document.getElementById("layer1");
var layer2 = document.getElementById("layer2");
function switchLayers() {
layer1.addEventListener("transitionend", function() {
layer2.classList.add("fadein");
document.getElementById("container").style.height = "170px";
});
layer1.classList.add("fadeout");
}
#container {
position: relative;
background-color: yellow;
padding: 10px;
height: 100px;
overflow: hidden;
transition: all 0.5s;
}
.layer {
position: relative;
width: 400px;
}
#layer1 {
height: 100px;
float: left;
background-color: blue;
}
#layer2 {
height: 150px;
background-color: red;
visibility: none;
opacity: 0;
}
#layer1.fadeout {
visibility: none;
opacity: 0;
transition: all 1s ease-out;
}
#layer2.fadein {
visibility: visible;
opacity: 1;
transition: all 1s ease-out;
}
<button onclick="switchLayers()">Switch layers</button>
<div id="container">
<div id="layer1" class="layer"></div>
<div id="layer2" class="layer"></div>
</div>
There is no straightforward way. Transitions do not work on display, nor do they work on auto height. So, visibility is a good bet.
Note that because the container should resize to the size of the
currently displayed layer, the visibility property can't be used (as
it hides the element but still lets it occupy the space).
Then, you will need to hack it out. You can make use of min-height. Give a faux min-height to your container, and then apply the height of your layer2 to it once the transition ends. Also, because display on layer2 will block the transition, you need to separate out the classes for display and opacity and space out their application using a zero timeout in between.
Here is a crude idea:
var layer1 = document.getElementById("layer1"),
layer2 = document.getElementById("layer2"),
container = document.getElementById("container"),
h = window.getComputedStyle(layer2).getPropertyValue("height");
container.addEventListener("transitionend", function(e) {
if (e.target.id === 'layer1') {
// apply layer2 height to container min-height
container.style.minHeight = h;
}
if (e.target.id === 'container') {
// First show the layer2
layer2.classList.add("show");
// Then a dummy pause to fadein
setTimeout(function(){
layer2.classList.add("fadein");
}, 0);
}
}, false);
function switchLayers() {
layer1.classList.add("fadeout");
}
#container {
position: relative;
background-color: yellow;
padding: 10px; overflow: hidden;
min-height: 1px; /* faux min-height */
transition: min-height 1s linear;
}
.layer { position: relative; width: 400px; }
#layer1 {
height: 100px; float: left;
background-color: blue;
transition: all 1s linear;
}
#layer2 {
height: 150px; background-color: red;
display: none; opacity: 0;
transition: opacity 1s linear;
}
#layer1.fadeout { opacity: 0; }
#layer2.show { display: block; } /* Separate out display */
#layer2.fadein { opacity: 1; } /* Separate out opacity */
<button onclick="switchLayers()">Switch layers</button>
<div id="container">
<div id="layer1" class="layer"></div>
<div id="layer2" class="layer"></div>
</div>

Reliable way to trigger one-way CSS transition?

I have designed a transition that changes instantly background color, then slowly recovers to original color:
body, html {margin: 0px;padding: 0px;}
div {
padding: 3px;
margin: 1px 0 1px 0;
border: 1px solid black;
background-color: #3377FF;
transition: background-color .9s ease-in;
}
div:hover {
background-color: #33FF11;
transition-duration: 0s;
}
<div> Hello</div>
<div> Hello</div>
This is designed for row in a table to notify user of a change. I'd like to trigger effect this programmatically, but by using the actual CSS.
Row.prototype.blink = function() {
... ?
}
I tried to use setTimeout to add and remove class name from the nodes:
$("div").on("click", function() {
$(this).addClass("updated");
var _this = this;
setTimeout(function(){$(_this).removeClass("updated");}, 30);
});
body, html {margin: 0px;padding: 0px;}
div {
padding: 3px;
margin: 1px 0 1px 0;
border: 1px solid black;
background-color: #3377FF;
transition: background-color .9s ease-in;
}
div.updated {
background-color: #33FF11;
transition-duration: 0s;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Click div to activate the blink effect:</p>
<div> Hello</div>
<div> Hello</div>
I also tried to use the transitioned event, but the event doesn't ever trigger:
$("div").on("click", function() {
$(this).one("transitionend", function(){$(this).removeClass("updated");}); // never happens
$(this).addClass("updated");
});
I don't like the setTimeout method, can't you think of better trick to trigger the blink effect?
The reason the transitionend event isn't being triggered is because transition-duration is set to 0s. In other words, the transition never begins until the class is removed (but the class isn't removed because the event isn't fired).
It really sounds like an animation would be better suited for this. Just listen to the animationend event and remove the class in the callback when the animation ends:
$('div').on('click', function() {
$(this).addClass('updated').one('animationend', function() {
$(this).removeClass('updated');
});
});
body, html {margin: 0px;padding: 0px;}
div {
padding: 3px;
margin: 1px 0 1px 0;
border: 1px solid black;
background-color: #3377FF;
cursor: pointer;
}
div.updated {
animation: updatedFade .9s ease-in forwards;
}
#keyframes updatedFade {
0% { background-color: #33FF11; }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>Hello</div>
<div>Hello</div>
As a work-around to your initial solution, you could change the transition-duration to something extremely short, like 1ms, so that the initial transition is actually started, thereby allowing the transitionend event to be fired:
$("div").on("click", function() {
$(this).addClass("updated").one("transitionend", function() {
$(this).removeClass("updated");
});
});
body, html {margin: 0px;padding: 0px;}
div {
padding: 3px;
margin: 1px 0 1px 0;
border: 1px solid black;
background-color: #3377FF;
transition: background-color .9s ease-in;
cursor: pointer;
}
div.updated {
background-color: #33FF11;
transition-duration: 1ms;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>Hello</div>
<div>Hello</div>

Categories