So this is my box
<div class="mybox"></div>
And i have a animation
.mybox{
animation:right 5s;
animation-fill-mode:forwards;
padding:50px;
width:0px;
height:0px;
display:block;
background-color:black;
}
#keyframes
right{from{left:0px;}to{left:300px
;}}
#keyframes
left{from{right:0px;}to{right:300p
x;}}
And i have two button
<button onclick="leftFunction">left</button>
<button onclick="rightFunction">right</button>
Then if i click left it will go left while it is going left then i click right i want to stop the left animation and go right but the right animation must start on current position and not teleport and go right.
Please help :(
Im new to stack overflow
Here is the js
function leftFunction() {
document.getElementById("myDIV").style.animation
= "left 4s";
}
function rightFunction() {
document.getElementById("myDIV").style.
animation = "right 4s";
}
There were a number of issues I found with the code that you posted, but you were on the right track.
Your DOM method was looking for an element with an ID of myDIV which does not exist, I changed it to document.querySelector(".mybox") since that is the class name of your div.
You weren't invoking the function call on your onclick handler, not sure you were getting anything to happen at all... Add the () invoke it onclick.
Your code layout was not following normal spacing conventions, but it is difficult to write code into stack overflow, so I understand. I made the changes I saw necessary.
You were setting position values to your div, but there was no position property declared. Position is static by default which doesn't respond to left or right. I used relative as that will keep the div in the document flow.
Changing left on one animation and right on another will maintain the previous left or right values, respectively and could cause your div to shrink or grow unexpectedly. I changed it to only affect the left property.
And now for the solution
Change your keyframes to only update the left property. Call the function from onclick and pass in the event object. This will allow you to have just one function to handle the change in animation, and can take the event target's innerHTML to set the name of the animation desired.
Make use of CSS custom properties, so that value can be mutated in javascript, and make your from properties start at the custom property. In this case --cur-pos.
When the function is called, get the div element, and find it's computed left value with document.getComputedStyle(), then update the custom property --cur-pos with that value. That way the animation always starts where the div is currently positioned.
Et voila! You should be good to go. You might have to tweak the starting --cur-pos value to have the div start where you want, and also update the to values to end where you want, just make sure to choose either left or right for updating horizontal position, not both.
function changeAnimation(e) {
const box = document.querySelector(".mybox")
const pos = window.getComputedStyle(box).left
box.style.setProperty("--cur-pos", pos)
box.style.animationName = e.target.innerHTML
}
.mybox {
--cur-pos: 150px;
width: 100px;
height: 100px;
background-color: black;
position: relative;
animation: right 4s forwards;
}
#keyframes right {
from { left: var(--cur-pos); }
to { left: 300px; }
}
#keyframes left {
from { left: var(--cur-pos); }
to { left: 0px; }
}
<div class="mybox"></div>
<button onclick="changeAnimation(event)">left</button>
<button onclick="changeAnimation(event)">right</button>
Related
I have one div, with buttons that trigger JS functions to add/remove CSS classes to the div. I understand this is a common way of doing simple animations ("transitions"). Try pressing the first two buttons, one after another. The red div will teleport up and become semi transparent, then fade in and slide back down. It always works.
The third button simply executes the same code as the first two buttons, but from one function. I expected it to have the same visible effect: the div immediately would jump up from the translate and be transparent, then during the course of 1 second it would slide back to its normal spot and fade in to full opacity. But it does not - the button has no visible effect.
Why? How can I make this work?
Here is the JS, see the whole thing at the codepen link.
const div = document.getElementById('red-box')
function translateUp() {
div.classList.remove('no-translate');
div.classList.add('translate-up');
}
function noTranslate() {
div.classList.remove('translate-up');
div.classList.add('no-translate');
}
//why does this function not show any transition animation?
function both() {
translateUp();
noTranslate();
}
https://codepen.io/DMcCreepy/pen/BampPyB
No jQuery please :)
The reason is that the functions are executed immediately one after the another, with no time to see the effect. transition in CSS refers to when the class is applied (could be related to a CSS state like :hover), not to switching classes via JavaScript.
To add the 1 second delay in the JS, you can use window.setTimeout:
function both() {
translateUp();
window.setTimeout(noTranslate, 1000);
}
You are relying on the div getting rerendered and all its properties being recomputed before switching the class again with the second function. I am not sure entirely what order things are done in under the hood, but I am sure you cannot rely on constructions like this working in general. It is true that switching classes is a common way to do animations and maybe it is possible to work out a solution like that, but I have provided a workaround using a CSS animation. You can read more about animations here https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations
const div = document.getElementById('red-box')
div.addEventListener('animationend', reset);
function reset() {
div.classList.remove('dropin');
}
function both() {
div.classList.add('dropin');
}
button {
display: block;
}
.box {
height: 70px;
width: 80px;
font-size: 50px;
background-color: red;
animation-duration: 1s;
animation-fill-mode: forwards;
animation-name: none;
}
.box.dropin {
animation-name: dropin;
}
#keyframes dropin {
from {
transform: translate(0, -40px);
opacity: 0.1;
}
to {
transform: translate(0, 0);
opacity: 1;
}
}
<div id="red-box" class="box">div</div>
<button onclick='both()'>Do Both</button>
I am a bit new to CSS and I know there are more topics like this around. But none seem to be the solution for my problem. So after 2 hours of trying all sort of hidden, transition, display etc and with the risk of posting a duplicate here is my question.
How do I make this modal show up smooth using CSS and Javascript?
The CSS
/* The Modal (background) */
.modalestate {
visibility: hidden; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 1; /* Sit on top */
left: 25;
top: 15;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
transition: all ease 1s;
}
/* Modal Content/Box */
.modalestate-content {
background-color: rgba(0,0,0,0.0);
margin: 12% auto; /* 15% from the top and centered */
padding: 22px;
float:left;
width: 550px; /* Could be more or less, depending on screen size */
transition: all ease 1s;
}
/* The Close Button */
.closeestate {
color: #aaa;
float: right;
font-size: 20px;
font-weight: bold;
}
.closeestate:hover,
.closeestate:focus {
color: whitesmoke;
text-decoration: none;
cursor: pointer;
}
The divs to show the modal and its content:
<div id="mymodelestate" class="modalestate">
<div class="modalestate-content">
<span class="closeestate">×</span>
<div id="responsecontainer" align="center"></div>
</div></div>
The Javascript:
<script>
// Get the modal
var modal = document.getElementById("mymodelestate");
// Get the button that opens the modal
var btn = document.getElementById("btnestate");
// Get the <span> element that closes the modal
var span = document.getElementsByClassName("closeestate")[0];
// When the user clicks on the button, open the modal
btn.onclick = function() {
modal.style.visibility = "visible";
}
// When the user clicks on <span> (x), close the modal
span.onclick = function() {
modal.style.visibility = "hidden";
}
// When the user clicks anywhere outside of the modal, close it
window.addEventListener('click', function(event) {
if (event.target == modal) {
modal.style.visibility = "hidden";
}
});</script>
Thanks in advance guys !
What kind of "transition" are you trying to do? What effect do you want to apply to your modal? It's not very clear, but I'm going to guess you are trying to have the modal fade in and fade out slowly as the user clicks the buttons. This assumption comes from the fact you are using visiblity:hidden/visible.
If you are trying to have a fade-in/fade-out effect, you need to set the opacity to 0 instead:
opacity: 0;
And when you want to display it, you set opacity to 1.
However, you are going to run into many other issues. Though, I will say this will be a good learning exercise to familiarize yourself with how elements in HTML work and their interactions with CSS.
EDIT: As promised from my comments, I'll provide an example here and some advice.
First, you have the BODY. This BODY, is literally like a human body. You attach elements to it (or human parts). You have this down pretty good, you even use the z-index so I won't go over this too much. I know more "advanced" devs are going to say something about the DOM blah blah it's not attached yada yada, but we're going to keep it simple here.
Next is that the elements all have a initial attribute associated to them. For example, a DIV element has inline-block as an initial attribute. P element tag has some margin and is a block element. Etc etc. You can obviously override these with CSS or add extra attributes as you have been doing. By giving an element a CLASS, you are creating your own custom element of sorts (though it is still it's base element, DIV, P, A, SPAN, etc)
Next is understanding all of these attributes. You seem to know a good deal amount of attributes so I won't go over much, but I will go over these:
Visibility - The element still exists on the BODY. It's there, it still touches everything and affects everything around it. Think of it as an invisible arm on a human body. You can still use that arm and touch things, make things move, push things away, etc... It's just literally invisible. The GPU or whatever is driving graphics will simply NOT render this element.
Opacity - Similar to visibility, but you get some more control. Opacity of 1 (or 100%) means the element is completely visible. An opacity of .5 or 50% means the element is only half seen. That means things behind it can be seen through it. The GPU is still rendering this element but you can now include transparency. 0 means it has complete transparency (basically it's invisible).
Display - This is used to set how an element behaves in your body and how it interacts with other elements. You will often see or even use "display: none" which makes it seem like the element does not exist anymore (it's still there in HTML, though). It will no longer affect anything around it.
Now, what's more important is to understand some of the more complexities of these attributes. In your case, you should know about transitions. A transition attribute allows you to modify the browser's handling of attribute manipulation on an element. In simpler terms, as you know, it lets you create effects on elements, such as fading in, fading out, movement, etc. And it comes out nice and smooth (if done correctly).
However, these attributes that are being manipulated occur instantly. Thus, as mentioned, transitions give you that control to make it smooth. HOWEVER, it is important to note that these transitions will only work when an element already has attribute values to be manipulated. For example:
.MyDivClass{
height: 32px;
width: 32px;
transition: all .3s ease;
}
.MyDivClassExtra{
height: 64px;
width: 64px;
}
The div will seem to grow larger over .3 seconds. That is because my height and width are set. If I did "NOT" set width/height or even set it to atuo, it will just happen instantly. Because there was no attribute to be manipulated! Even setting height/width to 0 will work, because at least it has an attribute to work with.
You have to sometimes set aside common sense when it comes to programming. Things like Visibility you'd assume if you turn it on and off with transition, it'll fade in nicely. However, no. Visibility is simple ON or OFF. There is nothing to transition into. With opacity, it is not just simply ON or OFF. There are 101 numbers (infinite actually, I guess...) to transition from. 0% to 100%. You can do decimals, too. The same is true with display. A little more complicated but simply put - there is only ON or OFF with display.
Some other notes on your code is that your .modelstate element is on top of everything. Your users won't be able to click anything because of this. Visibility hidden does not make it unclickable. Like I mentioned, it simply makes it invisible - it's still there affecting everything else.
http://codepen.io/justinturner/pen/VjyWJE
The linked codepen has a fixed header div.
I'm using javascript to add a menu div on the left when the hamburger icon is clicked. This is also a fixed div.
When the menu div is added, the header div seems to revert to relative positioning, and jumps to the top of the main content div. Scroll down just a hair and then click the hamburger, you'll see what I mean.
What issue am I running into here? When a user clicks the hamburger, I want the header to remain fixed and translate directly to the right like the rest of the content.
<em>too much code to paste</em>
According to the spec and other similar questions here on SO, fixed elements and translates don't "play" well together.
As a workaround you could:
1) Use transitions (eg. on the left property) instead of transform (translateX)
2) Remove the position:fixed button from the container which uses transforms
Following the first suggestions from above (using left instead of translateX), edit your code to the following and the issue should no longer persist.
.o-wrapper.has-push-left {
left: 300px;
}
.o-wrapper {
position: relative;
transition: left 0.3s;
}
#header-wrapper {
transition: left 0.3s;
}
.has-push-left #header-wrapper {
left: 300px;
}
DEMO
I want to animate a translateX with transition on a click event by adding a class to the div in the js. The transform and transition properties are added in the css file.
var widget = document.getElementById('widget');
widget.style.display = 'block';
document.getElementById('widget2').clientWidth; //comment this line out and it wont work
widget.className = 'visible';
It only works if I query the width property of any element in the dom before adding the class.
here is a jsfiddle:
https://jsfiddle.net/5z9fLsr5/2/
Can anyone explain why this is not working?
That's because you begin your transition and modified the display property "at the same time". Altering display will ruin any transition (citation needed, admittedly), so it would be a good idea to isolate the display changing and actual transiting:
https://jsfiddle.net/5z9fLsr5/3/
document.getElementById('showWidget').addEventListener('click', function(e) {
e.preventDefault();
var widget = document.getElementById('widget');
widget.style.display = 'block';
//document.getElementById('widget2').clientWidth;
window.setTimeout(function(){
widget.className = 'visible';
},0);
});
#widget {
width: 200px;
height: 80px;
background: black;
position: absolute;
transition: transform 500ms;
transform: translateX(-200px);
display: none;
}
#widget.visible {
transform: translateX(200px);
}
#widget2 {
position: absolute;
right: 0
}
show
<div id="widget"></div>
<div id="widget2">xxx</div>
Querying clientWidth seems to "pause" the execution for some time, so it works too.
The issue here is the initial setting of display: none. To the browser's layout manager, this indicates that the layout should be done as if the element in question wasn't even in the DOM (it still is, mind you). This means that the CSS style transform: translateX(-200px); will not be applied.
Doing this:
widget.style.display = 'block';
widget.className = 'visible';
triggers both modifications essentially at the same time - the layout is only re-done after both statements have been executed. Inserting document.getElementById('widget2').clientWidth; (clientHeight works as well) triggers the layout manager to repaint, thus registering transform: translateX(-200px).
As others have mentioned before me, the solution is to either use opacity instead of display (this would be my choice), or to use setTimeout with a delay of 0 (see Why is setTimeout(fn, 0) sometimes useful?).
I am using the following jQuery script:
$("#divid").mouseenter(function() {
$('#divid').show(1000);
}).mouseleave(function() {
$('#divid').hide(1000);
});
$("#hldiv").mouseenter(function() {
$('#divid').show(1000);
}).mouseleave(function() {
$('#divid').hide(1000);
});
As you can see, when the mouse hovers over a hyperlink called #hldiv, the #divid should be shown. The main goal is to keep the DIV shown if the mouse is over the DIV - but the #divid should not be visible initially.
If the mouse moves over the hyperlink, the DIV should appear, and when the mouse then moves over the DIV, it should stay until the mouse leaves.
The problem is that with my current code, when the user moves over the hyperlink and then out - the DIV appears/disappears correctly, but when the user moves out of the hyperlink and over the DIV itself, the DIV also disappears.
How should I fix this?
Why don't you add a container and do:
<div id='container'>
<a ID="hlDiv">hlink</a>
<div ID="divId">Test Test Test</div>
</div>
$(document).ready(function() {
$("#hlDiv").hover(function() {
$('#divId').show(1000);
})
$('#container').mouseleave(function(){
$('#divId').hide(1000);
});
});
fiddle here: http://jsfiddle.net/w68YX/8/
If I understood right, rewriting
$("#divid").mouseenter(function() {
$('#divid').stop(true);
$('#divid').show(1000);
}).mouseleave(function() {
$('#divid').hide(1000);
});
Might help, since it stops the current animation (fading out) and fades it back in (if it has already turned a bit transparent).
However this depends on your HTML, and might not work in your case, so please post the structure also.
I am very late to this party - but there is a far better way to do this, so I want to add it for the sake of future browsers. You don't need jQuery for this effect at all.
First, wrap the two items in a container (here I'm using a div with class container), and apply a class to the item you want to appear/disappear on hove (here I'm using the show-on-hover class on the #divId element)
<div class="container">
<a id="hlDiv" href="...">link text</a>
<div class="show-on-hover" id="divId">popup stuff</div>
</div>
Next, set up your CSS as follows:
.container > .show-on-hover { display: none; }
.container:hover > .show-on-hover { display: block; }
#divId { /* whatever styles you want */ }
The effect is that the hover is now controlled entirely by CSS - but, it doesn't have the 1s transition you originally had. This is a little more complicated (and currently doesn't work in IE - but will be supported as of IE10).
Simply change the CSS as follows:
.container { position: relative; }
.container > .show-on-hover { opacity: 0.0; position: absolute; }
.container:hover > .show-on-hover { opacity: 1.0; }
.show-on-hover {
-webkit-transition: opacity 1s; /* Chrome / Safari */
-moz-transition: opacity 1s; /* Firefox */
-o-transition: opacity 1s; /* Opera */
}
The relative positioning on the .container means that the container sets its own bounding boxes for its child elements and their positioning. This means that when you then set the > .show-on-hover styling to position: absolute;, it will still be constrained to its parent (if you set left: 0; as an example, it will move to the left edge of the .container, rather than the screen).
The opacity toggle now simply makes the absolutely positioned item show/disappear wherever you've placed it (and you would update the CSS to put it exactly where you want, relative to the hyperlink). Because we're no longer using display: none - the DIV will always take up space on the screen - even when hidden (which is probably not what you want).
Finally - the last block, which sets transitions, tells modern browsers that whenever the opacity changes on elements of class .show-on-hover, make that change happen as a tween over 1s of duration.
Here is a jsFiddle showing the transitions: http://jsfiddle.net/TroyAlford/nHrXK/2
And here is a jsFiddle showing just the toggle: http://jsfiddle.net/TroyAlford/nHrXK/3/