Jquery Animate Infinite Loop: How to Avoid Stack limit - javascript

I've got some Jquery that simply zooms in and out non-stop for a banner image. When I run this, I get a stack limit error in the browser. It still runs, but is there a way to make it only load into the stack "just in time"? When looking at the stack it loads zoomIn() and zoomOut() over and over again on the initial load until it hits the limit, page loads are really slow because of it.
$(document).ready(function(){
$bannerImg = $('.post-picture img')
function zoomIn(){
$bannerImg.animate({
width: 1500,
}, 50000,'linear');
$bannerImg.promise().done(zoomOut());
}
function zoomOut(){
$bannerImg.animate({
width: 1500,
}, 50000,'linear');
$bannerImg.promise().done(zoomIn());
}
zoomIn();
});
Update: Thanks for the answers. Using done(ZoomOut/ZoomIn) worked.

.done() expects a function reference - the function pass will be executed as soon as the promise object is resolved. Instead, you're just calling the functions (which return nothing, undefined, anyways). If you do this, the functions will continually call each other, acting as an infinite loop. Use this:
$bannerImg.promise().done(zoomOut);
// and later:
$bannerImg.promise().done(zoomIn);
DEMO: http://jsfiddle.net/G6uWs/
(I had to change the numbers to make it usable)
Reference:
http://api.jquery.com/deferred.done/

You're calling the function in .done() instead of passing it as a parameter.
$bannerImg.promise().done(zoomOut());
should be
$bannerImg.promise().done(zoomOut);
and
$bannerImg.promise().done(zoomIn());
should be
$bannerImg.promise().done(zoomIn);

Looks like you are causing an infinite loop. Luckily, the jQuery has a complete callback that you can leverage to prevent the infinite loop.
Non-stop zoom in and out banner
$(document).ready(function () {
$bannerImg = $('.post-picture img');
function zoomIn() {
$bannerImg.animate({
width: 1500
}, {
duration: 10000,
complete: function () {
zoomOut();
}
});
}
function zoomOut() {
$bannerImg.animate({
width: 100
}, {
duration: 10000,
complete: function () {
zoomIn();
}
});
}
zoomIn();
});
*Source: * jsfiddle

Related

Unsure on why .load() complete event isn't working

I'm unsure on why this isn't working:
$(document).ready(function() {
setInterval(RefreshDiv, 2000);
})
function RefreshDiv(){
$('#box').load('messages.php #box', function() {
$('#box').on('load', function() {
$('#box').scroll(0, 50);
});
});
}
The tags are correct and the .load() part works every two seconds but I don't understand why my complete event to scroll down 50px isn't working?
I've also tried another method to scroll:
var log = document.querySelector('#box');
log.scrollTop = log.scrollHeight - log.clientHeight;
but this also doesn't execute on load
Edit #1
jQuery($ => {
setInterval(RefreshDiv, 2000);
})
function RefreshDiv() {
$('#box').load('messages.php #box', () => {
$('#box').scrollTop(50);
});
}
The load event only fires on certain elements such as img and the window object. As such I presume #box is not one of them.
You don't actually need the load event handler anyway as the callback itself runs when the load() method completes its request. Try this:
jQuery($ => {
setInterval(RefreshDiv, 2000);
})
function RefreshDiv() {
$('#box').load('messages.php #box', () => {
$('#box').scrollTop(5000);
});
}
It's also worth noting that sending AJAX requests every 2 seconds is not ideal, as it will not scale as you have more concurrent visitors to your site, and can lead to server performance problems. There's likely to be a much better alternative, depending on what it is you're doing.

Extjs SlideOut panel

