How to pass $(this) reference to a call back function? - javascript

I have the following animations in my web page:
$(".anim-item").not(this).animate({
opacity: 0,
}, { queue: true, duration: 1000 } , function() {
// Animation complete.
});
$(this).animate({
left: 200,
}, { queue: true, duration: 1000 } , function() {
// Animation complete.
});
Currently both the animations are running simultaneously. I want the second animation to run after the first one. I tried putting the second one inside the callback function, but cannot find a way to get the $(this) reference working. Any idea how to get this working?
Thanks in advance.

Your function is wrong, if you are declaring options, then the callback goes in the options object:
$(".anim-item").animate({
opacity: 1,
}, {duration: 1000, queue: true, complete: function() {
$(this).animate({
left: 200,
}, { queue: true, duration: 1000, complete: function() {
// Animation complete.
}});
}});
Also, don't make a global variable containing the item, that's just asking for trouble, especially as jquery will maintain it for you in this instance, if you need to declare a new variable for the object in chaining, generally you are not doing it right ;)

Two ways:
cache this in a local variable before calling .animate()
use .proxy() to pass your this reference to .animate()
example 1:
var func = function(){
var self = this;
$(".anim-item").not(this).animate({
opacity: 0,
}, { queue: true, duration: 1000 } , function() {
self.animate({});
});
};
example 2:
var func = function(){
$.proxy($(".anim-item").not(this).animate({
}), this);
};

Save it under a different name, like this:
var myThis = this;
$(".anim-item").not(this).animate({
opacity: 0,
}, { queue: true, duration: 1000 } , function() {
$(myThis).animate({
left: 200,
}, { queue: true, duration: 1000 } , function() {
// Animation complete.
});
});
The closure of the inner function will make sure it's visible.

Make an alias for this via
var _this = this;
If you write a jQuery query $('.abc') and use functions like click, hover etc, this will always reference to current DOM node jQuery is processing.

Store this in a local variable.
var _this = this;
$(".anim-item").not(this).animate({
opacity: 0,
}, { queue: true, duration: 1000 } , function() {
// Animation complete. Next animation
$(_this).animate({
left: 200,
}, { queue: true, duration: 1000 } , function() {
// Animation complete.
});
}
);

In a jQuery callback function this is always set to the DOM element that the function applies to.
If you want access to this in your first callback function you'll have to create a reference to it before you animate:
var self = this;
$(".anim-item").not(this).animate({
opacity: 0,
}, { queue: true, duration: 1000 } , function() {
$(self).animate({
left: 200,
}, { queue: true, duration: 1000 } , function() {
// Animation complete.
});
});

Related

How to run inline script AFTER jquery confirmed as defined

I'm working under particular circumstances and I am trying to get a slick slider carousel to run.
I get an error that $ is not defined.
Unless I use
window.onload = function(e){
$(document).ready(function () {
$('.js-slick').slick({
autoplay: true,
autoplaySpeed: 5000,
dots: true,
fade: true,
speed: 1000
});
});
};
The problem is, this slider is the first thing on the page, so I can not wait until everything is loaded, as it takes way too long.
I cannot change the order of my script tag or make sure the jQuery link is properly placed in the head an so on.
I know that other sliders on the page work just fine, and I also know that this particular one works fine once the proper files are loaded
There must be a way to check if $ is defined, keep checking until it is, and then run the script once confirmed.
use a waiter:
setTimeout(function wait(){
if(!window.$) return setTimeout(wait, 100);
// do stuff needing $ here:
console.info("$=", $);
}, 100);
var timer = setInterval(checkjQuery, 5000);
function checkjQuery() {
if(window.jQuery) {
clearInterval(timer);
callSlick();
console.log('jQuery is loaded');
return;
} else {
console.log('waiting');
}
}
function callSlick() {
$('.js-slick').slick({
autoplay: true,
autoplaySpeed: 5000,
dots: true,
fade: true,
speed: 1000
});
};
Similar to DanDavis solution
IMO the answer from #dandavis solves the issue the simplest.
I'd like to offer a way to make a simple "wait-er" re-usable and show how to integrate this with the code you shared.
A re-usable wait-er:
function waitUntil(getResource, callback) {
// Copied from #dandavis' answer
setTimeout(function wait() {
const resource = getResource();
if(!resource) return setTimeout(wait, 100);
callback(resource);
}, 100);
}
window.onload = function(e) {
waitUntil(function () { return window.$; }, function () {
$(document).ready(function () {
$('.js-slick').slick({
autoplay: true,
autoplaySpeed: 5000,
dots: true,
fade: true,
speed: 1000
});
});
});
};
Or if you wanted to, you could make it promise-based:
function waitUntil(getResource) {
return new Promise((resolve, reject) => {
setTimeout(function wait() {
const resource = getResource();
if(!resource) return setTimeout(wait, 100);
resolve(resource);
}, 100);
});
}
window.onload = function(e) {
waitUntil(function () { return window.$; })
.then(function () {
$(document).ready(function () {
$('.js-slick').slick({
autoplay: true,
autoplaySpeed: 5000,
dots: true,
fade: true,
speed: 1000
});
});
});
};

