JavaScript setTimeout will not die. - javascript

I made a photogallery and is AJAX powered. Below are the relevant pieces of code.
if (isset($_POST['stopss'])){
echo'<script type="text/javascript">
clearTimeout(setss);
</script>';
}
if (isset($_POST['startss'])){
?>
<script type="text/javascript">
var setss = window.setTimeout('delayer(\''+src+'\',\''+dir+'\',\''+title+'\',\''+width+'\',\''+height+'\',\''+img+'\')',5000);
</script>
<?
}
The delayer function passes params for a new modal box window (new AJAX request).
The problem as I see it. The timeout gets started when a user clicks the Slideshow button.
Even though they press the Stop Slideshow button (this reloads the AJAX page), the timer is still running (in the background) and in clearTimeout(setss), setss is no longer available (because of the new AJAX page request) so the clearTimeout fails to clear.
Is there any way to kill the timeout running in the background? Even if the modalbox window is closed, 5 seconds later it opens and happily keeps playing the slide show.
btw, this is the modalbox call.
Modalbox.show('../b-gal-scripts/ajaxgal.php', {title: title, overlayOpacity: .1, width: width, height: height, method: 'post', params: {src: next, dir: dir, img: img, ssimg: next} }); return false;

The problem
When you reload the page, the old JavaScript code (for the previous request) stops running and the new one is executed.
The problem is that you first call clearTimeout(setss) and later define what setss really is.
A couple additional problems
A few tips:
Do not mix quoting styles, you are creating a mess this way.
If you want quotes within quotes, just use the fact there are different quotes like that:
var setss = window.setTimeout("delayer('"+src+'","'+dir+'","'+title+'","'+width+'","'+height+'","'+img+'")',5000);
...and preferably get used to event-based programming and closures when coding JavaScript, like that:
var sets = window.setTimeout(function(){
delayer(src, dir, title, width, height, img);
}, 5000);
Final version of your code
Your code should look like the following:
if (isset($_POST['startss']) && !isset($_POST['stopss'])){
?>
<script type="text/javascript">
var setss = window.setTimeout(function(){
delayer(src, dir, title, width, height, img);
}, 5000);
</script>
<?
}
It makes use of the fact, that you do not need to first output a script that is executed only to immediately stop it if not needed. Just make PHP condition to check if you want it to be executed, the output it.
Second, the JS has been changed from using strings (which is totally messy) to using anonymous functions. Take a closer look into that.
Did it help?

I'm having a little difficulty understanding your problem, but I hope this helps.
When a page is reloaded, the previous instance of the page, and any state associated with it ceases to exist. As a result, the timeout has been stopped. Essentially, if the global setSS doesn't exist, then neither does the timeout to which it refers, therefore, that timeout cannot be responsible for starting the slideshow.
When an active page is served from something like PHP, the work of the server (WRT the creation of any content of the page) is done. No more content can subsequently be written to the page. Your clearTimeout is essentially useless, as it could only clear a timeout that is set during the load of the page. It won't write a new script element into the existing page.
Variables are not shared across pages, so if you are setting the timeout in one page, and clearing it in another, that will not work.
So, in order to clear the timeout, you need to call clearTimeout in the same page (and instance of that page) as the setTimeout, and that call must be executed after the setTimeout.
If none of the above apply to your situation, then is it possible that $_POST['startss'] and $_POST['stopss'] are set in the same request? If so, the new page will create a new timeout after the attempt to clear it, and will therefore display the slideshow.
(as an aside, using a closure to create a function to pass to setTimeout will be more readable than compiling a string that calls a function).

Related

ERROR: 508 (Loop detected)