I am trying to slide out a panel and then hide it using extjs. The slideout is working fine but as soon as I add the hide function it stops working. How do I fix this.
My function is as below.
toggleSidebar : function () {
var sidebar = this.getSidebar();
if(sidebar.hidden){
sidebar['show']();
}else{
sidebar.el.slideOut('l', {
easing: 'easeOut',
duration: 200,
scope: this,
callback: this.onSidebarAnim()
});
sidebar['hide'](); // Slide works if I remove this line.
}
},
Animation is an asynchronous process, and slideOut does not block until animation has finished; in fact your code starts to animate the panel and then hides it immediately. That is why it's not working the way you expect it to.
The solution is to hide the panel after the animation has finished. That is what callback is for, except that in your original code instead of passing the function in callback property, you're calling it and assigning the result of its execution to the callback property. That is not going to work, and in fact it's going to blow up with "foo not a function" exception.
toggleSidebar: function () {
var sidebar = this.getSidebar();
if (sidebar.hidden) {
sidebar.show();
}
else {
sidebar.el.slideOut('l', {
easing: 'easeOut',
duration: 200,
scope: this,
// Pass the function itself, note no parentheses:
callback: this.onSidebarAnim
});
}
},
onSidebarAnim: function() {
this.getSidebar().hide();
...
}

clearInterval clearing two intervals

I can't run clearInterval for my functions. I use them to scroll the window by firing setInterval with function that fires scrollLeft. The code:
function scrollSpan() {
$('nav#scrolling').children().css('width',config.windowWidth/10+'px');
var inter;
$('nav#scrolling').children('span').hover(function() {
var value;
if($(this).is('.scrollLeft')) {
value = '-=50'
} else {
value = '+=50'
}
inter = setInterval(function() {
$('body, html').animate({
scrollLeft: value
}, 50);
},0)
})
$('nav#scrolling').children('span').mouseleave(function() {
clearInterval(inter)
})
}
Problem is, when mouseleave is triggered, interval doesn't stop.
Fiddle: http://jsfiddle.net/FpX4M/
You are using hover where you should be using mouseenter. When only one handler is passed to hover that handler is called both on enter and leave. So your hover is called twice (once entering and once leaving) but your mouseleave is only called once. This is why even though one interval is cleared, the other remains.
See the documentation, in particular the signature added in v1.4 which takes only a single handler (scrolldown).
EDIT: Jsfiddles with proof:
http://jsfiddle.net/FpX4M/1/
Open your console and see that the handlers trigger twice and that interval continues.
http://jsfiddle.net/FpX4M/2/
In the console you will now see only one firing of the handler and then the intervals stop on leave.
Your whole scope is a little wonky. Try something like this:
var inter;
function scrollSpan() {
$('nav#scrolling').children().css('width',config.windowWidth/10+'px');
}
$('nav#scrolling').children('span').hover(function() {
var value;
if($(this).is('.scrollLeft')) {
value = '-=50'
} else {
value = '+=50'
}
inter = setInterval(function() {
$('body, html').animate({
scrollLeft: value
}, 50);
},0)
});
$('nav#scrolling').children('span').mouseleave(function() {
clearInterval(inter)
});
You need to make sure the inter variable is accessible outside of the function. Also, generally, state functions shouldn't be assigned within functions unless you're changing them rapidly - and it doesn't look like you're detaching them anywhere. The only things that need to be in the function are things that will be repeated. Maybe add a clearInterval(inter); right before your inter = setInterval... to make sure no old intervals persist.

Jquery css(width, 0) not working if the element is hidden

I have created a quick jsfiddle here showing it not working.
The problem I have is with the slide up. I want it to work so that it only sets the width to 0 after the slideup has finished. The obvious callback function does not seem to be getting called after the slideup has finished.
I would like it to work like this:
Shows the red box by sliding down and increasing the width together.
Click again and the box slides up then sets the width the 0. So that if the user clicks the button again the first animation would appear the same.
var $foo = $("#elm");
$("#btn").toggle(function() {
showDropDown();
}, function() {
hideDropDown();
});
function showDropDown(){
$foo.slideDown({duration:500, queue:false}).animate({"width": 400}, 250);
}
function hideDropDown(){
$foo.slideUp({duration:800, queue:false},function(){
$foo.css({"width": 0});
});
}
UPDATE:
The strange thing is that if I add a alert() into the callback function for slidedown it never gets called.
Edit: Sorry for the first answer, didn't pay attention.
The problem is that the callback is not executed, because you don't give the parameters according to the API, and the callback is not "wired" in.
Instead, you can use the promise().done(...) combination to achieve the objective you wanted.
So, you should modify your hideDropDown method as follows:
function hideDropDown(){
$foo.slideUp({duration:800, queue:false}).promise().done(function(){
$foo.css("width", "0px");
});
}
From the jQuery docs:
"The .promise() method returns a dynamically generated Promise that is resolved once all actions of a certain type bound to the collection, queued or not, have ended."
Maybe you just need to use animate to reset width to 0 like this:
var $foo = $("#elm");
$("#btn").toggle(function() {
showDropDown();
}, function() {
hideDropDown();
});
function showDropDown(){
$foo.slideDown({duration:500, queue:false}).animate({"width": 400}, 250);
}
function hideDropDown(){
$foo.slideUp({duration:800, queue:false}).animate({"width": 0}, 1);
}
or set width to 0 like this:
function hideDropDown(){
$foo.slideUp({duration:800, queue:false}).width(0);
}
Why not chain .animate() after .slideUp()?
$foo.slideUp({duration: 800, queue: false}).animate({"width": 0}, 800);

