Run a line of jQuery after the previous one is complete - javascript

I have two lines of jQuery and I need one to run after the previous one is complete.
I know js is read line by line, but the second line is happening too soon.
I can't use jQuery callbacks because these functions unfortunately don't have them.
I tried doing a setTimeout() with a wait for 500ms, 1000ms, and 1600ms, but it didn't seem to work. Visually, the second line took place before the first line was completed.
The element used in the first selector has a css transition of 1s and I want that to finish and then the second line to take place.
Is it possible to do something like:
if transition complete, run js
else wait, then check if transition complete, then if true run js.
Here are the two lines of js:
$('#searchInput').removeClass('slideBack').css('top', 0);
$('#headerTopBar').css('position', 'relative').hide();

If you want to wait for a CSS transition to complete, then you need to use an eventListener for the CSS transitionend event on the object that is doing the transition. That will then provide an event handler callback where you can carry out the second line of code after the CSS transition completes.
$('#searchInput').one("transitionend", function(e) {
$('#headerTopBar').css('position', 'relative').hide();
}).removeClass('slideBack').css('top', 0);
Now, in the real world, not every browser uses the same event name for the transition end event so one typically either installs event handlers for all the possible names of the transition end event or one in your code figure out which name is used in this local browser and then use that variable as the event name.
Note: I use .one() so that this event handler will automatically uninstall itself after it fires. I don't know for sure you want that, but it is often desirable with these types of event notifications.
Here's a more verbose version that listens for all possible transition end event names (for all major browsers):
$('#searchInput').one("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", function(e) {
$('#headerTopBar').css('position', 'relative').hide();
}).removeClass('slideBack').css('top', 0);

.css() does not take a callback, you can use .animate() instead:
$('#searchInput').removeClass('slideBack').animate({top:0},500,function () {
$('#headerTopBar').css('position', 'relative').hide();
});

Related

Why shouldn't I use delay? [duplicate]

According to jQuery document on .delay(),
The .delay() method is best for delaying between queued jQuery
effects. Because it is limited—it doesn't, for example, offer a way to
cancel the delay—.delay() is not a replacement for JavaScript's native
setTimeout function, which may be more appropriate for certain use
cases.
Could someone please expand on this? When is it more appropriate to use .delay(), and when is it better to use .setTimeout()?
I think what you posted explains itself really.
Use .delay() for jQuery effects including animations.
setTimeout() is best used for everything else. For example when you need to trigger an event at a certain elapsed time.
As far as I understand, .delay() is meant specifically for adding a delay between methods in a given jQuery queue. For example, if you wanted to fade an image into view during the span of 1 second, have it visible for 5 seconds, and then spend another second to fade it out of view again, you could do the following:
$('#image').fadeIn(1000).delay(5000).fadeOut(1000);
In this instance, .delay() is more intuitive to use since it is specifically meant for delaying events in a given jQuery queue. setImeout(), on the other hand, is a native JavaScript method that isn't intended explicitly for a queue line. If you wanted an alert box to pop up 1 second after clicking on a button, you could do the following:
function delayAlert() {
var x = setTimeout("alert('5 seconds later!')", 5000);
}
<input type="submit" value="Delay!" onclick="delayAlert();" />
You can use delay with animations, for example:
$('.message').delay(5000).fadeOut();
You can also use timeOut to delay the start of animations, for example:
window.setTimeout(function(){
$('.message').fadeOut();
}, 5000);
As you see, it's easier to use delay than setTimeout with animations.
You can delay pretty much anything with setTimeout, but you can only delay animations with delay. Methods that aren't animations are not affected by delay, so this would not wait a while before hiding the element, it would hide it immediately:
$('.message').delay(5000).hide();
.delay() is mostly used for chaining together animation effects with pauses in between.
As the docs mention, there is no way to cancel the delay. In the case where you may want to cancel the delay, a setTimeout() should be used instead so you can cancel it with clearTimeout()
Another side effect of delay(): it seems to disable the ability to hide (or fadeOut, etc) the objecting being delayed, until the delay is over.
For example, I set up the following code (perhaps a stackoverflow developer will recognize the css names....) to hide a 'div':
$j(document).ready(function(){
var $messageDiv = $j("<div>").addClass('fading_message')
.text("my alert message here").hide();
var $closeSpan = $j("<span>").addClass('notify_close').text("x");
$closeSpan.click(function() {$j(this).parent().slideUp(400);});
$messageDiv.append($closeSpan);
$j('.content_wrapper_div').prepend($messageDiv);
$messageDiv.fadeTo(500, .9).delay(5000).fadeTo(800,0);
});
Clicking the "x" that's in the span (that's in the 'div') did fire off the click function (I tested with an alert in there), but the div didn't slideUp as directed. However, If I replace the last line with this:
$messageDiv.fadeTo(500, .9);
..then it did work - when I clicked the "x", the surrounding div slideUp and and away. It seems as if the background running of the "delay()" function on the $messageDiv "locked" that object, so that a separate mechanism trying to close it couldn't do so until the delay was done.

