Javascript - clearInterval not working when multiple setInterval - javascript

I got some problem with setInterval & clearInterval.
In my code, I set multiple intervals, and when count reduce to 0, stop the execution.
Like below:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0" charset="utf-8">
</head>
<body>
<body>
<script type="text/javascript">
for (var i=0; i<4; i++){
var count = 100;
var IntervalID = window.setInterval((function(){ // closure
var timeoutID = IntervalID; // temp
var countTemp = count; // temp
var id = i;
return function(){
countTemp --;
console.log(id + " " + countTemp);
// do something here
if ( countTemp == 0 ){
clearInterval(timeoutID); // stop the execution
console.log(id + " stop");
}
}
})(), 20);
}
</script>
</body>
</html>
After the console appear the stop message "x stop", all element stop except the last element(id:3), it still going.
I try to write my code in another form:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0" charset="utf-8">
</head>
<body>
<script type="text/javascript">
for (var i=0; i<4; i++){
doSomething(i);
}
function doSomething(id){
var count = 100;
var IntervalID = window.setInterval((function(){ // closure
var timeoutID = IntervalID; // temp
var countTemp = count; // temp
return function(){
countTemp --;
console.log(id + " " + countTemp);
// do something here
if ( countTemp == 0 ){
clearInterval(timeoutID); // stop the execution
console.log(id + " stop");
}
}
})(), 20);
}
</script>
</body>
</html>
But this time, all elements don't stop.
I have two questions:
1. What is difference between these two code?
2. How to make the code work fine?
Edit:
If you just want to make the code work, only change one line in the second snippet:
clearInterval(timeoutID); // stop the execution
to
clearInterval(IntervalID); // stop the execution
But other people's answer can solve what I confuse at this problem.

The problem is that the correct IntervalID is not being captured in your closure, by the time your closure runs, window.setInterval hasn't returned the id as the assignment expression has not finished yet.
A simple trick can be used with an object, since they are passed to functions by reference in JavaScript
I have modified the loop to accomplish this
for (var i=0; i < 4; i++){
var count = 100;
var args = { id: i, counter: count };
var IntervalID = window.setInterval((function(args){ // closure
return function(){
args.counter--;
console.log(args.id + " " + args.counter)
if ( args.counter == 0 ){
clearInterval(args.IntervalID); // stop the execution
console.log(args.id + " stop");
}
}.bind(args);
})(args), 20);
// by now the correct IntervalID will be captured
// as the assignment expression has finished executing
args.IntervalID = IntervalID;
}

Related

setInterval not updating with variable

When I run the code it only intervals at the initial variable I created "2000". When i click on the button it doesn't change the interval to "50". Does anyone know why?
<html>
<body>
<h1 id="pressme"> Press me! </h1>
</body>
<script>
amount = 2000;
var i = 1;
document.getElementById("pressme").onclick = function() {
amount = 50;
}
function doSomething() {
i++;
console.log("I did something! " + i);
}
setInterval(doSomething, amount)
</script>
</html>
This isn't the OG code, more a simplified version of it.
You should use setInterval with clearInterval together.
<html>
<body>
<h1 id="pressme"> Press me! </h1>
</body>
<script>
amount = 2000;
var i = 1;
var handler
document.getElementById("pressme").onclick = function() {
amount = 50;
clearInterval(handler);
handler = setInterval(doSomething, amount);
}
function doSomething() {
i++;
console.log("I did something! " + i);
}
handler = setInterval(doSomething, amount);
</script>
So when click button, you should remove original setInterval handler and recreate it.
The interval was already set with the 2s, if you change the variable after that, it won't make any difference.
I recommend you do this:
let amount = 2000;
let interval = setInterval(doSomething, amount);
var i = 1;
document.getElementById("pressme").onclick = function () {
clearInterval(interval);
amount = 50;
setInterval(doSomething, amount);
}
function doSomething() {
i++;
console.log("I did something! " + i);
}

How to create a countdown timer before a function is performed

