I've written a function to catch a loop after it has run over a specified time.
var t1 = new Date().getTime();
while(true){
//general code in here...
document.body.appendChild(document.createElement('div')); //This causes the problem
if(isInfinite(t1,3000)){
alert('Loop stopped after 3 seconds')
break;
}
}
function isInfinite(t1,timeLimit){
var t2 = new Date().getTime();
if(t2-t1> timeLimit){
return true;
}
else return false;
}
It works as expected, but when I try to append nodes to the document it fails to catch in chrome & safari.
What's weird is it works when I have the debugger running, and it works in FF. What is causing this?
Maybe you are firing this JS code before DOM is ready. Here is my implementation.
<!--Basically I am firing this script with onload event function.-->
<html>
<head></head>
<body onload="init()">
<script>
function init() {
alert("asg");
var t1 = new Date().getTime();
while(true) {
//general code in here...
document.body.appendChild(document.createElement('div')); //This causes the problem
if(isInfinite(t1, 3000)) {
alert('Loop stopped after 3 seconds')
break;
}
}
function isInfinite(t1, timeLimit) {
var t2 = new Date().getTime();
if(t2 - t1 > timeLimit) {
return truite;
} else return false;
}
}
</script>
</body>
</html>
However, this script is most likely going to crash depending on how browser manufacturers have set their restriction on looping (This usually occurs when main thread is blocked). So I think it is much better idea to use setTimeout function for this example.
Just for your interest
Javascript is event based and it has only 1 thread. You have mentioned that you are going to show it to beginners. Why not explain them event based nature of JS instead of doing it this way?
Related
I want to automate clicking the agree button to Google's cookie policies.
(I clean cookies after closing a tab, and I don't want to create a google account, so I get asked every time I use google)
There is a div element with the ID "introAgreeButton" that I'm trying to access with my script:
<div role="button" id="introAgreeButton" [...]></div>
However, document.getElementById('introAgreeButton') always returns null.
My first thought was that the element wasn't loaded by the time my function was executed. But it doesn't work if I execute it on window.onload, or even if I run it in a loop until the element is definitely there:
window.onload = function() {
var x = document.getElementById('introAgreeButton')
console.log(x)
}
Output:
null
function loop() {
var x = document.getElementById('introAgreeButton')
if (x) {
console.log('success')
} else {
loop()
}
}
Output:
null
null
null
...
Can be tested on https://www.google.com/search?hl=en&q=test
Anyone have an idea why this is and how to solve it?
Edit: I execute the script via the browser extension TamperMonkey
You can use setInterval to check if element is rendered in DOM like this :
document.addEventListener('DOMContentLoaded', function () {
var intervalID = null;
function checkElementInDOM () {
var element = document.getElementById('introAgreeButton');
if (element) {
clearInterval(intervalID);
// DO YOUR STUFF HERE ...
}
}
intervalID = setInterval(checkElementInDOM, 100);
});
To be used intelligently, however, so as not to have a setInterval which works continuously. Maybe think about adding a maximum number of attempts.
The issue is the following: I want to modify a h1 (id="waiting") and then execute a function that takes several seconds to complete (solve).
function solveWrapper(table) {
document.getElementById("waiting").innerHTML = "Wait please, Nurikabe is being solved...";
alert("alert");
var currentState = new State(table);
solve(table, currentState);
}
With this code I get the alert before the solve function is called, but the h1 tag is modified only after that function execution is finished. Does anyone know how to fix this?
This is the button that calls solveWrapper:
<button onclick="solveWrapper(grid);">Solve</button>
Here is the whole code: https://github.com/jackowski626/projects/blob/master/Nurikabe_SJ_Solver.html
Put the call to solve() in a timeout handler:
function solveWrapper(table) {
document.getElementById("waiting").innerHTML = "Wait please, Nurikabe is being solved...";
var currentState = new State(table);
setTimeout(function() {
solve(table, currentState);
}, 10);
}
Browsers defer updating the view until event loops finish unless they have no choice.
I'd say give this a go:
function solveWrapper(table) {
window.requestAnimationFrame(
document.getElementById("waiting").innerHTML = "Wait please, Nurikabe is being solved..."
);
alert("alert");
var currentState = new State(table);
solve(table, currentState);
}
The idea is that requestAnimationFrame should cause a browser repaint and update what's rendered on the screen for you. I think the issue is that you're calling a change to the innerHTML but it's not being reflected in the browser until it's ready to do it, which is after it runs your solve function.
https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
I have a javascript function that runs on window.onload:
if(window.onload) {
var curronload = window.onload;
var newonload = function() {
curronload();
formatICCID_IMEI();
};
window.onload = newonload;
} else {
window.onload = formatICCID_IMEI;
function formatICCID_IMEI() {
var IMEI = $find("<%=cbIMEI.ClientID %>");
alert(IMEI.get_textBoxControl().value);
alert(IMEI.get_textBoxControl().value.indexOf("."));
if (IMEI.get_textBoxControl().value.indexOf(".") > -1) {
alert("Hi!");
}
}
I'm using this more elaborate way of calling my function from this link because if I just use window.onload or document.onload, my control (cbIMEI) is not found. Using this more elaborate method, I don't have that problem. However, my function formatICCID_IMEI is acting strangely. I don't know if it's due to the way I'm calling formatICCID_IMEI, or just something in formatICCID_IMEI that I'm not seeing. If I comment out
if (IMEI.get_textBoxControl().value.indexOf(".") > -1) {
alert("Hi!");
the first and second alerts tell me that
IMEI.get_textBoxControl().value = 351937.04.230880.7
and that
IMEI.get_textBoxControl().value.indexOf = 6
all as expected. HOWEVER, if I comment out the two above alert lines and uncomment the IF condition, the line
alert("Hi!");
never runs. If I uncomment all lines, none of the alerts run. The same behavior holds true if I'm in debug mode. If the condition is uncommented, my cursor never gets to my function at all. What the heck?
You have no close bracket for the if(window.onload) condition - is that intentional?
Since you're using jQuery, why are you not just using the standard $(document).ready stuff?
function formatICCID_IMEI() {
var IMEI = $find("<%=cbIMEI.ClientID %>");
alert(IMEI.get_textBoxControl().value);
alert(IMEI.get_textBoxControl().value.indexOf("."));
if (IMEI.get_textBoxControl().value.indexOf(".") > -1) {
alert("Hi!");
}
}
$(document).ready(formatICCID_IMEI);
function playPlaylist(trackstemp) {
trackstemp = trackstemp.split(' ');
for (i=0; i < trackstemp.length; i++){
tracks[i] = trackstemp[i];
}
numoftracks = tracks.length - 1;
currenttrack = 0;
loadNewVideo(tracks[currenttrack])
}
function loadNewVideo(id) {
ytplayerid.loadVideoById(id, 0);
}
I have a prev() and next() function that work just fine by calling loadNewVideo(tracks[currenttrack]), but the initial video only works if I alert(id) within the loadNewVideo() function.
Any clue to why this is happening?
Are you using onYouTubePlayerReady which tells you when the player is ready?
function onYouTubePlayerReady(){
//Call you first track here
}
Mostly likely the effect of the alert is to delay the execution of ytplayerid.loadVideoById(id, 0); so that it satisfies a race condition in your code - for example, the function doesn't exist yet, or some dependency in the function is not yet settled.
Make sure that the first loadNewVideo happens after all other scripts are loaded and the DOM is ready (ie, by attaching the function to the window.load event).
Separate this out and put in that head in its own script tags.
<script type="text/javascript" language="javascript">
function onYouTubePlayerReady(playerid) {
ytp = document.getElementById('ytplayer');
ytp.mute();
};
</script>
I have the following (javascript/jquery) code to show a busy indicator (after a delay) while an image is loading:
function imgUpdate(arg) {
var loaded = false;
$("#image").one("load", function(){
loaded = true;
$("#busyIndicator").hide();
});
setTimeout(function(){
if (!loaded) {
$("#busyIndicator").show();
}
}, 250);
$("#image")[0].src = arg;
}
Sometimes, the indicator comes up and stays up. How is this possible if the browser's javascript engine is single-threaded? (This is on Firefox 3, by the way.)
One note: this seems to happen when the image being loaded is already cached.
Another note: if I log to my firebug console, all of the lines in imgUpdate are executed, but a log message inside the onload handler never prints on subsequent calls to imgUpdate.
Is there any other javascript on the page that breaks? If so, this may not be a race condition -- JS could simply stop executing before the busyIndicator is hidden again...
I'm hard pressed to replicate this.
Here is the implementation of what you're doing:
A version using caching:
http://jsbin.com/uwuho
A version with caching being prevented: (uses parameter to avoid caching)
http://jsbin.com/oguvi
Hit F5/Ctrl-F5 to see it go. (in particular with the version which prevents caching)
With or without caching neither version is doing what you'd described.
Your problem probably lies elsewhere.
Clearing the image's src tag seems to fix the problem:
function imgUpdate(arg) {
var loaded = false;
$("#image").one("load", function(){
loaded = true;
$("#busyIndicator").hide();
});
setTimeout(function(){
if (!loaded) {
$("#busyIndicator").show();
}
}, 250);
$("#image")[0].src = "";
$("#image")[0].src = arg;
}
You might want to clear the timeout in your callback so that it won't fire if the image is loaded.
var timer = null;
function imgUpdate(arg) {
var loaded = false;
timer = setTimeout(function(){
$("#busyIndicator").show();
timer = null;
}, 250);
$("#image").one("load", function(){
if (timer) {
clearTimeout(timer);
timer = null;
}
$("#busyIndicator").hide();
});
$("#image")[0].src = arg;
}