Velocity.js: Animate second set of objects after first set finishes

I'm trying to animate a series of SVG objects. Here's the basic goal: The first set of 4 objects animates in, then out, and then the next set of objects animate in. While the first 2 queue up just fine, I'm not sure of the best method to get the second set to wait until the first set finishes.
Here's my code:
JS:
$( document ).ready(function() {
$('.dt0,.dt1,.dt2,.dt3').velocity("transition.perspectiveRightIn", {stagger: 200, drag: true });
$('.dt0,.dt1,.dt2,.dt3').velocity("transition.perspectiveRightOut", {stagger: 200, drag: true });
$('.tr0,.tr1,.tr2,.tr3').velocity("transition.perspectiveRightIn", {stagger: 200, drag: true });
})
UPDATE: Here's my solution, but I'm bothered by utilizing a delay rather than a queuing method.
$( document ).ready(function() {
$('.dt0,.dt1,.dt2,.dt3').velocity("transition.expandIn", {stagger: 200, drag: true });
$('.dt0,.dt1,.dt2,.dt3').velocity("transition.expandOut", {stagger: 200, drag: true, delay: 1000 });
$('.tr0,.tr1,.tr2,.tr3').velocity({opacity: 0}, {duration:0 });
$('.tr0,.tr1,.tr2,.tr3').velocity("transition.expandIn", {stagger: 200, drag: true, delay: 3000 });
})
Use the callbacks?
$('.dt0').velocity({
opacity: 0 //or animation name
}, {
complete: function(elements) {
$('.dt1').velocity({
opacity: 0 //or animation name
}, {
complete: function(elements) {
//... the others
},{duration:200, delay:2000 } //2s delay
});
},{duration:1000 }
});

jQuery deferred - when multiple timeout tasks have completed

Having a few teething problems with $.deferred, $.when and $.done.
I'm calling a method which has a couple of tasks inside on a timer. I'm looking at getting a callback when everything inside this method has completed, including the stuff in timers, so started looking at $.when() and $.done() to achieve this.
The problem I am getting is the function is firing before the tasks have completed, immediately as the method is called. So, I started playing with $.deferred and resolve(), but haven't managed to get anything working. Without the timers, I can do it.
This is where I call the method:
$.when(cover.start()).done(function() {
console.log("Cover has started.");
});
This is the entire method:
return {
other: function() {},
start: function() {
var dfd = $.Deferred();
el.filter.animate({
"opacity": "0.6", "filter": "alpha(opacity=60)"
}, 2000, "easeInOutCirc", function() {
el.share.removeClass('fa-spin');
setTimeout(function() {
el.share.removeClass('fa-cog').addClass('fa-bars');
},1000);
setTimeout(function() {
el.scroll.animate({
"opacity": "1",
"bottom": "40px"
}, 1200, "easeOutBounce", function() {
var pulseOptions = { opacity: "0" };
setTimeout(function() {
el.scroll.pulse(pulseOptions, {
duration : 400,
pulses: 3,
interval: 500,
returnDelay: 800
});
}, 2000);
dfd.resolve();
});
}, 2000);
return dfd.promise();
});
}
} // end return
As you can see, after my original attempt failed, I added dfd.resolve() to where I want the callback and tried to return the promise. However, the function still fires too early. Where am I going wrong?
The problem is, you need to return promise from the start method
return {
other: function () {},
start: function () {
var dfd = $.Deferred();
el.filter.animate({
"opacity": "0.6",
"filter": "alpha(opacity=60)"
}, 2000, "easeInOutCirc", function () {
el.share.removeClass('fa-spin');
setTimeout(function () {
el.share.removeClass('fa-cog').addClass('fa-bars');
}, 1000);
setTimeout(function () {
el.scroll.animate({
"opacity": "1",
"bottom": "40px"
}, 1200, "easeOutBounce", function () {
var pulseOptions = {
opacity: "0"
};
setTimeout(function () {
el.scroll.pulse(pulseOptions, {
duration: 400,
pulses: 3,
interval: 500,
returnDelay: 800
});
}, 2000);
dfd.resolve();
});
}, 2000);
});
//need to return from start
return dfd.promise();
}
} // end return
Not fishing to steal APJ's rep' but out of interest, you could avoid callback hell by exploiting .delay() and .promise(), both of which relate to the default "fx" animation queue.
Something along the following lines would fix the problem, and would be more readable :
//animation maps
var maps = [];
maps[0] = { 'opacity':0.6, 'filter':'alpha(opacity=60)' };
maps[1] = { 'opacity':1, 'bottom':'40px' };
maps[2] = { 'opacity':0 };
maps[3] = { 'duration':400, 'pulses':3, 'interval':500, 'returnDelay':800 };
//animation functions
var f = [];
f[0] = function () {
return el.filter.animate(maps[0], 2000, "easeInOutCirc").promise();
};
f[1] = function () {
return el.share.removeClass('fa-spin').delay(1000).promise();
};
f[2] = function () {
return el.share.removeClass('fa-cog').addClass('fa-bars').delay(1000).promise();
};
f[3] = function () {
el.scroll.animate(maps[1], 1200, "easeOutBounce").promise();
}
f[4] = function () {
return el.scroll.delay(2000).promise();//delay() could be called on any element. `el.scroll` is arbitrary.
};
f[5] = function () {
el.scroll.pulse(maps[2], maps[3]);
};
return {
other: function () {},
start: function () {
//animation sequence
var p = f[0]().then(f[1]).then(f[2]).then(f[3]);
p.then(f[4]).then(f[5]);
return p;//<<<< and here's the all important return
}
}
Not sure this is 100% correct - might need some work.
It's worth noting that there are performance pros and cons with this approach :
Pros: Reusable animation maps; Reusable functions;
Cons: More liberal use of promises will cause a larger memory spike.

