SetInterval Not working as Expected in my HTML task - javascript

I'm to write a function, such that when the Start Timer button is clicked, the h1 text counts down from 10 to 0. So, when the button is clicked, after 1 second, the text changes to 9, then after another second, it changes to 8, then 7, then 6, and so on, and the timer stops at 0. Additionally, when the countdown timer starts, the button is disabled, then when the timer elapses at 0, enable the button again.
I have written some code below but I don't know what I'm not doing right.
My code below
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>10</h1>
<button onclick='execute()'>Start Timer</button>
</body>
<script>
let count = document.getElementsByTagName("h1")[0].innerHTML;
setInterval(function execute() {
count--
h1 = document.getElementsByTagName("h1")[0].innerHTML = count;
btn = document.getElementsByTagName("button")[0];
if(count <= 0){
btn.disabled = true;
}
}, 1000);
</script>
</html>

The setTimeout() method calls a function or evaluates an expression after a specified number of milliseconds.
setTimeout() is not a loop method
if you want to make a loop using setTimeout() :
setTimeout(function execute() {
count--
h1 = document.getElementsByTagName("h1")[0].innerHTML = count;
btn = document.getElementsByTagName("button")[0];
if(count <= 0){
btn.disabled = true;
}
else{
execute() // <- call your function again
}
}, 1000);

Related

Javascript Beginner: can't get function to execute on multiple objects

