I'm trying to get a javascript function to run only once. I've seen this question has been asked before, e.g. Function in javascript that can be called only once, but I can't get the solutions in here to work. I'm not sure if it's because I've got nested functions, or whether there's something I'm missing. Essentially, I'm trying to run a function which, when a webpage is scrolled, it:
- runs a little animation on a canvas in the header
- reduces the size of the header
- leaves it at that
But when there is any subsequent scrolling, the animation keeps re-running. Here's a summarised version of the non-working code:
$(document).on("scroll",function(){
var arrange_title = function(){
//some code
};
if($(document).scrollTop()>0){
arrange_title();
arrange_title = function(){};
setTimeout(function(){
$("header").removeClass("large").addClass("small");
},1000);
}
});
I've also tried declaring a global variable, setting it to "false" in a "window.onload" function, then set it to true in an if function that runs the animation (the if function running only if the variable is false), but that doesn't stop it either. Thoughts?
What you're looking for is something along the lines of listenToOnce where the listener fires the one time, but never again. This could be modified to a number of calls, but the logic is like so:
Register the listener.
Then once the listener fires, remove it.
See .off
$(document).on("scroll",function(){
var arrange_title = function(){
//some code
};
if($(document).scrollTop()>0){
arrange_title();
arrange_title = function(){};
setTimeout(function(){
$("header").removeClass("large").addClass("small");
// $(document).off('scroll'); // or here
},1000);
}
$(document).off('scroll'); // remove listener, you can place this in the setTimeout if you wish to make sure that the classes are added/removed
});
Don't use a time out. That is why you are getting in trouble. Declare a variable outside of your function using var, that will make it global. Your code should be inside of a check for that variable. Before executing your code the first time but inside of the check, change that variable so that the code will never run again.
Try avoid setTimeout. Almost all animation can be watched for end.
function doHeaderAnimation() {
return $('header').animate();
}
function makeHeaderSmall() {
$("header").removeClass("large").addClass("small");
}
function handleScroll(event) {
if ($(document).scrollTop() > 0) {
doHeaderAnimation().then(makeHeaderSmall);
$(document).off("scroll", handleScroll);
}
}
$(document).on("scroll", handleScroll);
Related
This is my code:
var b;
while(!b){
setTimeout(function(){
alert('sss')
b=1;
}, 500);
}
and it will not alert 'sss'
What can i do?
Updated:
I want to get bounds on google maps v3:
function get_bounds(){
var bounds_;
while(!bounds_){
setTimeout(function(){
bounds_=map.getBounds();
if(bounds_){
var leftBottom=[bounds_.getSouthWest().lat(),bounds_.getSouthWest().lng()]
var rightTop=[bounds_.getNorthEast().lat(),bounds_.getNorthEast().lng()]
return [leftBottom,rightTop];
}
}, 500);
}
}
updated2:
hi patrick dw, i don't know why , but your code doesn't work:
var b;
function waitForB() {
setTimeout(function(){
if(!b)
waitForB();
else
alert('sss');
}, 500);
}
waitForB()
updated3:
it is ok now :
var b;
function waitForB() {
setTimeout(function(){
if(!b){
waitForB();
b='ss';
}
else{
alert('sss')
}
}, 500);
}
waitForB()
JavaScript in web browsers is run in a single thread. When you call setTimeout(), it won't spawn a new thread. This means that setTimeout() will not execute until all of your main code has finished executing.
For this reason, you will end up with an infinite loop, because your loop condition is dependant on the execution of the setTimeout() callback.
Here's an interesting article on how JavaScript timers work:
How JavaScript Timers Work by John Resig
UPDATE:
Further to the updated question, you may want to listen to the bounds_changed event instead. I am not sure how you are planning to use your get_bounds() function, but you may want to refactor your logic to use an event listener instead:
google.maps.event.addListener(map,'bounds_changed', function () {
// The code here is triggered when the bounds change
});
That code is going to burn CPU time and memory by scheduling timeouts to happen. Think about it: you're loop condition is "b" becoming truthy. How is that going to happen? Only when a timer event fires. Will that happen? No, because you're eating the whole machine scheduling zillions more timeouts.
This sort of situation has as a tell-tale sign the effect of warming up the room you're sitting in.
I don't know what effect you're trying to get. Why not start by just the setTimeout() call and see how that goes. Maybe you could describe more about what it is you're trying to do.
Maybe you will want to use setInterval instead of setTimeout.
When b is changed, alert shows up.
var b = false;
(function () {
var intervalId;
function wait() {
if (b) {
clearInterval(intervalId);
alert('sss');
}
}
intervalId = setInterval(wait, 500);
})();
It is more intuitive and it doesn't mess with global variables too much.
HINT: Put semicolon after every statement if you are not sure where to omit safely.
This problem can now be solved correctly using the idle rather than the bounds_changed event listener:
google.maps.event.addListener(map, 'idle', function() {
updateStuff();
});
This event is fired when the map becomes idle after panning or zooming.
http://code.google.com/apis/maps/documentation/javascript/reference.html
It also fires after the map is first rendered, so this is probably the only event listener you need on your map in order to keep it up to date.
I basically want to know how global variables work in a javascript/JQuery environment. I am most familiar with a language called processing which I've been told is java-based. I expected variables in javascript and JQuery to behave like the ones in processing, but they do NOT work as I expect and I cannot for the life of me wrap my head around it.
I have a very simple example made up to illustrate my confusion:
var what="";
$(document).ready(function(){
$("p").click(function () {
what="p";
});
if(what=="p"){
alert(what);
}//end if
});//end doc ready
In processing, this would work because the 'what' variable is global and as it is changed by clicking on a paragraph, the if statement should be continuously checking to see if 'what'=='p', and trigger the alert. But that is not what happens-- 'what' only seems to be updated WITHIN the click function, even though it is a global variable, so when it comes to the if statement, 'what' still equals "" instead of "p".
If someone could explain why this happens, I will be very grateful!
The if statement only runs once when the DOM is first ready. It is not running continuously. If you want it to run during the click handler, then you would use this code:
var what="";
$(document).ready(function(){
$("p").click(function () {
what="p";
if(what=="p"){
alert(what);
}//end if
});
});//end doc ready
the if statement should be continuously checking to see if 'what'=='p', and trigger the alert.
Why? None of your code produces that functionality. If you want that to happen, you can use setInterval():
setInterval(function() {
if(what=="p") {
alert("what");
}
}, 500); // executes function every 500 milliseconds
But that is not what happens-- 'what' only seems to be updated WITHIN the click function, even though it is a global variable
No, your what variable is being updated globally. You just don't notice because you made false assumptions about the if functionality (it's only being called once).
I'm trying to find a way to stop a function at a certain point until something does not have a specific class anymore. I cannot change the place where this class is being assigned and removed because it's a plugin.
I was thinking of doing something like this
function DoSomething() {
while ($('div.divControl').hasClass('playing'))
{
//Wait here
}
};
Is this the correct way to go?
This will block so the element will never be changed, as no other code will execute.
What you need to use is an interval:
var interval = setInterval(DoSomething, 500);
function DoSomething() {
if ($('div.divControl').hasClass('playing'))
{
// Do something
clearInterval(interval);
}
};
This will execute the function every half second. The interval will be cancelled after the function succeeds.
No, that will just hang the browser as it goes into an infinite loop.
Your best bet (as best I can think at the moment anyhow) is to do a setTimeout on the function and have it check to see if it your div still has the class every quarter of a second or so.
Still, not nice at all =[
I'm trying to execute a piece of code each time a specific element has a certain class in jQuery.
The problem I'm experiencing is that the code only executes once, then seems unactive. I'm using an if statement, but I also tried while. When I tried while the nothing really worked so that wasn't a good idea. Is there any solution? Here's the code:
if($(".slide:first").hasClass("active-slide")) {
$(".prev").hide();
$(".next").click (function () {
$(".prev").show();
});
}
JQuery queries are performed only once. You could execute it in an interval:
var checkPage = function(){
if($(".slide:first").hasClass("active-slide")) {
$(".prev").hide();
$(".next").click (function () {
$(".prev").show();
});
}
}
var intrvl = setInterval( checkPage, 300 );
This might work, but it could become really slow on big pages.
I'd rather attach the checkPage() function to the slide change event.
Wrap it in a each(), so that it executes every time slider:first occurs.
$(".slide:first-child").each(function(){
// your function goes here
});
EDIT: :first only targets the first instance of it on the page, so it will only fire one time anyway. You are probably looking for :first-child.
I am using jquery to slide up an down a div.
For some reason setTimeout is not working (looks like a function scope issue).
Not able to figure out what is wrong with the below code.
(both functions are inside $(document).ready(function(){ } )
$('.slider-thumb').click(function(){
var source = $(this).attr("src");
$('#image_view').css('background-image',"url("+source+")");
$('#image_view').slideDown(1000, calbck);
initiate_timeout();
function calbck(){}
});
function initiate_timeout(){
var time_out = setTimeout(function() {
$('#image_view').slideUp(1000, calbck);
},2000);
}
Indenting the source code shows that you indeed have some scoping issue. The calbck function is private to the click handler function, and is thus not visible to initiate_timeout function. Either make calbck a top-level function, or make initiate_timeout a function local to the click handler function.
$('.slider-thumb').click(function() {
var source = $(this).attr("src");
$('#image_view').css('background-image',"url("+source+")");
$('#image_view').slideDown(1000, calbck);
initiate_timeout();
function calbck(){}
});
function initiate_timeout(){
var time_out = setTimeout(function() {
$('#image_view').slideUp(1000, calbck);
}, 2000);
}
You have some JS errors and scoping issues. Why would you ever have debugging turned off when trying to troubleshoot an error? Change your code to this:
$('.slider-thumb').click(function(){
var source = $(this).attr("src");
$('#image_view').css('background-image',"url("+source+")");
$('#image_view').slideDown(1000);
setTimeout(function() {
$('#image_view').slideUp(1000);
},2000); // will start 2 seconds after slideDown starts (which is 1 second after it completes)
});
or even better, use the completion function of the first animation to set the timer:
$('.slider-thumb').click(function(){
var source = $(this).attr("src");
$('#image_view').css('background-image',"url("+source+")");
$('#image_view').slideDown(1000, setTimeout(function() {
$('#image_view').slideUp(1000);
},1000)); // will stay open for 1 second before sliding up again
});
Or, even better, using jQuery's delay/queuing, you can do this:
$('.slider-thumb').click(function(){
var source = $(this).attr("src");
$('#image_view').css('background-image',"url("+source+")");
$('#image_view').slideDown(1000).delay(1000).slideUp(1000); // delay 1 sec between effects
});
The calbck you were trying to pass to slideUp was not defined in the scope you were using it (inside of initiate_timout()). It was private to your click handler.
jQuery probably has better ways to chain effects than using your own timer, but I see no reason why this code shouldn't work if it matches your HTML.
Note: if your background image wasn't already pre-cached, it may not be loaded right away when your slideDown starts.
Are you getting any errors? You're passing an undefined value as calbck to slideUp in the setTimeout function.
It works for me (with my modified organization): http://jsfiddle.net/v3cjG/1/
And when I run your exact code ( http://jsfiddle.net/v3cjG/2/ ) I get an error for "Can't find variable calbck"--the var I said you were passing without defining.