Jquery: execute a function when another is finished

I need your help.
I want to animate with jquery a panel. It must get open on click on buttons (function OPEN_PANEL) and load different php pages on it, then close when click on a div with a class "close" (function CLOSE_PANEL).
This works fine, the problem is when I want to open a different panel when one is already open. It should close the open one and after that open the last I selected, but it looks like it executes both functions at the same time. How can I solve the problem?
This is the javascript code:
var panel_is_open=0;
var last_open_panel="";
function animation_open_panel(id){
window_height=$(window).height()*0.85;
$("#"+id+"_button").css({'background-color':'rgba(255,255,255,0.8)', 'box-shadow':'0px 5px 10px #39C', '-webkit-box-shadow':'0px 5px 10px #39C'});
$("#main_panel").show().animate({ height: window_height+"px" }, 1500)
.animate({ width: "90%" },1000);
$("#main_panel").queue(function(){
$(".close").show();
$("#page_container").hide().load(id+"/"+id+".php",function(){
$("#page_container").fadeIn(1000);
});
$(this).dequeue();
});
}
function animation_close_panel(){
$("#page_container").fadeOut(1000, function(){
$("#main_panel").animate({ width: "637px" }, 1000)
.animate({ height:"0px" }, 1500, function(){
$(".close").hide();
$("#"+last_open_panel+"_button").css({'background-color':'', 'box-shadow':'', '-webkit-box-shadow':''});
});
});
}
function close_panel(){
if(panel_is_open==1){
animation_close_panel();
panel_is_open=0;
}
}
function open_panel(id){
if(panel_is_open==0){
animation_open_panel(id);
last_open_panel=id;
}
else if(panel_is_open==1){
if(id!=last_open_panel){
close_panel();
open_panel(id);
}
}
panel_is_open=1;
}
Thank you very much in advance for your help.
Thank you very much for your suggestions, but I couldn't solve the problem with both solutions. I am mistaking something but I can't understand what.
This is my code:
function close_panel(){
if(panel_is_open==1){
// animations here
panel_is_open=0;
}
}
function close_open_panel(next){
close_panel();
next();
}
function open_panel(id){
if(panel_is_open==0){
// animations here
last_open_panel=id;
panel_is_open=1;
}
else if(panel_is_open==1){
if(id!=last_open_panel){
close_open_panel(function(){
open_pannel(id);
});
}
}
}
Any idea where I am mistaking?
Thanks.
If you're after jQuery specific solution then look up Deferred Object:
jQuery.Deferred(), introduced in version 1.5, is a chainable utility object that can register multiple callbacks into callback queues, invoke callback queues, and relay the success or failure state of any synchronous or asynchronous function.
You can use callbacks in the functions, e.g.
function open(callback)
{
// do stuff
callback(some_value);
}
Then you can use it by doing this:
open(function(value)
{
// anything here will be executed
// after the function has finished
});
The value in the callback() and function(value) is optional, you can return a straightforward function instead of passing a value that is to be called back, however, it's useful for some certain functions that requires asynchronous callbacks.
More information about callback functions:
Getting a better understanding of callback functions in JavaScript

Categories