Well, this isn't like a proper question or anything..
I am just a bit curious, tried searching but couldn't find accurate answer for my problem so I had decided to try out and ask another question out here.
I have this small fun-time project in which you are supposed to keep on clicking a button until you gain the highest clicks among the other players, I have a page ManageClick.php which has the following code in it -
<?
sleep(rand(1,3));
$storage = fopen("TOTAL.txt", "r+");
if (flock($storage, LOCK_EX)) {
$a = fread($storage,filesize("TOTAL.txt"));
$a++;
fseek($storage, 0);
fwrite($storage, $a);
flock($storage, LOCK_UN);
} else {
}
fclose($storage);
?>
The above adds a random delay between 1-3 seconds and then opens a file TOTAL.txt reads it, and adds +1 to the value in the file.
I have a main page Testpage.html which has a simple button and a JQuery function which calls the page ManageClick.php when the button is clicked. However, now the problem arises. Whenever an user rapidly clicks the button in the main page it shows the following error in the Console window-
GET http://domain.tk/ManageClick.php 508 (Loop Detected)
GET http://domain.tk/TOTAL.txt 508 (Loop Detected)
WHERE domain is my website's name.
Any idea what could be causing the following issue? Rapid clicking of the button is one of them which I guess sends multiple requests to the page and causes the 508 error. Also, any possible way in which I could try and fix this error from showing up in the Console?
Also, please note that I am not English... Sorry for it.
PLEASE SEE- Adding the code of my Testpage.html for better quality of help.
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script>
$(function(){
$("img").click(function() {
var test = $("#Clicks").load("TOTAL.txt");
$.post('ManageClick.php');
});
});
</script>
</head>
<body >
<center>
<img src='ABCD.png'>
<div id='Clicks' style=""></div>
</body>
</html>
If I understood, you have a webpage that has a game in which you call a php script to save how many times the user has clicked on a button.
You say the console (I guess chrome's developper tools or the similar) returns a 508 error as "Loop detected". This is the server telling the client that there might be a loop in the code because it gets called too many times. Well... it's not an automated redirect, it's just you calling it tons of times.
According to your explanation of the game, I would rather save the number of clicks in a javascript variable, and send them only after the game ended. This way, you only call the server one time.
On the other hand, your php code assumes that there's only one user of your website. I mean, you save the value in a file, but it doesn't take in account who's game is it.
Think that if you and I opened the website at the same time, you start adding clicks, and then I add another one, but the count gets saved in the same place.
Edit: Code example
jQuery:
var test = $("#Clicks").load("TOTAL.txt");
$("img").click(function() {
test = parseInt(test) + 1;
});
With this, you load the number previously stored and keep count of the clicks. Next you need to build a function that sends the click count after some idle time (or if you have a timeout per round )
I'll assume that if the user spent 3 seconds without clicking, he's gone, so I update the server. The new jquery would be:
var test = $("#Clicks").load("TOTAL.txt");
var wdt;
$("img").click(function() {
wdt = setTimeout(serversync, 3000);
test = parseInt(test) + 1;
});
function serversync(){
$.post('ManageClick.php?count='+test);
}
In this code I added the serversync function. It runs after 3000 ms have gone for the setTimeout(). This timeout gets fired when the user clicks, but if there's a click again, the timer resets.
Finally, you should pass the new count value to the ManageClick.php script. I passed it as the count GET attribute (which you can retrieve in your php by $_GET['count']
Of course, this is not perfect nor safe (I could easily fake a massive count to be sent to the server, but that's another story), but it's the idea
Your webserver is detecting a long running script based on some configuration. I'd bet if you take out the sleep function, it would no longer detect a loop.
Also, if you are expecting the value in TOTAL.txt to increase, be sure to cast as integer.
$a = fread($storage,filesize("TOTAL.txt"));
$a = (integer) $a; // cast as integer
$a++; // now you can increase it
To further mitigate this issue, if you must keep the lseep() function, use the following steps in your html/js:
click_button (call ajax)
disable_button (while ajax is running/php is sleeping)
accept_ajax_return_value (when ajax responds)
re-enable button (after ajax is complete and can go again)

Wait to call a method from the Action page

I'm kinda new to the JSP world.
I have this web application that calls the page below, the result.jsp page.
This page is called after a submit on a form, and the action class generates an image from the createImage method, called by a dispatchParam.
The actual code part is this:
<div id="container_imagem_resultado">
<p>
<img src="<%=pathBase%>access.do?dispatchParam=createImage&time="new Date().getTime();>
</div>
I need to create some trigger so the page waits for 1s before calling the img tag.
How can I do it?
is there a way to dinamically insert into the HTML response page the tag after that 1s wait? Would adding <%Thread.sleep(1000);%> just before the img tag work?
Doing a delay in Java (server-side) on the page, even in a scriptlet, will only result in a delay in sending the page to the client's browser, which results in either a long pause before they receive the page, or (in some cases) a half-loaded page with an odd delay until the rest is loaded.
To delay the loading of something on the client-side, the best answer is to use Javascript. Given that you've tagged the question jquery, I'm going to assume that jQuery is an available option for you.
For that purpose, this answer gives a reasonable example of what to do here:
// jQuery construct; calls this function after the page is ready.
$(document).ready(function () {
// Create the function which loads the image.
function showImage() {
// Gets a handle for the container div, and replaces its HTML contents.
// Not the most elegant, but it works.
$("#container_imagem_resultado").html('<p><img src="<%=pathBase%>access.do?dispatchParam=createImage&time=' + new Date().getTime() + '" /></p>');
}
// Schedule it after 1 second (1000ms).
setTimeout(showImage, 1000);
});
And the HTML:
<div id="container_imagem_resultado">
<p>
<!-- TODO placeholder -->
</p>
</div>
As a side note, isn't 1 second a little long for a loading delay? Unless you intend for there to be a noticeable lag in the image loading, or there's another reason for loading the image after 1 second, you should make it a lot lower, otherwise it might come across as a broken window.

Javascript - Loop keeps running after page refresh

Today one of the weirdest things i have ever seens just happened.
I have a loop in a page that does a synchronous ajax request for each element of the loop. Since it was taking too long i decided to stop the loop by refreshing the page.
When the page loaded i couldn't click on any element of the page so i checked Firebug console and i saw that the ajax calls of the previous loop were still being done (the loop is set to start after i click on a link, so it can't start as soon as page loads).
To stop the loop i had to close the current tab and open my page in a new one.
It's worth mentioning that i have a datatables table on that page and i have enabled the option to save the table state, maybe this is interfering with my code (the loop itself is not part of the datatables initialization, though it uses data from the table).
I have also noticed that some things of the loop aren't being done. It should've changed the page of the datatable by time to time, and it should've written in the javascript console, both things don't happen after i refresh the page, the ajax calls are still going though.
Here's the code where the loop is contained:
//This variable will contain a reference to the datatable
var oTable;
var isWorking=false;
//Change the datatables page
function changeTablePage(oTable, page, clear){
if(clear){
oTable.fnClearTable();
oTable.fnDraw();
}
oTable.fnPageChange(page);
}
//This part is inside a document.ready block
$(document).on("click", ".validation-all-click", function(event){
if(!isWorking){
isWorking=true;
//saves the current page, will be needed for later
var page=oTable.fnPagingInfo().iPage;
var numberPage=0;
var done=false;
while(!done){
console.log("page: "+numberPage);
changeTablePage(oTable, numberPage, false);
var nNodes = oTable.fnGetNodes();
var len=nNodes.length;
$('.validation-click', nNodes).each(function( index, value ){
//id of the element to be validated
var id=$(value).data("rowid");
//Calls the validation url
$.ajax({
url: "/validate?id="+id,
async: false
});
});
//if the datatables page has 0 items, then it's done
if(len==0){
done=true;
}
numberPage++;
}
//goes back to the original page
changeTablePage(oTable, page, true);
isWorking=false;
}
});
I don't want this behavior to happen, but i have no idea on why it's happening and how to prevent it
Thanks for sharing your story. I don't see any specific question in the post, so I'll just comment on whatever I feel like.
As I understand it, browsers aren't required to redraw the UI while JavaScript is running. Since there's a script blocking on a synchronous ajax request, maybe it's to be expected that you don't see the changes to the table until the loop finishes.
Furthermore, it may also be that the browser isn't required to destroy a page while its scripts are running. That would explain why you saw requests in Firebug after refreshing the page--perhaps the previous copy of the page was still running in a hidden state.
edit: valepu reports in a comment, below, that the table does change while the script is running. That's fine. The browser can probably determine that it can redraw the UI during the ajax call (which doesn't affect the JavaScript environment). valepu also clarifies that the visual updates stop after refreshing the page, though the requests continue to go out. This is also consistent with the idea that the browser has just hidden the previous page (until it finishes) and loaded up a new copy of the page when refreshing.
As for how to prevent it: the most reliable way would be to use asynchronous requests, or otherwise yield between requests. Sorry, folks.
I have solved by adding a variable that will stop the loop when i exit the page:
var stopLoop=false
$( window ).unload(function() {
stopLoop=true;
});
both loops will now check if this variable is false before executing the code inside the loop.
This works on Firefox but not on Chrome though.
-- EDIT --
In the end i have solved by editing the code in order to make the ajax calls asynchronous and using the callback functions to continue the cycle, though it was no simple task (some days later i found a new solution that allowed me to do all that i needed in a single call when i found out how to recover the parameters used by datatables to retrieve data, but this has nothing to do with the original question). So, for future references: expect this "weird" behaviour when making a loop with ajax synchronous calls

What to do if $(window).load(function(){}); is too early

I need to trigger a piece of code after every single bits are done downloading. The script works if injected after everything is loaded, but how do I trigger that automaticly?
My script is:
var divId = "jwplayer-0_wrapper";
if ($('#' + divId).length == 1) {
myReg = /https?:\/\/www\.youtube\.com\/watch\?v=[^"]+/;
var plainText = $('#' + divId).parent().children('script').text();
var url = plainText.match(myReg);
if (url !== null) {
window.location = url;
};
};
It is used to skip certain site that decide to use the JW player witch I find horribly buggy. So it looks for a div with the indication of the JW player and if there's one, it finds the link to the original youtube video and directly goes there.
Its triggered By Google Chrome Add-on named Javascript Injector and I apply the script on every page I visit. The plug in work perfectly well on sites like www.ayoye.co and www.veuxturire.com. But on other sites, that uses the same pathern, it seems that the script is triggerd too early. For example there www.mondedestars.com and www.lesautos.ca triggers it too early.
If I use the "inject now" fonction of the Add on after the page is really done loading, then it redirects me to the youtube page as expected. I am lost on the why it works some where and not were else.
I'm not trying to understand every single website here, I'd prefer make it dynamicly triggered after the page has done loading everything from its php, ajax, script, flash, html and CSS.
I've tryed to look to the JWplayer API, but since its terribly unclear to me, over the fact that its partialy in flash, it woudl be simpler if there was a way to trigger it after, or maybe just triggering it after i hover over the body, since every sites has a body. It cant be specific to one page.
Use something like this
var timer;
function injectYouTube() {
// DO YOUR STUFF HERE
// ONCE DONE CALL clearInterval(timer);
clearInterval(timer);
}
timer = setInterval(injectYouTube, 2000);
I am not saying this will be called after everything is loaded but instead you can make sure your code is executed when you want it to.
The JWPlayer API are not that difficult. You can retrive the informations you need even not knowing the container id.
This is an example:
var player = jwplayer(0); // get the first jwplayer element of the page
var video = player.getPlaylistItem(); // get the player video
var url = video.file // retrieve the video url
I think the setTimeout or setInterval are unreliable.
Setting up a listener on jwplayer onReady event would be better.
The pessimistic answer to this is that you can't wait until a page has finished all AJAX operations etc. because web pages can continue loading new content indefinitely if they wish.
What you might consider is running your code every time a new HTML element is added to the page. This way, you can be certain to catch JWPlayer the moment it is inserted into the page.
document.addEventListener("DOMNodeInserted", yourRemovalFunction);

Delay a javascript function until a css animation is complete

I am trying to make a modal object library that will create and open a div, iframe, img, similar to colorbox. I am doing this in pure javascript, so please do not recommend jQuery.
The problem is, when a user creates a new modal using var myModal = new modal(options, width, height), I want it to check if a modal already exists, close it, wait for the close animation, then continue to create the new modal. I can already do everything, but I'm having an issue waiting to create the new modal until the old one is gone. I am aware of webkitTransisionEnd and firing custom events, but that is not the issue. I need the actual code to wait until the old modal is finished closing until it continues on to finish the rest of the function and still return the correct object to the user. Here are some of the things I've tried:
Creating a transisionEnd listener waiting for the animation to end then creating the new modal. (this worked but considering it then becomes a nested function, it's hard to return the correct object).
Using a try, catch block. (this didn't work for my purposes)
Using a countless number of variations of the same thing where I use recursive functions
If anyone has ideas, please feel free to post them. I have tried a lot of things, but apparently not the one thing that I need to. Thanks.
EDIT:
I was able to figure it out. All I had to do was attach a transitionEnd listener to the modal that is already open, then create an additional function outside of the class that would then recall the modal with the same constructor. The code looks a bit like this:
function create(options, width, height) {
return new modal(options, width, height);
}
function modal(options, width, height) {
if (modal != null) {
modal.close();
modal.addEventListener('webkitTransitionEnd', function() {
create(options,width,height);
});
}
return;
}
var animationDuration = 1000;
setTimeout(function(){
// Animation done!
}, animationDuration);
You can't cause code to wait (e.g. pause execution of the current thread of execution) until some future event occurs. Javascript simply does not support that or work that way. It does not have a way to block the current thread of execution other than a couple modal functions like alert().
What you can do is use callbacks to notify some calling code of a future event. But, the calling code will register its callback and be returned to immediately and continue executing so the calling code has to be written to handle the callback implementation.
If you're trying to do all the work inside your library, then it should not be that tough. When the caller creates a new modal, you just have to check for a pre-existing modal dialog. If one is not up you proceed as normal. If one is up, then you register a callback notification with the previous one, store the contents of the constructor, but don't actually create the new modal dialog. Then, when your callback gets called to indicate the previous modal dialog has completed, you finish putting up the new modal.
If these modal dialogs are all of your own creation, then you need to implement completion notification on them so that when they are closed, they can notify any listeners that they're done now. If they use an animation to close and you want to wait for the close notification until the animation is complete, then you can implement that also. If you're using CSS3 animations, then as you appear to already know, you can use the transtionEnd event to know when an animation is done or if you know the timing of the animation and you don't need to be ms precise, you can also just use a setTimeout() to know approx when the animation is complete.

Categories

Resources