Stopping sequential functions/animations with clearTimeout? - javascript

This code sample works, it does not work with animations of the same duration instead of just an alert.
<script>
var timeoutOne = null,
timeoutTwo = null,
timeoutThree = null;
function alertOne() {
alert('first message');
timeoutOne = setTimeout(alertTwo, 3000);
}
function alertTwo() {
alert('second message');
timeoutTwo = setTimeout(alertThree, 1000);
}
function alertThree() {
alert('third message');
timeoutThree = setTimeout(alertFour, 1000);
}
function alertFour() {
alert('fourth message');
}
function startCountdown() {
alertOne();
}
function stopCountdown() {
clearTimeout(timeoutOne);
clearTimeout(timeoutTwo);
clearTimeout(timeoutThree);
}
</script>
I intended to have multiple links but I'm limited to two. The first link is the working demo of the project. The second is all of the code discussed in the question
working-demo
different-code
I'm working on this project that involves animation and re-calculation of elements/repositioning in the event of a rescale and I can't seem to get it to work. I've been working on this for over a month now, and I've tried at least 20 different iterations.
Note: the links are highlighted code (except for the actual demo of the interactive display).
The timeout-testing link is a short demonstration of four, sequential-alerts being stopped by a single button. This is the effect I'm trying to accomplish with regard to the animation. note that this is just code, not an actual alert/button interface
The shortened-script is the specific problematic-code taken out of the problematic script.
My solution so far is to use three scripts, the calculated/positioned/animated elements are removed, re-added, the three main functions of setup,position,dimensions are recalled as if it was a new page... but I have three scripts so it breaks after the fourth re-scale while the animation is running. I've tried to switch back and forward between two scripts but it doesn't work going from script 2 to 1.
The alert example and shortened-script is my new attempt which tries not to use the three scripts method done in the working demo.
I haven't tried promises/deferred yet. I'm wondering if I'm missing something obvious.
The three scripts used in the working demo are first,second,third-script respectively.
They are pretty much copies of each other with minor differences.

The animationend event is more reliable for what I think you're trying to do. What's nice is that you don't have to try and time the exact moment an animation finishes; you instead rely on the browser. Have a look at this example.
var step_one = document.querySelector('.step.one');
var step_two = document.querySelector('.step.two');
function stepOneHandler() {
step_two.classList.add('go');
}
step_one.classList.add('go');
step_one.addEventListener('animationend', stepOneHandler);
#keyframes move-one-right {
to { transform: translateX(3em); }
}
#keyframes move-two-right {
to { transform: translateX(6em); }
}
.step {
background: red;
display: block;
width: 3em;
height: 1.5em;
margin-bottom: 1em;
}
.step.one.go {
animation: 1s move-one-right forwards;
}
.step.two.go {
animation: 1s move-two-right forwards;
}
<div class="step one"></div>
<div class="step two"></div>

Related

JQuery or Javascript that will make an element fade away automatically

So I am making a clicker game and am kind of stuck. I want a popup like cookieClicker has when you get an achievement. It pops up and tells you what happened, you can click the x or it will just fade away after a few seconds.
I tried making something with pure javascript and CSS to no avail, it would fade away nicely but not automatically.
So how do I make it so whenever X element is made/displayed then it goes away after 3 seconds?
Also, if it matters the element would be created by a javascript function, and multiples might be created at the same time.
P.S. I tried searching and found something about auto-fading in javascript but nothing in there seemed to work either.
EDIT: After trying to view cookieclicker source and playing the game again it appears it doesn't even have this functionality. The closest thing I can compare it to is when you would add something to your cart on a website, then it alerts you the item was added and then fades away.
Here is one approach which uses Javascript to trigger a CSS transition:
var button = document.getElementsByTagName('button')[0];
var div = document.getElementsByTagName('div')[0];
function autoFader() {
if (window.getComputedStyle(div).getPropertyValue('display') === 'none') {
div.style.display = 'block';
setTimeout(function(){
div.style.opacity = '0';
},10);
setTimeout(function(){
div.removeAttribute('style');
},4010);
}
}
button.addEventListener('click',autoFader,false);
div {
display: none;
width: 200px;
height: 100px;
margin: 20px auto;
padding: 6px;
font-size: 20px;
color: rgb(255,255,255);
text-align: center;
border: 3px solid rgb(127,0,0);
background-color: rgb(255,0,0);
opacity: 1;
transition: opacity 3s linear 1s;
}
<button type="button">Click Me</button>
<div>
<p>Hi, I'm an auto-fading pop-up.</p>
</div>
So, your openPopup function might look like this:
function openPopup(/* options here */) {
const popup = actuallyOpenPopup();
waitSomeTimeAndCloseIfNotClosedYet(popup);
}
where 2nd function should take a popup instance (which has .close method probably, or dismiss)
and start a timeout. You need to keep that timeout, so if close was called, you need to cancel it.
Something like this:
function waitSomeTimeAndCloseIfNotClosedYet(popup) {
const originalClose = popup.close;
/* monkey patching, decorating,
separate method - whatever you prefer */
popup.close = function () {
clearTimeout(this.timeout);
originalClose.call(this);
};
popup.timeout = setTimeout(() => popup.close(), 3000);
}
So, if was closed manually - it wont be called twice, if not, will fire up a timeout and close automatically.
Via CSS you can only achieve visible closing, but not removal of nodes. (Google for transition visibility, fade out modal etc.)
Hope this helps!