What I want to do is have a coin flip which will start a countdown when the flip button is pressed or if instructed to elsewhere. Then I want it to countdown 3, 2, 1 and display that on the screen. When the countdown is complete it will display heads or tails. The reason for this is so I do not have to create an animation of the coin flipping instead a delay to build the tension.
This is the code I have so far:
<html>
<head>
<title>Coin Toss </title>
<script>
function toss() {
if (Math.random()>.5) {
window.document.coin.src = "CT.jpg";
}
else {
window.document.coin.src = "T.jpg";
}
return false;
}
</script>
</head>
<body>
<img name="coin" src="questionmark.png">
<form action="" onSubmit="return toss();">
<input type="submit" value="TOSS">
</form>
</body>
</html>
Here's an example using setTimeout. In this instance I've removed the form as you don't strictly need it imo, and used event listeners so that the JS call is removed from the HTML.
HTML
<img id="coin"/><br/>
<button id="toss">Toss</button><br/>
<div id="count"></div>
JS
function toss() {
var div = document.getElementById('coin');
if (Math.random() > .5) {
coin.src = "CT.jpg";
} else {
coin.src = "T.jpg";
}
}
function countDown() {
var count = 3, timer;
var div = document.getElementById('count');
// if count is not 0, add the new count to the
// count element and loop again, reducing the count number
// otherwise erase the count element, clear the timeout
// and run the toss function
var loop = function loop (count) {
if (count > 0) {
div.textContent = count--;
timer = setTimeout(loop, 1000, count);
} else {
div.textContent = '';
clearTimeout(timer);
toss();
}
}
// start the countdown
loop(count);
}
var button = document.getElementById('toss');
button.addEventListener('click', countDown, false);
DEMO
you can do this with a setInterval, here is an example:
Javascript:
var current = 3;
var elem = document.getElementById('toss');
var intervalId = setInterval( function(){
if( current > 0 ){
elem.innerHTML = "<h1>" + current + "</h1>";
} else {
if( Math.random() > .5 ){
elem.innerHTML = '<img src="http://www.thecoinspot.com/25c/1932%20Type%201%20Silver%20Washington%20Quarter%20Obv.png">';
} else {
elem.innerHTML = '<img src="http://www.thecoinspot.com/25c/1988%20Type%202%20Clad%20Washington%20Quarter%20Reverse.png">';
}
clearInterval( intervalId );
}
current--;
}, 1000 ); // 1000 ms = 1s
HTML:
<div id="toss"><div>
Also here is a Fiddle so you can test it out and see what it does:
http://jsfiddle.net/Cedriking/cLs9r3m6/
For your second question (in the comment), do this:
<html>
<head>
<title>This is my title</title>
</head>
<body>
<div id="toss"></div>
<script type="text/javascript">
var current = 3;
var elem = document.getElementById('toss');
var intervalId = setInterval( function(){
if( current > 0 ){
elem.innerHTML = "<h1>" + current + "</h1>";
} else {
if( Math.random() > .5 ){
elem.innerHTML = '<img src="http://www.thecoinspot.com/25c/1932%20Type%201%20Silver%20Washington%20Quarter%20Obv.png">';
} else {
elem.innerHTML = '<img src="http://www.thecoinspot.com/25c/1988%20Type%202%20Clad%20Washington%20Quarter%20Reverse.png">';
}
clearInterval( intervalId );
}
current--;
}, 1000 ); // 1000 ms = 1s
</script>
</body>
</html>

Javascript simple image slider

I would like to have a simple picture slider. That slider should be in the header of the website and should switch between a small delay. I want it simple and named the pictures 1.jpg, 2.jpg and so on and they are in the folder "bilder".
I have tried a bit and here is my result:
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
<script type="text/javascript">
function picture_slider(){
setInterval( switch_picture(), 3000 );
return false;
}
function switch_picture() {
for ( var i = 1; i < 7 ; i++ ) {
var pfad = "bilder/" + i + ".jpg";
document.getElementById("bild").src = pfad;
i++;
};
return false;
}
</script>
</head>
<body onload="picture_slider();">
<img id="bild" src="" />
</body>
</html>
I guess, that I did something wrong, because my browser is showing only one picture and is not switching.
jsBin demo
On every loop you're iterating (for loop) over all your images, resulting in the latest one. Also use only switch_picture (instead of switch_picture()).
P.S: create an 0.jpg image for this counter:
function picture_slider(){
setInterval( switch_picture, 2000 ); // corrected removing "()"
}
var bild = document.getElementById("bild")
var i = 0; // Start from image 0.jpg
function switch_picture() { // don't iterate a loop in here!
bild.src = "bilder/"+ (i++ % 7) +".jpg";
}
I found something here: Stackoverflow Link
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
<script type="text/javascript">
var i = 0;
var images = [ "1", "2", "3", "4", "5", "6", "7"]
function picture_slider(){
setInterval( switch_picture, 2000 );
}
function switch_picture() {
i++;
if ( i >= images.length ) {
i = 0;
};
var bild = document.getElementById("bild");
bild.src = "bilder/" + images[i] + ".jpg";
}
</script>
</head>
<body onload="picture_slider();">
<img id="bild" src="" />
</body>
</html>
// 1. images need to store in an Array
const images = [
"img/pic-1.jpg",
"img/pic-2.jpg",
"img/pic-3.jpg",
"img/pic-4.jpg",
"img/pic-5.jpg",
"img/pic-6.jpg",
"img/pic-7.jpg"
];
// 7. getElementById by calling, store globally (do not call inside loop/setInterval performance will loose)
const imgElement= document.getElementById("slider-image");
// 2. set initial value for array index
let imgIndex = 0;
// 3. create setInterval()
const sliderInterval = setInterval(() => {
// 6. check condition if length is finished then start again from 0
if (imgIndex >= images.length) { // use >= because index start from 0 and length start from 1, if use just > then last element will be undefined
imgIndex = 0;
}
// 5. testing
// console.log(imgIndex);
// 9. Dynamically change image src
const imgUrl = images[imgIndex];
imgElement.setAttribute('src', imgUrl);
// 4. increase value by 1
imgIndex++;
}, 1000);