Window.requestAnimationFrame() not workig in combination with stopImmediatePropagation

I am trying to speed up webpage but since I am new to JavaScript, optimization doesn't go without errors. I created onclick transition effect that I would like to combine with Window.requestAnimationFrame(). When I add this line of code i get transition effect but an error pops up in console with a message:
Uncaught TypeError: Window.requestAnimationFrame is not a function.
This message is repeated in console in milliseconds. So I have thousand or so messages in few minutes. Like some kind of a loop.
Here is the code:
function showNews(event) {
requestAnimationFrame(showNews);
event.stopImmediatePropagation();
document.getElementsByClassName("News")[0].style.cssText = "width:45%; height: 100%; transition: 0.5s; overflow-y: auto";
document.getElementsByClassName("Menu_item")[1].getElementsByTagName("A")[0].blur();
document.getElementsByClassName("News")[0].focus();
}
document.getElementsByClassName("Menu_news")[0].addEventListener("click", showNews, false);
Uncaught TypeError: Window.requestAnimationFrame is not a function is happening when your brother does not support requestAnimationFrame.
But I would be surprised if it is the case.
The loop you are seing justly is the effect of requestAnimationFrame(showNews).
What is happening is that the first time showNews is called, i.e. when you click on .Menu_news, it requests itself (showNews) to be called again at next animation frame.
Then when it is called again it does it again (so it will be called a third time, then a fourth time and so and so on).
So your browser looks like it supports requestAnimationFrame.
If it really doesn't, try to use a polyfill like this one but I really don't think you need it.
What is going to throw errors though is event.stopImmediatePropagation(); because when requestAnimationFrame calls showNews, it does not give it an event but a number (a timestamp). This number obviously does not have a stopImmediatePropagation method. I believe this is the source of the error you are seing.
What you could do to fix it is to replace event.stopImmediatePropagation(); with :
if(event && isNaN(event)){
event.stopImmediatePropagation();
}
It will solve this immediate issue.
But it is probably not really what you are trying to achieve (though I have no idea what it is).
Indeed creating an animation loop like you are doing here doesn't make sense.
There isn't any operation in showNews that are meant to animate something.
Worse, even if it was working the blur and focus function will just block user focus on .News node (because of the requestAnimationFrame you are creating).
The CSS you are creating does define a animated transition, but it will be automatically managed by the browser and it surely doesn't need to be manually set again at each animation frame. One time is enough.

jQuery cancel previously queued event handlers to restart CSS animation

I'm creating a slideshow, which will run on a timer (shown by progress bar), but allow users to click arrows to force next. I'm trying to use as much CSS3 as possible, so for my loop timer I'm using the CSS3 animation of the progress bar.
The way it works is that I start my progress bar at width:0, and set it to width:100%;. It has a CSS3 transition of 5s. I then watch for the end of the animation, and use that to call my resetprogress and changeimage functions, after which I then start the progress again. It loops indefinitely.
I've created a jsFiddle, simplified, to show what I'm talking about: http://jsfiddle.net/a3H9L/
Code for the simplified version is below. As you can see, I call startProgress, in which I start the CSS3 animation by changing the width, then set a watcher for the end of said animation, at which point I reset and then start again.
startProgress();
function startProgress() {
$('div').width('100%');
$('div').one('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend',function(e){
resetProgress();
startProgress();
});
}
function resetProgress (){
$('div').addClass('notransition'); // Disable transitions
$('div').width('0');
$('div')[0].offsetHeight; // Trigger a reflow, flushing the CSS changes
$('div').removeClass('notransition'); // Re-enable transitions
}
$('button').click(function(event){
resetProgress();
startProgress();
});
My question is, if a user clicks the reset (which will eventually be Next and Previous), how do I break the original loop before resetting and starting a new loop? Right now, I think that I'm starting a new loop without ending the original, which is getting me two loops running at the same time.
EDIT: The reason I think something is wrong is that as I clicked reset a few times, things in the loop start happening at other times besides when the progress is reset.
TL;DR:
Simply call an .off() before chaining your .one()
Let's run an experiment using your fiddle.
Experiment 1:
Using the console to log a simple message everytime the .one() function is called:
$('div').one('...', function(e) {
console.log('one called!');
resetProgress();
startProgress();
});
Observation:
Yes, you are right, they do stack! If you click on the button, more .one() functions are added and will fire together at the same time once the bar reaches the end of the animation. ALL of them will still fire once the animation completes the following and subsequent times!
i.e.: Run fiddle, click your button five times. On completion of the first animation, console logs six messages (5 + 1). The bar resets itself and produces another six more messages. This goes on in multiples of six.
Experiment 2:
Now, let's try turning itself off at the start of the function:
$('div').one('...', function(e) {
$(this).off(e);
console.log('one called!');
resetProgress();
startProgress();
});
Observation:
This didn't produce the cancelling effect we were expecting. Same result as the first experiment.
Experiment 3:
Let's try turning all the handlers off (by omitting the "e"):
$('div').one('...', function(e) {
$(this).off();
console.log('one called!');
resetProgress();
startProgress();
});
Observation:
All queued .one() handlers execute at the end of the animation, but they terminate themselves after running once and do not fire the next time the animation completes.
Experiment 4:
What you actually wanted to do, was to cancel all previously queued handlers before setting a new one. So let's do this:
$('div').off().one('...', function(e) {
console.log('one called!');
resetProgress();
startProgress();
});
Observation:
There's your answer! This function now runs once, as the previous handlers were unset before a new one has been placed. Simply call an .off() before chaining your .one()
Disclaimer:
These experiments assume that those were your only event handlers on your element. If you have additional handlers set by .on(), .one() or similar, instead of using .off() to clear everything, you have to specify which handlers you want to clear, like so:
.off('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend')