Fade in arbitrary number of text lines sequentially with jquery queue

This gets pretty specific but I'd like to be able to fade in an arbitrary number of children sequentially and with delayed timing using jquery queue (though I'm open to other methods). Here is what I have working already.
This is the basic html block I'm working with
<header>
<p class="copy">Thing one</p>
<p class="copy">Thing two</p>
<p class="copy">Cat in the Hat</p>
</header>
This current jquery works, but it feels hacky to me in that I need to know in advance how many nodes to expect.
var $screenHeader = $('header');
$screenHeader.queue(function () {
$screenHeader.find('.copy:nth-child(1)').addClass('visible');
$(this).dequeue();
})
.delay(1500)
.queue(function () {
$screenHeader.find('.copy:nth-child(2)').addClass('visible');
$(this).dequeue();
})
.delay(1500)
.queue(function () {
$screenHeader.find('.copy:nth-child(3)').addClass('visible');
$(this).dequeue();
})
.delay(1500);
I would love it if something like this worked
for (var i = 1; i < $screenHeader.children().length+1; i++) {
$screenHeader.queue(function () {
$screenHeader.find('.copy:nth-child('+i+')').addClass('visible');
$screenHeader.dequeue();
}).delay(1500);
}
or better yet
$screenHeader.children().each(function (i) {
$screenHeader.queue(function () {
$screenHeader.find('.copy:nth-child('+i+')').addClass('visible');
$screenHeader.dequeue();
}).delay(1500);
});
or even more betterer (then i'm done, I promise)
$screenHeader.children().each(function () {
$screenHeader.queue(function () {
$(this).addClass('visible');
$screenHeader.dequeue();
}).delay(1500);
});
Now, I know there's some funkiness with how $(this) is passed around so that last one isn't a priority, but it would be really nice to get some sort of loop working. Listing them out and repeating all that code and being tied to the html kills me.
Help would be greatly appreciated. :)
Why not do like this:
var $elements = $('header').find('.copy');
$($elements).each(function(i, ui){
setTimeout(function(){
$(ui).addClass('visible');
},(i*1500));
});
You might consider using CSS for the timing of the animation, instead of jQuery.
See this live example.
And here is the code:
HTML:
<header>
<p class="copy">Thing one</p>
<p class="copy">Thing two</p>
<p class="copy">Cat in the Hat</p>
</header>
CSS:
#keyframes fade-in {
0% { opacity: 0; }
100% { opacity: 1; }
}
header p {
opacity: 0;
animation-name: fade-in;
animation-duration: 1s;
animation-fill-mode: forwards;
}
JS:
$(function() {
$('header').children().each(function(idx) {
$(this).css('animation-delay', idx * 500 + 'ms')
});
});
In my solution, I am using jQuery only for the "arbitrary number of children" part of your question. If you knew the number of children in advance (or, at least had an idea as to what the maximum number of children might ever be), you could do the animation entirely in CSS:
header p:nth-child(2) { animation-delay: 500ms; }
header p:nth-child(3) { animation-delay: 1s; }
/* So on and so forth until your bases are covered... */
(That type of solution would be very easy to generate in a loop using a CSS preprocessor like Sass or Less.)
Figured it out!
$(this) inside the $screenHeader.queue(... loop was the header. I needed to store the child before entering the loop queue part.
var delayTime = 1500,
$screenHeader = $('#screen-'+screenIndex+' header'),
$copyChildren = $screenHeader.children('.copy');
$copyChildren.each(function () {
var child = $(this);
$screenHeader.queue(function () {
child.addClass('visible');
$screenHeader.dequeue();
}).delay(delayTime);
});
Feelin classy.

Show a div only when user hover an element for couple seconds

I have an hidden div, and I want to show the hidden div only when user's mouse over another a trigger element for several seconds instead show the hidden div once the user hover the trigger element
here is my javascript code
$('.c_like_icon').mouseover(
function() {
var timeout = setTimeout(function(){
var comment_id=$(this).attr('data-commentId');
$.ajax({
url: 'ajax_c_like_user.php',
method:'post',
data:{comment_id:comment_id},
success:function(data){
var like_num=$('#'+comment_id+'c_like_number').text();
if(like_num>=1){
$('#'+comment_id+'like_user_w').html(data);
$('#'+comment_id+'like_user_w').show();
}
else{
$('#'+comment_id+'like_user_w').hide();
}
}
})
}, 2000); //2 seconds
},
function(){
var comment_id=$(this).attr('data-commentId');
clearTimeout(timeout); //cancel the timeout if they hover off
$('#'+comment_id+'like_user_w').hide();
// do stuff when hover off
}
)
define a timeout in your hover in function and clear in the hover out function, to prevent it being fired if they leave before the time runs out, like this:
var timeout;
$('#trigger').hover(
function() {
timeout = setTimeout(function(){
// do stuff on hover
$('#hiddenDiv').show();
}, 2000); //2 seconds
},
function(){
clearTimeout(timeout); //cancel the timeout if they hover off
// do stuff when hover off
$('#hiddenDiv').hide();
}
);
You can very easily do this CSS only. No jquery is required which presents a huge benefit as it is a big library to download.
Just use delayed transitions. Here is my example (live demo here: http://codepen.io/anon/pen/jbGhi):
HTML
<div id="first"></div>
<div id="second"></div>
In this example, the ids are not necessary but I find it better to understand what happens.
CSS
for the purpose of this example, I'll stylize the divs (to make the hover effect more obvious) but none of the following really matters:
div{
height: 50vmin;
width: 50vmin;
border: solid 5px black;
float: left;
margin-right: 10vmin;
}
and this is where the magic happens:
div#first:hover ~ div#second{
transition: all 0.2s ease 1s;
background-color: green;
}
We are using the css selector "~" that means "any sibling element after (and their children)". In that example it means "a div called #second that is sibling and after a div called #first that is hovered". Basically, as long as the second div is a sibling and after or contained within a sibling (that is after) of the first one, you'll get the desired effect.
And there you go. You can add more delay (change "1s" to whatever duration) before the change occurs, and you can smoothen the transition itself (change "0.2s" to whatever duration).
PS: in the CSS, don't forget to add all vendor prefixes for transition and transform. Always check caniuse.com to know which prefixes are needed. Example:
-webkit-transition: all 1s;
transition: all 1s;
i know its an old question, but i think it should have a vanilla solution
// Element will be the triggerer
let timeOut;
element.addEventListener('mouseover', (e) => {
timeOut = setTimeout(() => {
// Do your stuff here
}, 400);
});
element.addEventListener('mouseout', (e) => {
clearTimeout(timeOut);
});

How do I show or hide div, based on position of another div

I have the following jquery that slides a div horizontally:
$('.nextcol').click(function() {
$('.innerslide').animate({'left': '-=711px'}, 1000);
});
$('.prevcol').click(function() {
$('.innerslide').animate({'left': '+=711px'}, 1000);
});
What I want to happen is this... if the div.innerslide has a position that is left: 0px then I want to hide div.backarrow. If the position is not left: 0px, then it shows it.
Any help would be greatly appreciated.
EDIT (added HTML Markup)
<div class="backarrow prevcol">
<div id="mainleft" class="overflowhidden">
<div class="innerslide">
<div class="col">my content including next</div>
<div class="col">my content including next</div>
<div class="col">my content including next</div>
</div>
</div>
Try this:
if ($('.innerslide').css("left") == 0) {
$('div.backarrow').hide();
} else {
$('div.backarrow').show();
}
Fix for Double-Click Issue:
From what you described in your comment about the issue when the visitor double-clicks, it sounds like the double-click is causing two of the animation events to fire. To keep this from happening, you can either disable the click handler while the animation is running and re-enable it once it is finished, or you can try to write a new thread to continually check the element's position. One of these solutions is not a good idea - I'll let you figure out which one :) - but the other actually has a very simple solution that requires little change to your existing code (and may actually reduce your overhead by a teeny weeny amount):
$('.nextcol').on("click.next", function() {
$('.innerslide').animate({'left': '-=711px'}, 1000, showHideBack());
$(this).off("click.next");
});
$('.prevcol').on("click.prev", function() {
$('.innerslide').animate({'left': '+=711px'}, 1000, showHideForward());
$(this).off("click.prev");
});
Then add this this line to showHideBack() (and a complementary one to showHideForward() if you are using that):
$('.nextcol').on("click.next".....
I suggest that you write a function to set each click handler and another to remove each one. This will make your live very easy and the whole solution should reduce overhead by removing unnecessary click handlers while the animation is running.
Note: the animation method often calls its callback before the animation finishes. As such, you may wish to use a delay before calling the showHide... method(s).
Let me know if you have any questions. Good luck! :)
UPDATE:
Here is the updated version of the fiddle you gave me with all bugs ironed out. It looks like I misunderstood part of your goal in my original solution, but I straightened it out here. I have also included the updated jQuery, here:
var speed = 1000;
var back = $("div.backarrow");
var next = $(".nextcol");
var prev = $(".prevcol");
var inner = $(".innerslide");
function clickNext(index) {
next.off("click.next");
inner.animate({
'left': '-=711px'
}, speed, function() {
back.show(); //this line will only be hit if there is a previous column to show
next.delay(speed).on("click.next", function() {
clickNext();
});
});
}
function clickPrev() {
prev.off("click.prev");
inner.animate({
'left': '+=711px'
}, speed, function() {
if (inner.css("left") == "0px") {
back.delay(speed).hide();
prev.delay(speed).on("click.prev", function() {
clickPrev();
});
} else {
back.delay(speed).show();
prev.delay(speed).on("click.prev", function() {
clickPrev();
});
}
});
}
next.on("click.next", function() {
clickNext();
});
prev.on("click.prev", function() {
clickPrev();
});​
I was going to also include a condition to check if you were viewing the last column, but, as I don't know what your final implementation will be, I didn't know if it would be applicable. As always, let me know if you need help or clarification on any of this. :)
You could try the step option — a callback function that is fired at each step of the animation:
$('.prevcol').click(function() {
$('.innerslide').animate({ left: '+=711px' },
{
duration: 1000,
step: function(now, fx) {
if (now === 0 ) {
$('div.backarrow').hide();
} else {
$('div.backarrow').show();
}
}
});
});
More examples of usage in this article The jQuery animate() step callback function