jQuery animate not firing callback function

I have the following jQuery animate function:
$myDiv.animate({ "left": "0%" }, { duration: 1000, easing: 'easeInOutExpo' },
function () {
alert('hi');
}
);
The animation itself works. $myDiv slides with the easeInOutExpo effect, as desired. However, the callback function is never fired. To test it, I changed the callback to just alert("hi");, as you can see above. Still doesn't work.
What could I be doing wrong?
Try this
Demo: jsFiddle
$("#myDiv").animate({ "left": "0%" }, { duration: 1000, easing: 'easeInOutExpo' ,
complete:function () {
alert('hi');
}
}
);
There are a couple of things that need fixing here:
Make sure you've included jQuery UI in your code, because easeInOutExpo is not part of the standard jQuery library.
Your syntax is wrong: you're mixing up the two different options for the animate() function.
It's either
$(element).animate(properties [,duration] [,easing] [,complete]);
or
$(element).animate(properties, options)
where options is an object formatted like this:
{
duration: number,
easing: string,
complete: function,
}
You've gone with the second option, so you need to format it properly to use the complete attribute of the options object for your function:
$myDiv.animate({
"left": "0%",
}, {
duration: 1000,
easing: "easeInOutExpo",
complete: function () {
alert('hi');
},
});
Demo
Alternatively, you could use the first format option:
$("#myDiv").animate({
"left": "0%",
}, 1000, "easeInOutExpo", function () {
alert('hi');
});
Demo
One way is use of JQuery Promise
$myDiv.animate({ "left": "0%" }, { duration: 1000, easing: 'easeInOutExpo' }).promise().done(function(){
alert('hi done');
});

Push content javascript content

How can i push my content the same as another content? OK i know the question seems kind of vague but what I want to do is push my content like the way another script is pushing its content. What is doing when clicking the button it will push the content from the side causing the div to contract. I am not too far so maybe somebody can help. This is the script that works:
$(function(){
var $trigger = $(".icon-menu-2");
var $menu = $(".c_left");
$trigger.toggle(function show() {
$menu.animate({ width: 185, marginLeft: 0, display: 'toggle'}, 'slow');
$(".c_right").animate({ marginLeft:185, display:'toggle'}, 'slow');
}, function hide() {
$menu.animate({ marginLeft: -185, display: 'toggle'}, 'slow');
$(".c_right").animate({ marginLeft:0, display:'toggle'}, 'slow');
});
})
http://jsfiddle.net/Ndvbn/2/
Here is the script that needs just a small touch up so that it will push the content just like the script above does when clicking on test. Here is the script:
var timer;
$("#slideout").animate({right:'0px', queue: false, duration: "slow"}, function () {
timer = setTimeout(function () {
$("#slideout").animate({right:'-280px'}, {queue: false, duration: "slow"})
}, 500);
});
$("#clickme2").click(function () {
if ($("#slideout").css("right") == "-280px"){
$("#slideout").animate({right:'0px'}, {queue: false, duration: 500}, function () {
clearTimeout(timer);
});
} else {
$("#slideout").animate({right:'-280px'}, {queue: false, duration: 500}, function () {
clearTimeout(timer);
});}
});
http://jsfiddle.net/5UpHk/4/
Can anybody post the code so that my second script will push the content to the left?
This script below will push the content to the left. I made a couple of changes to the CSS as well. Check out the demo and code here: http://jsfiddle.net/BdKhW/1/
var timer;
$("#slideout").animate({width:'275px'}, {queue: false, duration: "slow"}, function () {
timer = setTimeout(function () {
$("#slideout").animate({width:0}, {queue: false, duration: "slow"})
$(".c_left").animate({marginRight:0}, {queue: false, duration: "slow"})
}, 2000);
});
$(".c_left").animate({marginRight:'275px'}, {queue: false, duration: "slow"})
$("#clickme2").click(function () {
$("#slideout").animate({width:0}, {queue: false, duration: "slow"})
$(".c_left").animate({marginRight:0}, {queue: false, duration: "slow"})
});

Categories

Resources