Understanding Threading in javascript

I have two div's in my html page
<div id="box1">
</div>
<div id="box2">
</div>
Both of them are absolutely positioned at different positions.
#box1,#box2 {
height: 100px;
width: 100px;
background-color: red;
position: absolute;
}
#box2 {
top :200px;
}
In my Javascript I am animating these two div's as shown below
$(document).ready(function() {
function animateBoxes() {
$("#box1").animate({left : "500px"},5000);
$("#box2").animate({left : "500px"},3000);
}
animateBoxes();
});
When my page loads both the div's start moving at the same time.
My question is if javascript is single threaded how can both the div's move at the same time.
Because the movement of the div's is handled by javascript by two animate functions, one on box1 and other on box2.
How both the animate functions got executed at the same time?
Javascript is single threaded meaning something if is stuck the whole script is stuck...and only way overcome this is spawn worker. To answer your question, Do You Know how animate function of jquery works...it sets timer to call function that updates div's position. So both div get positioned a little bit toward their goal. Timer is provided by javascript and is handled like an event. Javascript has event loop..which is reiterated by browser. Meaning as soon as js is done browser goes through all events and check if they have been fired and then run the function that is associated with them. Here animate function associate itself to timer event and gradually updates div's position, thus looking like it animated. And since it happens in steps the whole js doesn't have to wait for this animation to end to continue executing.
This is how events basically work in js:
browser starts executing the code..
every next action waits till last action is done.
when javascript reaches the code that is attaching function to event, js registers the event, let's say click event. Now it know that button has click event and it has this function.
Now after all code below has been executed browser starts new routine. To check for any event that have been fired.
...looping but no events..
you click button ...it adds that click event has fired to event loop check list.
browser again checks the event loop...it sees the event.
it runs the code for that event and clear it...
suppose you clicked two times...then the code of second event won't start executing till the first is done.
Timer is same but it fires every x amount of time has passed. But as you can see event loops gets stucked executing the function related to the event...the timer is not perfect.
let's say you set timer for 10 ms. And as 9ms had passed you clicked the button starting another event. So your button event starts executing..but it did something really long that took 5 ms.
so your timer actually fires at 14ms.
Take a look at this example: http://jsfiddle.net/82zLC/6/
This will give you idea that animation is divided into chunks which are update step by step...try changing t to 60.
JavaScript can act asynchronous in many situations. .animate() is an example of this. It pretty much has to act asynchronous, in order to not interrupt other page processes. If you are looking for the events to happen one-after-the-other, try looking into callbacks:
$("#box1").animate({left: "500px"},5000, function(){
$("#box2").animate({left: "500px"},5000);
});
When we pass the function to .animate(), it calls the function after it is done with the animation.

jquery functions overloading

I have a function binded to a .click() event. Each time the user clicks, the browser fadeOut() the current element and animate() the marginLeft of the new element. For some reason when I click fast on the button which is binded to this click event, in Chrome it jumpes the animation and just go on directly to the next on (only if I click like 5 times in 1 second) and don't add the marginLeft, in this case it causes a major usability issue for the UI. Is there any fallback for this scenario? Like if the animation is not completed directly add it with css() or something like that?
Thanks
E: The click() event calls a function where all the magic happens, if it helps ..
You may want to look into jquery's .stop() method. It allows you to short-circuit any currently-running animations on an element.
Simple example:
$myElement.on('click', function() {
// first 'true' clears animations in the queue for this element
// second 'true' completes the currently-running animation immediately
$(this).stop(true, true).fadeOut();
});
http://api.jquery.com/stop/

Categories

Resources