How to get timing & sequence right in jQuery

Assume we have three light bulbs, and I want to glow the first one, keep it on for a few milliseconds, turn it off, turn on the next and continue in the same way.
Turning the light bulbs on and off is done by adding and removing a class. How do I achieve this?
P.S. I used light bulbs just to make my question clearer. Basically what I need is, how to add a class to a div, keep it for some time, remove class, apply a class to another div, keep it for some time and remove it and so on...
*Edit
Clarification: The number of bulbs is dynamic
You can use a simple combination of setInterval and jquery selecors
Check this fiddle http://jsfiddle.net/aqXtL/1/
The Javascript function setInterval(code, interval) will repeatedly execute code with your interval. Just keep a variable with a counter, and use jquery's addClass and removeClass to turn the lights on and off.
you can use setTimeout like setTimeout(yourfuncname, 3000) == wait 3 seconds then run that function. If you need to use animations then the jquery animate method has a completed callback as a parameter.
very simplified example...
function startDimming(){
setTimeout(_interval,function(){
// Remove all "glowing" bulbs.
// Add class to first "bulb".
setTimeout(_interval,function(){
// Remove all "glowing" bulbs.
// Add class to second "bulb".
setTimeout(_interval,function(){
// Remove all "glowing" bulbs.
// Add class to last "bulb".
startDimming();
});
});
});
}
You can use the animation of jquery and use the complete function to do other tasks.
.animate( properties [, duration] [, easing] [, complete] )
$('#clickme').click(function() {
$('#book').animate({
opacity: 0.25,
left: '+=50',
height: 'toggle'
}, 5000, function() {
// Here you have animated the object and the animation is complete, so
// you can start a new animation here
});
});
HTML :
<div class="bulb"> </div>
<div class="bulb"> </div>
<div class="bulb"> </div>
CSS:
.bulb {
width: 50px;
height: 100px;
border: 2px #000 solid;
float: left;
margin: 10px;
}
.bulb.on {
background: #ff0;
}
Jquery :
var timer;
function light ( i ) {
i = i % $(".bulb").length;
$(".bulb").eq(i).addClass("on").siblings().removeClass("on");
timer = setTimeout(function(){
light( ++i );
}, 1000);
}
$(document).ready( function() {
light(0);
});
Edit : here's a working sample, http://jsfiddle.net/QdWgM/

Categories