Uncaught TypeError: Cannot set property 'innerHTML' of null

I get this error: "Uncaught TypeError: Cannot set property 'innerHTML' of null". I can't find the fault in my code. I don't use CSS now, nor jQuery.
Here is my HTML code:
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<!--<script src="js/banner_test_script.js" language="javascript" type="text/javascript"></script>-->
</head>
<body>
<div id="ban"></div>
<script src="js/banner_test_script.js"></script>
</body>
</html>
And here is my JavaScript code:
window.onload = function what()
{
var myTimery = setInterval(function() {
myTimer();
}, 1000);
var imgn = 0;
function myTimer()
{
if (imgn === 0)
{
var d = "../../download.jpg";
imgn++;
}
else if (imgn === 1)
{
var d = "../../images.jpg";
imgn++;
}
else if (imgn === 2)
{
var d = "../../images1.jpg";
resetImgn();
}
var p = '<img src="' + d + '"></img>';
document.getElementById("ban").innerHTML = "lol";
document.writeln(p);
console.log(document.getElementById('ban') + "1");
console.log(document.getElementById("ban") + "2");
}
;
function resetImgn()
{
imgn = 0;
}
;
/*
function myTimer()
{
for (i = 0; i < 200; i++)
{
document.writeln(i);
}
}
;
*/
};
If you use document.write (or document.writeln) after the initial parsing of the page (which you do, because you're using it in a timer), that implicitly does a document.open, which wipes out the existing page entirely (and the replaces it with what you write). So your elements no longer exist when you try to look them up.
It's not clear to me what your document.writeln(p) is meant to do, but if you remove it, you'll stop wiping out your element, and document.getElementById should be able to find it.
If your goal is to have the image markup in p written to the ban element, then:
document.getElementById("bad").innerHTML = p;
...and removing document.writeln will do it.
Side note: That setInterval line can be just: var myTimery = setInterval(myTimer, 1000); Functions are first-class objects.
Side note 2: You seem to be cycling between imgn 0, 1, 2, then back to 0. You have somewhat complicated logic for doing it. Just FYI, there's a handy trick using the % operator you can use:
imgn = (imgn + 1) % 3;
Assuming you start with 0, the next will be 1, then 2, then 0, then 1...

JavaScript undefined variable in an anonymous function

This code is supposed to switch the display property of all children elements of #slide-container to "block" with time delay two seconds between the switches.
var magic = window.setInterval(function(){
if (document.readyState === "complete") {
var children = document.getElementById('slide-container').children;
for (var i = 0; children.length > i; i++ ) {
setTimeout(function(){
children[i].style.display = "block";
console.log(i);
},2000);
}
magic = window.clearInterval(magic);
} else {
console.log("...");
}
}, 1000);
I am using it along with this html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
</head>
<body>
<ul id="slide-container">
<li style="display: none;"><img src="http://i.imgur.com/8qBcyzc.jpg"></li>
<li style="display: none;"><img src="http://i.imgur.com/oxMTFTF.png"></li>
<li style="display: none;"><img src="http://i.imgur.com/JTM6Yqg.jpg"></li>
</ul>
</body>
</html>
I get error Uncaught TypeError: Cannot read property 'style' of undefined
It says it cannot find children or children[0]. But that variable has been specified and the dom nodes exist.
Closure issue.
Try adding a 3rd parameter to the setTimeout (doesn't work):
setTimeout(function(i){
children[i].style.display = "block";
console.log(i);
}, 2000, i);
Example
Another formate:
var i = 0;
var timer = setInterval(function () {
children[i].style.display = "block";
i++;
if (i == children.length) {
clearInterval(timer);
}
}, 2000);
EXAMPLE
ES6 is around the corner, the let statement is especially built for situations like this:
for (let i = 0; children.length > i; i++ ) {
setTimeout(function(){
children[i].style.display = "block";
console.log(i);
}, 2000);
}
However this is not the answer you need for right now. This was just a note.
Try encasing the setTimeout in an IIFE (Immediately invoked function expression)
for (var i = 0; children.length > i; i++) {
(function (index) {
setTimeout(function () {
children[index].style.display = "block";
console.log(i);
}, 2000);
})(i);
}
Check Fiddle
The reference of i is common to all the functions executed by setTimeout . So by the time the function inside executes , the value of i will point to children.length .
But there is no element that refers to children[children.length] which does not exist and throws an error.
By the time setTimeout is ready i will be the length of children so you have to capture the value of i
try this
var time = 2000;
for (var i = 0; children.length > i; i++ ) {
(function( child, time) {
window.setTimeout(function() {
child.style.display = "block";
}, time);
}( children[i], time));
time += 2000;
}
or you could do this. ... I have fixed the delay thing
var hideElement = function( element, time) {
window.setTimeout(function() {
element.style.display = 'block';
}, time);
};
var time = 2000;
for (var i = 0; children.length > i; i++ ) {
hideElement(children[i], time);
time += 2000;
}

Categories

Resources