I'm working on Cole Steele's Web Developer Bootcamp on Udemy #264. Event Bubbling. I'm trying to build a function which will allow one or more objects to be passed in and to execute the same action (toggle the classList 'hide' so that the 'Click Here to Hide' button goes away and the 'Click Here to Show' button appears) on each of them.
I am able to get this working by calling the function separately, such as
const container = document.querySelector('#container')
const show = document.querySelector('#show')
function hideOneElement(ele){
ele.classList.toggle('hide');
}
show.addEventListener('click', function () {
hideOneElement(container);
hideOneElement(show);
})
However, when I try to call the function with both container (the div that says 'Click here to hide') and show at the same time, I can't get it to work. I tried writing the hide function as a for...of, such as
function hideElements(elements){
for (const ele of elements) {
ele.classList.toggle('hide')
}
}
let stuffToHide = [container,show]
hideElements(stuffToHide)
However this does not seem to work. I also tried passing in as two separate arguments but that also doesn't seem to work:
function hideElements(ele1, ele2) {
ele1.classList.toggle('hide');
ele2.classList.toggle('hide')
}
hideElements(container, show);
At this point, I'm not sure where to go, and my Google-jitsu is not finding anything useful. This isn't really part of the course exercise, but seems like I'm fundamentally missing something about calling functions.
Full code and HTML below:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.hide {
display: none;
}
</style>
</head>
<body>
<section onclick="alert('sectopm clicked')">
<p onclick="alert('paragraph clicked')">I am a paragraph
<button onclick="alert('button clicked')">Click</button>
</p>
</section>
<div id="container">
Click to Hide
<button id="colorbtn">Change Color</button>
</div>
<div id="show">
Click here to Show
</div>
<script src="app.js"></script>
</body>
</html>
const makeRandColor = () => {
const r = Math.floor(Math.random() * 256);
const g = Math.floor(Math.random() * 256);
const b = Math.floor(Math.random() * 256);
return (`rgb(${r},${g},${b})`);
}
const button = document.querySelector('#colorbtn')
const container = document.querySelector('#container')
const show = document.querySelector('#show')
function hideElements(ele1, ele2) {
// for (const ele of elements) {
// ele.classList.toggle('hide')
// }
// elements.classList.toggle('hide')
ele1.classList.toggle('hide');
ele2.classList.toggle('hide')
}
function hideOneElement(ele){
ele.classList.toggle('hide');
}
//hideElements(show); //run function once to toggle on the 'hide' class so that this is not shown by default.
hideElements(show)
button.addEventListener('click', function (e) {
container.style.backgroundColor = makeRandColor();
e.stopPropagation();
})
container.addEventListener('click', function () {
hideElements(container, show);
//hideElements(container, show); //hide the 'click here to hide' stuff
})
show.addEventListener('click', function () {
hideElements(container);
hideElements(show);
})
show.addEventListener('click', function () {
hideOneElement(container);
hideOneElement(show);
})
"I'm trying to build a function which will allow one or more objects to be passed in" so you need to be able to call hideElements as either hideElements(ele1) or hideElements(ele1, ele2)?
If that's the case you'll need to pass an array vs individual variables. You were on the right track with the commented out code here:
// for (const ele of elements) {
// ele.classList.toggle('hide')
// }
// elements.classList.toggle('hide')
But you weren't trying to pass them as an array and you can't loop through an individual element (there's only one). If you wrap your variables in an array using [] that should fix your issues.
Example:
function hideElements(elements) {
for (const ele of elements) {
ele.classList.toggle('hide')
}
}
hideElements([show]); //run function once to toggle on the 'hide' class so that this is not shown by default.
container.addEventListener('click', function() {
hideElements([container, show]);
})
show.addEventListener('click', function() {
hideElements([container, show]);
})
Working Example:
const makeRandColor = () => {
const r = Math.floor(Math.random() * 256);
const g = Math.floor(Math.random() * 256);
const b = Math.floor(Math.random() * 256);
return (`rgb(${r},${g},${b})`);
}
const button = document.querySelector('#colorbtn')
const container = document.querySelector('#container')
const show = document.querySelector('#show')
function hideElements(elements) {
for (const ele of elements) {
ele.classList.toggle('hide')
}
}
function hideOneElement(ele) {
ele.classList.toggle('hide');
}
hideElements([show]); //run function once to toggle on the 'hide' class so that this is not shown by default.
button.addEventListener('click', function(e) {
container.style.backgroundColor = makeRandColor();
e.stopPropagation();
})
container.addEventListener('click', function() {
hideElements([container, show]);
})
show.addEventListener('click', function() {
hideElements([container, show]);
})
.hide {
display: none;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<section onclick="alert('sectopm clicked')">
<p onclick="alert('paragraph clicked')">I am a paragraph
<button onclick="alert('button clicked')">Click</button>
</p>
</section>
<div id="container">
Click to Hide
<button id="colorbtn">Change Color</button>
</div>
<div id="show">
Click here to Show
</div>
<script src="app.js"></script>
</body>
</html>
Some blocks will not work for sure - as you call hideElements with a single argument - the second arg will be undefined and there's no classList on undefined of course (causes error).
And also it's very confusing because you add event listener on the show element twice..
Copied from your post and added comments:
//hideElements(show); //run function once to toggle on the 'hide' class so that this is not shown by default.
hideElements(show) // - this will error like I said above as elem2 will be undefined..
button.addEventListener('click', function (e) {
container.style.backgroundColor = makeRandColor();
e.stopPropagation();
})
container.addEventListener('click', function () {
hideElements(container, show);
//hideElements(container, show); //hide the 'click here to hide' stuff
})
//below you make same event listener twice which is very confusing :
show.addEventListener('click', function () {
hideElements(container);
hideElements(show);
})
show.addEventListener('click', function () {
hideOneElement(container);
hideOneElement(show);
})

Timer will not fire "onclick". Fires on page load

I am trying to teach myself to code. I am coding a simple quiz. I would like my timer to fire on "start", and eventually, "next question". My timer starts once the page loads. Not sure why.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</head>
<body>
<button id="b1">Click Me!</button>
<p id="demo"></p>
<script>
var sec = 5;
var time = setInterval(myTimer, 1000);
function myTimer() {
document.getElementById("b1").onclick = function() {
myTimer()
};
document.getElementById('demo').innerHTML = sec + "sec.";
sec--;
if (sec <= -1) {
clearInterval(time);
// alert("Time out!! :(");
document.getElementById("demo").innerHTML="Time's up!";
}
}
Have tried several different ways, including "addEventListener". Nothing seems to work.
If you take a look at a minimal example you'll see that this also runs as soon as the page is loaded. Here setInterval() is called when the script loads in the page. In turn the run() function is called every second.
var timerID = setInterval(run, 1000);
function run() {
console.log("I'm running");
}
Think of the difference between var timer = run; and var timer = run(). The first assigns the function run to timer. The later executes run() and assigns the return value.
Here's your code with comments:
var sec = 5;
// start interval timer and assign the return value "intervalID" to time
var time = setInterval(myTimer, 1000);
// to be called every second
function myTimer() {
// assign an onclick handler to "b1" EVERY SECOND!
document.getElementById("b1").onclick = function() {
myTimer()
};
// update the demo DOM element with sec
document.getElementById('demo').innerHTML = sec + "sec.";
sec--;
if (sec <= -1) {
clearInterval(time);
// alert("Time out!! :(");
document.getElementById("demo").innerHTML="Time's up!";
}
}
For a solution I've moved setInterval into the onclick handler and moved said handler assignment out of the myTimer function as you only want to setup your handlers once.
I've also renamed time to timerID to make it clear what it is.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</head>
<body>
<button id="b1">Click Me!</button>
<p id="demo"></p>
<script>
var sec = 5;
var timerID;
document.getElementById("b1").onclick = function() {
timerID = setInterval(myTimer, 1000);
};
function myTimer() {
document.getElementById('demo').innerHTML = sec + "sec.";
sec--;
if (sec <= -1) {
clearInterval(timerID);
// alert("Time out!! :(");
document.getElementById("demo").innerHTML="Time's up!";
}
}
</script>
</body>
</html>
I would suggest a couple of extra exercises to help you:
Reset the timer so that you can click on the button to start the timer again
Prevent the timer being started again (which would run multiple timers with different IDs) while a timer is running
The myTimer() function is never invoked. Even if you invoke it, it does not take any action. It's just repeating itself on click.
So, instead of:
function myTimer() {
document.getElementById("b1").onclick = function() {
myTimer()
};
Try adding an Event Listener:
document.getElementById("b1").addEventListener('click', function() {
// inside here you put the code for going into next question
})
Or use just the same code, but not inside a function, and its content to be a meaningful code that leads to the next answer:
document.getElementById("b1").onclick = function() {
// code to proceed into next question
}

Uncaught TypeError: Cannot set property 'innerHTML' of nulll

Im getting a error menioned above to not use innerHTML please help with this.Getting error in js file.
Its just a basic counter im trying to make......
index.html
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script type="text/javascript" src="script.js"></script>
</head>
<body>
<div>
<h1>Counter</h1>
<h1 class="counter-display">(..)</h1>
<button class="counter-minus">-</button>
<button class="counter-plus">+</button>
</div>
</body>
</html>
Script.js
let counterDisplayElem = document.querySelector('.counter-display');
let counterMinusElem = document.querySelector('.counter-minus');
let counterPlusElem = document.querySelector('.counter-plus');
let count = 0;
updateDisplay();
counterPlusElem.addEventListener("click", () => {
count++;
updateDisplay();
});
counterMinusElem.addEventListener("click", () => {
count--;
updateDisplay();
});
function updateDisplay() {
counterDisplayElem.innerHTML = count;
};
Javascript can't find the elements since the script runs before the DOM is loaded.
You should put your code inside of a function and run it through the body onload.
function loaded() {
let counterDisplayElem = document.querySelector('.counter-display');
let counterMinusElem = document.querySelector('.counter-minus');
let counterPlusElem = document.querySelector('.counter-plus');
let count = 0;
updateDisplay();
counterPlusElem.addEventListener("click", () => {
count++;
updateDisplay();
});
counterMinusElem.addEventListener("click", () => {
count--;
updateDisplay();
});
function updateDisplay() {
counterDisplayElem.innerHTML = count;
};
}
<body onload="loaded()">
<div>
<h1>Counter</h1>
<h1 class="counter-display">(..)</h1>
<button class="counter-minus">-</button>
<button class="counter-plus">+</button>
</div>
</body>
Another option would be to put the script tag at the end of your <body> rather than inside of <head>
Your querySelector functions are probably not finding the HTML elements because they are running before the elements are created.
To prevent this without the use of jQuery you should surround your JS code with this:
document.addEventListener("DOMContentLoaded", function(event) {
//do work
});
It's supported by 99 % of browsers.
Put your code in a window's load event handler, then your code will run when your DOM has been loaded and document.querySelector('.counter-display'); can access to the h1 element.
Try this:
<script>
window.addEventListener('load', () => {
let counterDisplayElem = document.querySelector('.counter-display');
let counterMinusElem = document.querySelector('.counter-minus');
let counterPlusElem = document.querySelector('.counter-plus');
let count = 0;
updateDisplay();
counterPlusElem.addEventListener("click", () => {
count++;
updateDisplay();
});
counterMinusElem.addEventListener("click", () => {
count--;
updateDisplay();
});
function updateDisplay() {
counterDisplayElem.innerHTML = count;
};
});
</script>

Switch function when click button in javascript

I want to switch function when button 'historyBtn' clicked in javascript.
Here is the code:
const expr = '';
var history = "document.getElementById('historyBtn'). clicked == true";
switch (expr) {
case history:
allMessage();
break;
default:
limit_message();
}
But when i click button 'historyBtn' i cant switch function limit_message() to allMessage()
Edit:
in this case i want to run function 'limit_message' for default function, and then switch that function to allMessage when i click 'historyBtn'
Try this, first get the button element, then assign an eventListener and logs the click event:
let history = document.getElementById('historyBtn');
history.addEventListener('click', function(e){
console.log(e)
})
function hello(ishistory) {
if (ishistory) {
console.log("History")
}else{
console.log("Limit")
}
}
<button onclick="hello(true)">history</button>
<button onclick="hello(false)">Limit</button>
Just pass a flag to function on button click like this.
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id = "historyBtn" onclick="allMessage()">History</button>
</body>
<script>
window.onload = limit_message();
function allMessage (){
alert ('In all messages - historyBtn clicked!');
}
function limit_message (){
alert ('limit_message - onload');
}
</script>
</html>

jQuery countdown running when it's paused

I'm using Keith Wood's jQuery Countdown timer. http://keith-wood.name/countdown.html
What I want to achieve is a countup with stop and resume buttons, and controls to add and subtract minutes and seconds:
I create a countup (from 0 to 60 minutes) and pause it right away, like this:
$('#contador_tiempo').countdown({
since: 0,
format: 'MS',
layout: '{mnn}{sep}{snn}'
});
$('#contador_tiempo').countdown('pause');
But it seems that it's still running in the background. When I click the buttons to add or subtract, the functions do the operations on top of that background counter, not the displayed counter.
Full code on JSFiddle, with the behaviour reproduced:
http://jsfiddle.net/J2XHm/4/
(Play a bit with the controls and you will see that it keeps counting although it's paused.)
Yes, there is a bug in the 'getTimes' command - it recalculates when paused. I'll make the correction in the next release (1.6.2) but in the meantime you can change the _getTimesPlugin function:
_getTimesPlugin: function(target) {
var inst = $.data(target, this.propertyName);
return (!inst ? null : (inst._hold == 'pause' ? inst._savePeriods : (!inst._hold ? inst._periods :
this._calculatePeriods(inst, inst._show, inst.options.significant, new Date()))));
},
If you can accept lightweight code, i.e. without using the jQuery countdown timer, the following might help you:
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="http://localhost/web/JavaScript/jQuery/jquery"></script>
<script type="text/javascript">
var countUpSeconds = 0;
var interval = null;
function displayTime() {
$("#timeContainer").text(format(Math.floor(countUpSeconds/60))+":"+format(countUpSeconds%60));
}
function playStop() {
if(interval) {interval=window.clearInterval(interval);}
else {interval = window.setInterval(countUp, 1000);}
}
function countUp() {
++countUpSeconds;
if(countUpSeconds >= 3600) {/* do something when countup is reached*/}
displayTime();
}
function format(s) {return s<10 ? "0"+s : s;}
$(function() {
displayTime();
$("#playStop").on("click", function () { playStop(); } );
$("#addMin").on("click", function () { countUpSeconds += 60; displayTime(); } );
$("#subMin").on("click", function () { countUpSeconds = Math.max(0, countUpSeconds-60); displayTime(); } );
$("#addSec").on("click", function () { countUpSeconds += 1; displayTime(); } );
$("#subSec").on("click", function () { countUpSeconds = Math.max(0, countUpSeconds-1); displayTime(); } );
});
</script>
</head>
<body>
<div id="timeContainer"></div>
<button id="playStop">Play/Stop</button>
<button id="addMin">+1 minute</button>
<button id="subMin">-1 minute</button>
<button id="addSec">+1 second</button>
<button id="subSec">-1 second</button>
</body>
</html>

Categories