I've tried make simple clicking game (if time count is higher than 5, the game is over, but you can reset time if you click generated element).
Unfortunately time is still counting. How can I fix this?
Why do I get this error?
Uncaught TypeError: Cannot set property 'textContent' of null
at js.js:8
at js.js:49
(function() {
const startGame = document.querySelector('button')
let time = 0;
let roll = true;
let h2 = document.querySelector('h2')
h2.textContent = time;
const timeFlow = () => {
time++
}
const resetTime = () => {
time = 0;
console.log(time)
}
const rollBall = () => {
if (roll == true) {
let divCreator = document.createElement('div')
document.body.appendChild(divCreator)
divCreator.classList.add('square')
divCreator.style.backgroundColor = 'red'
divCreator.style.top = Math.floor(Math.random() * 99) + '%'
divCreator.style.left = Math.floor(Math.random() * 99) + '%'
divCreator.addEventListener('click', letsPlay)
divCreator.addEventListener('click', resetTime)
setInterval(timeFlow, 1000)
} else if (roll == false) {
roll = true;
}
}
const letsPlay = (e) => {
let start = document.body.removeChild(e.target)
roll = false;
if (time >= 5) {
alert('U lost')
} else {
rollBall()
}
}
startGame.addEventListener('click', rollBall)
})();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<button>Generate</button>
<script src='js.js'></script>
<h2>Time</h2>
</body>
</html>
You can use the load event instead of an IIFE before the elements exist.
Also your div needs dimensions
Also I assume you want the time in the H2?
Also you need position absolute to move the div
Lastly I clear the interval before a new one
window.addEventListener("load", function() {
const startGame = document.querySelector('button')
const h2 = document.querySelector('h2')
let tId;
let time = 0;
let roll = true;
h2.textContent = time;
const timeFlow = () => {
time++
h2.textContent = time;
}
const resetTime = () => {
time = 0;
}
const rollBall = () => {
if (roll == true) {
let divCreator = document.createElement('div')
document.body.appendChild(divCreator)
divCreator.classList.add('square');
divCreator.style.height = '50px'
divCreator.style.width = '50px'
divCreator.style.backgroundColor = 'red'
divCreator.style.position = 'absolute'
divCreator.style.top = Math.floor(Math.random() * 99) + '%'
divCreator.style.left = Math.floor(Math.random() * 99) + '%'
divCreator.addEventListener('click', letsPlay)
divCreator.addEventListener('click', resetTime)
clearInterval(tId); // remove any running interval
tId = setInterval(timeFlow, 1000)
}
roll = !roll;
}
const letsPlay = (e) => {
let start = document.body.removeChild(e.target)
roll = false;
if (time >= 5) {
alert('U lost')
} else {
roll = true; // right?
rollBall()
}
}
startGame.addEventListener('click', rollBall)
})
body {
height: 100%;
background-color: yellow;
}
<button>Generate</button>
<h2>Time</h2>
Your HTML code is in the wrong order: the <h2> element and the <script> element should be switched.
You should change it like this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<button>Generate</button>
<h2>Time</h2>
<script src='js.js'></script>
</body>
</html>
Related
Im developing a chess clock. The thing is that when Im retrieving the minutes and seconds from the DOM and transforming it with msToClockFormat I'm getting 100 times extra minutes. However, For more I look at the function, the more convinced I am that is working as it should be. The minutes are being divided by 60000, 1000 times 60`, and I dont see the error. Thanks.
let btn1 = document.getElementById("btn1");
let btn2 = document.getElementById("btn2");
let resetBtn = document.getElementById("reset");
let ms1 = stringTimeToMs(btn1.innerHTML);
let ms2 = stringTimeToMs(btn2.innerHTML);
let player1 = true;
let reset = false;
function tick() {
if (!reset) {
if (player1) {
btn1.innerHTML = msToClockFormat(--ms1);
} else {
btn2.innerHTML = msToClockFormat(--ms2);
}
}
}
function msToClockFormat(ms) {
let minutes = Math.floor(ms / 60000);
let seconds = Math.floor((ms % 60000) / 1000);
return minutes + ":" + (seconds < 10 ? "0" + seconds : seconds);
}
function stringTimeToMs(time) {
let minutes = time.split(":")[0];
let seconds = time.split(":")[1];
return (minutes * 60 + seconds) * 1000;
}
var myInterval = setInterval(tick, 1);
btn1.addEventListener("click", () => {
player1 = false;
reset = false;
})
btn2.addEventListener("click", () => {
player1 = true;
reset = false;
})
resetBtn.addEventListener("click", () => {
btn1.innerHTML = "05:00";
btn2.innerHTML = "05:00";
player1 = true;
ms1 = 5 * 60 * 1000;
ms2 = 5 * 60 * 1000;
reset = true;
})
<!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>Clock</title>
<link rel="stylesheet" href="https://unpkg.com/#picocss/pico#latest/css/pico.min.css">
<script src="script.js" defer></script>
</head>
<body>
<main class="container">
<div class="container1">
<button class="timers" id="btn1">3:00</button>
</div>
<div class="container2">
<button class="timers" id="btn2">3:00</button>
</div>
<div class="container3">
<button id="reset">RESET</button>
</div>
</main>
</body>
</html>
You'd probably have a better time keeping your state in your code, and just writing it into the DOM elements.
I changed the tick interval to 1000ms (because it's unlikely setInterval would work correctly with 1ms), but even so it's not guaranteed that your function will be called exactly 1000ms apart, so it would be better to look at the wall clock (+new Date()) to see how much time actually elapsed since the last "tick".
let btn1 = document.getElementById("btn1");
let btn2 = document.getElementById("btn2");
let resetBtn = document.getElementById("reset");
let ms1 = 300000;
let ms2 = 300000;
let player1 = true;
let reset = false;
function update() {
btn1.innerHTML = msToClockFormat(ms1);
btn2.innerHTML = msToClockFormat(ms2);
}
function tick() {
if (!reset) {
if (player1) {
ms1 -= 1000;
} else {
ms2 -= 1000;
}
}
update();
}
function msToClockFormat(ms) {
let minutes = Math.floor(ms / 60000);
let seconds = Math.floor((ms % 60000) / 1000);
return minutes + ":" + (seconds < 10 ? "0" + seconds : seconds);
}
update();
var myInterval = setInterval(tick, 1000);
btn1.addEventListener("click", () => {
player1 = false;
reset = false;
})
btn2.addEventListener("click", () => {
player1 = true;
reset = false;
})
resetBtn.addEventListener("click", () => {
player1 = true;
ms1 = 5 * 60 * 1000;
ms2 = 5 * 60 * 1000;
reset = true;
})
<!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>Clock</title>
<link rel="stylesheet" href="https://unpkg.com/#picocss/pico#latest/css/pico.min.css">
<script src="script.js" defer></script>
</head>
<body>
<main class="container">
<div class="container1">
<button class="timers" id="btn1"></button>
</div>
<div class="container2">
<button class="timers" id="btn2"></button>
</div>
<div class="container3">
<button id="reset">RESET</button>
</div>
</main>
</body>
</html>
Inside StringTimeToMs, seconds is being read as String input "00" as opposed to integer. As a result, adding seconds in return (minutes * 60 + seconds) * 1000; is concatenating "00" to the end of the number, making the number larger. Use parseInt to read the integer value.
function stringTimeToMs(time) {
let minutes = time.split(":")[0];
let seconds = parseInt(time.split(":")[1]);
return (minutes * 60 + seconds) * 1000;
}
let spans = document.querySelector(`#spans`);
let hrs = document.querySelector(`#hrs`);
let mins = document.querySelector(`#mins`);
let secs = document.querySelector(`#secs`);
let start = document.querySelector(`#start`);
let stop = document.querySelector(`#stop`);
spans.style.fontSize = "10em";
let preFix = 0;
let num = 1;
let secspreNum= 0;
let minspreNum = 0;
let hrspreNum = 0;
let myFunc = ()=> {
setInterval(()=>{
switch (num){
case num===1:
mins.innerHTML = `0` + num;
num ===0;
secs.innerHTML =`0`+ num++;
default:
console.log(`default test`)
}
} , 1000);
};
start.addEventListener(`click`,myFunc)
<!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>
<div id="spans">
<span id="hrs">00</span>:<span id="mins">00</span>:<span id="secs">00</span><br></div>
<button id="start">Start</button>
<button id="stop">Stop</button>
<script src="go2.js"></script>
</body>
</html>
Hello, I'm trying to make a stopwatch atm but I got stuck into a problem. Trying to use the switch statement to set the mins to add up a number if the secs reach 10. But before doing that I've been testing this switch statement and for some reason it runs default statement instead of case . Why is it like that? I set the num variable to 1 and it still runs the default one instead of num===1 . Did I missunderstood something? Thanks !
Here is a working version
I changed your querySelector and unnecessary template literals
let spans = document.getElementById('spans');
let hrs = document.getElementById('hrs');
let mins = document.getElementById('mins');
let secs = document.getElementById('secs');
let start = document.getElementById('start');
let stop = document.getElementById('stop');
let tId;
let num = 0;
let myFunc = () => {
clearInterval(tId);
tId = setInterval(function() {
const date = new Date(null); // alternatively calculate hh,mm,ss using %60
date.setSeconds(++num);
const [hh, mm, ss] = date.toISOString().substr(11, 8).split(":");
hrs.textContent = hh;
mins.textContent = mm;
secs.textContent = ss;
}, 1000)
};
start.addEventListener('click', myFunc)
stop.addEventListener('click', function() {
clearInterval(tId)
})
#spans {
font-size: 10em
}
<!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>
<div id="spans">
<span id="hrs">00</span>:<span id="mins">00</span>:<span id="secs">00</span><br></div>
<button id="start">Start</button>
<button id="stop">Stop</button>
<script src="go2.js"></script>
</body>
</html>
try this:
let myFunc = ()=> {
setInterval(()=> {
switch (num) {
case 1:
console.log(num)
mins.innerHTML = `0` + num;
secs.innerHTML =`0`+ (num++);
break;
default:
console.log(`default test`)
}
} , 1000);
};
case takes constants and not expressions afaik. You also have to break or the code will "fall through" to the next condition.
I am creating a JavaScript slideshow. It works for the most part. However, when toggling back after moving forward many images it goes forward one image, and then will go back. It will also do this when going forward an image aver toggling back for a while. I do not know if this post makes much sense as it is a difficult question to phrase.
const nextBtn = document.querySelector("next");
const previousBtn = document.querySelector("previous");
const image = document.getElementById("slideShow");
let imageNumber = 0;
console.log(image.src, imageNumber);
const slideForward = function () {
image.src = `slide-${imageNumber++}.PNG`;
console.log(image.src, imageNumber);
if (imageNumber > slideArray.length - 1) {
imageNumber = 1;
}
};
const slideBack = function () {
image.src = `slide-${imageNumber--}.PNG`;
console.log(image.src, imageNumber);
if (imageNumber < 0) {
imageNumber = slideArray.length - 1;
}
};
document.addEventListener("keydown", function (q) {
if (q.key === "ArrowLeft") {
slideBack();
}
});
document.addEventListener("keydown", function (q) {
if (q.key === "ArrowRight") {
slideForward();
}
});
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link
href="https://fonts.googleapis.com/css2?family=Days+One&family=Roboto:ital,wght#0,400;1,900&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="style.css" />
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<body id="picturesStyle" >
<nav class="topnav">
<ul>
<li>Home</li>
<li>Books</li>
<li>Movies</li>
<li>Videos</li>
<li>Pictures</li>
</ul>
</nav>
<img width="600" id="slideShow" src="PleaseWork.png" alt="">
<button class="next" onclick="slideForward()">Next</button>
<button class="previous" onclick="slideBack()">Previous</button>
</main>
<title>SlideShow</title>
<script src="script.js"></script>
</body>
</html>
If I understand you correctly I think the reason you are experiencing the issue caused by the lines
image.src = `slide-${imageNumber++}.PNG`;
image.src = `slide-${imageNumber--}.PNG`;
and if checks and the starting index of the array.
I would do like this
const slideForward = function () {
if (++imageNumber >= slideArray.length) { //Increase the value first and check it if it is out of range
imageNumber = 0; //indexes don't start from 1. They start from 0
}
image.src = `slide-${imageNumber}.PNG`;
console.log(image.src, imageNumber);
};
Same logic applies for slideBack function
const slideBack = function () {
if (--imageNumber <= 0) { //Decrease the value first and check it if it is out of range
imageNumber = slideArray.length - 1;
}
image.src = `slide-${imageNumber}.PNG`;
console.log(image.src, imageNumber);
};
I hope that covers your question
Please try this and give feedback how it goes:
const slideForward = function () {
if (imageNumber > slideArray.length - 1) {
imageNumber = -1;
}
imageNumber++;
image.src = `slide-${imageNumber}.PNG`;
console.log(image.src, imageNumber);
};
const slideBack = function () {
if (imageNumber < 0) {
imageNumber = slideArray.length;
}
imageNumber--;
image.src = `slide-${imageNumber}.PNG`;
console.log(image.src, imageNumber);
};
Don't increment or decrement the counter unless it's in the range of your slide array and only add one "keydown" event.
const nextBtn = document.querySelector("next");
const previousBtn = document.querySelector("previous");
const image = document.getElementById("slideShow");
let imageNumber = 0;
console.log(image.src, imageNumber);
const slideForward = function () {
if(imageNumber >= 0 && imageNumber < slideArray.length - 1){
imageNumber++;
image.src = `slide-${imageNumber}.PNG`;
}
console.log(image.src, imageNumber);
};
const slideBack = function () {
if(imageNumber > 0 && imageNumber <= slideArray.length - 1){
imageNumber--;
image.src = `slide-${imageNumber}.PNG`;
}
console.log(image.src, imageNumber);
};
document.addEventListener("keydown", function (q) {
if (q.key === "ArrowLeft") {
slideBack();
}
if (q.key === "ArrowRight") {
slideForward();
}
});
I have a button which I am trying to use as a start and stop for an event which starts or stops a countdown timer. However it currently only starts and changes to the stop but will not change back to start or stop the timer from counting down.
Is there a better way of doing this? I've also included the reset button as I've tried to reset things when it it's pushed but it currently just changes back to the start, but the start won't fire again after I have done this.
((d) => {
let btn = d.getElementById("btn");
let reset = d.getElementById("reset");
let countdown = d.getElementById("countdown");
let counter;
let startTime = 1500;
let timerFormat = (s) => {
return (s - (s %= 60)) / 60 + (9 < s ? ":" : ":0") + s;
};
countdown.innerHTML = timerFormat(startTime);
let timer = () => {
startTime--;
countdown.innerHTML = timerFormat(startTime);
if (startTime === 0) clearInterval(counter);
};
btn.addEventListener("click", () => {
if (stop) {
start();
btn.innerHTML = "Stop";
} else {
stop();
btn.innerHTML = "Start";
}
});
let start = () => {
counter = counter || setInterval(timer, 1000);
};
let stop = () => {
clearInterval(counter);
counter = undefined;
};
reset.onclick = () => {
startTime = 1500;
countdown.innerHTML = timerFormat(startTime);
if (btn.innerHTML === "Stop") {
btn.innerHTML = "Start";
}
};
})(document);
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="../styles/styles.css" />
<script defer src="../JS/timer.js"></script>
</head>
<body>
<div id="countdown"></div>
<div class="btn__container">
<button class="btn" id="btn">Start</button>
<button class="btn" id="reset">Reset</button>
</div>
</body>
</html>
Your problem is in how you are determining whether to start or stop:
if (stop) {
stop is a function, so it has a "truthy" value and your if statement will always evaluate to true. Instead, check the existence of counter.
Other:
Don't use .innerHTML if you can help it and certainly not when the
string you are working with doesn't contain any HTML. .innerHTML
has security and performance implications. Instead, use
.textContent.
While a timer isn't going to be 100% accurate for timekeeping, you
can make the counter a little bit more accurate by having the
callback run just a little under every second. The reason being that
running every second means you run the risk of possibly skipping over
a second on the counter if the timer doesn't run exactly one second
later.
((d) => {
let btn = d.getElementById("btn");
let reset = d.getElementById("reset");
let countdown = d.getElementById("countdown");
let counter;
let startTime = 1500;
let timerFormat = (s) => {
return (s - (s %= 60)) / 60 + (9 < s ? ":" : ":0") + s;
};
countdown.textContent = timerFormat(startTime);
let timer = () => {
startTime--;
countdown.textContent = timerFormat(startTime);
if (startTime === 0) clearInterval(counter);
};
btn.addEventListener("click", () => {
if (counter) {
stop();
btn.textContent = "Start";
} else {
start();
btn.textContent = "Stop";
}
});
let start = () => {
counter = counter || setInterval(timer, 950);
};
let stop = () => {
clearInterval(counter);
counter = null;
};
reset.onclick = () => {
startTime = 1500;
countdown.textContent = timerFormat(startTime);
if (btn.textContent === "Stop") {
btn.textContent = "Start";
}
};
})(document);
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="../styles/styles.css" />
<script defer src="../JS/timer.js"></script>
</head>
<body>
<div id="countdown"></div>
<div class="btn__container">
<button class="btn" id="btn">Start</button>
<button class="btn" id="reset">Reset</button>
</div>
</body>
</html>
I am trying to expand on an already existing JavaScript project i coded along with by adding a score counter to a simple game where you control a GIF image with arrow keys, and collect a coin which randomly appears on the page. My attempts so far have been unsuccessful.
HTML code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Coin Game Starter</title>
<link rel="stylesheet" href="app.css">
</head>
<body>
<h1 id= "scoreboard">0</h1>
<img id="player" src="https://media.tenor.com/images/0791eb3858075aca85eed5ecfe08c778/tenor.gif" alt="">
<img id="coin" src="coin.gif" alt="">
<script src="app.js"></script>
</body>
</html>
JS code:
function isTouching(a, b) {
const aRect = a.getBoundingClientRect();
const bRect = b.getBoundingClientRect();
return !(
aRect.top + aRect.height < bRect.top ||
aRect.top > bRect.top + bRect.height ||
aRect.left + aRect.width < bRect.left ||
aRect.left > bRect.left + bRect.width
);
}
const avatar = document.querySelector('#player')
const coin = document.querySelector('#coin')
window.addEventListener('keyup', function(e){
if(e.key === 'ArrowDown') {
const currTop = extractPos (avatar.style.top)
avatar.style.top = `${currTop + 50}px`
}
else if (e.key === 'ArrowUp'){
const currTop = extractPos (avatar.style.top)
avatar.style.top = `${currTop - 50}px`
}
else if (e.key === 'ArrowRight'){
const currLeft = extractPos (avatar.style.left)
avatar.style.left = `${currLeft + 50}px`
avatar.style.transform = 'scale(1,1)'
}
else if (e.key === 'ArrowLeft'){
const currLeft = extractPos (avatar.style.left)
avatar.style.left = `${currLeft - 50}px`
avatar.style.transform = 'scale(-1,1)'
}
if(isTouching(avatar, coin)) moveCoin ()
});
const moveCoin = () => {
const x = Math.floor(Math.random() * window.innerWidth);
const y = Math.floor(Math.random() * window.innerHeight);
coin.style.top = `${y}px`;
coin.style.left = `${x}px`;
}
const extractPos = (pos) => {
if(!pos) return 0
return parseInt(pos.slice(0,-2))
}
const score = document.getElementById('scoreboard')
const coinScore = score
function scoreUp () {
if (isTouching){
score ++
}
score.textContent = score
}
the bottom of the js page shows where i have tried to write a function that causes the score to increase upon the player grabbing the coin
you made the score variable a constant. change it to look like this and see if it works
let score = document.getElementById('scoreboard')
let coinScore = score
function scoreUp () {
if (isTouching){
score ++
}
score.textContent = score
}