Here I am trying to run a function "renderQuestion" which will randomize questions and will show the next options once the "next" button is clicked. By "getSelectedValue" I am trying to get the selected radio button.
But what I am fetching is that it shows which button is selected for the first time I press "next" button, but it's not working onward. It's giving some error "Cannot read property 'value' of null".
If I don't use "renderForm.reset()", I can get the values every time I click next. Now, what I am doing wrong here?
const renderForm = document.querySelector("#renderForm");
const next = document.querySelector("#next");
// const q = document.querySelector("#question");
// const ans1 = document.querySelector("#ans1");
// const ans2 = document.querySelector("#ans2");
// const ans3 = document.querySelector("#ans3");
// const ans4 = document.querySelector("#ans4");
renderQuestion = async () => {
const timeout = async (ms) => new Promise((res) => setTimeout(res, ms));
let userClicked = false;
let arr = [ans1, ans2, ans3, ans4]; //array of options
for (let i = arr.length - 1; i > 0; i--) { ////randomizing array
const j = Math.floor(Math.random() * (i + 1));
const temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
// q.innerHTML =
// `${totalQ}/${ongoing}` + ` ` + qID.data().question;
// arr[0].innerHTML = qID.data().a;
// arr[1].innerHTML = qID.data().b;
// arr[2].innerHTML = qID.data().c;
// arr[3].innerHTML = qID.data().d;
next.addEventListener("click", (e) => {
console.log("inside next");
let getSelectedValue = document.querySelector('input[name="ans"]:checked');
console.log(getSelectedValue.value);
getSelectedValue.checked = false;
userClicked = true;
e.preventDefault();
// renderForm.reset();
});
while (userClicked === false) await timeout(50);
};
start = async () =>
{
for(let i=0;i<4;i++)
{
await renderQuestion();
}
}
start();
#renderForm p {
display: inline;
}
.option {
/* display: inline-block; */
font-size: 1.5rem;
margin-block: 2rem;
margin-left: 1rem;
}
.button {
border: none;
outline: none;
font-size: 1rem;
margin-top: 1rem;
background-color: #081b0a;
color: #ffffff;
padding: 0.75rem 1rem;
border-radius: 0.5rem;
font-weight: 100;
font-family: monospace;
margin-bottom:8rem;
}
<!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" />
<link rel="stylesheet" href="/style.css" />
<title>Document</title>
</head>
<body>
<section>
<form action="" id="renderForm">
<p id="question"></p>
<div class="option">
<input type="radio" name="ans" id="option1" value="ans1" required />
<p id="ans1">1</p>
</div>
<div class="option">
<input type="radio" name="ans" id="option2" value="ans2" required />
<p id="ans2">2</p>
</div>
<div class="option">
<input type="radio" name="ans" id="option3" value="ans3" required />
<p id="ans3">3</p>
</div>
<div class="option">
<input type="radio" name="ans" id="option4" value="ans4" required />
<p id="ans4">4</p>
</div>
<button class="button" id="next" type="submit">Next</button>
</form>
</section>
<script src="/js.js"></script>
</body>
</html>
Move the eventlistener out of the loop. You only need to apply it once, and if you delegate it (meaning apply the listener to a static parent, like document and then test for the target ID), it will work for any #next button you add to the DOM.
document.addEventListener("click", (e) => {
if (e.target.id !== 'next') return
console.log("inside next");
let getSelectedValue = document.querySelector('input[name="ans"]:checked');
console.log(getSelectedValue.value);
getSelectedValue.checked = false;
userClicked = true;
e.preventDefault();
// renderForm.reset();
});
const renderForm = document.querySelector("#renderForm");
const next = document.querySelector("#next");
document.addEventListener("click", (e) => {
if (e.target.id !== 'next') return
console.log("inside next");
let getSelectedValue = document.querySelector('input[name="ans"]:checked');
console.log(getSelectedValue.value);
getSelectedValue.checked = false;
userClicked = true;
e.preventDefault();
// renderForm.reset();
});
renderQuestion = async() => {
const timeout = async(ms) => new Promise((res) => setTimeout(res, ms));
let userClicked = false;
let arr = [ans1, ans2, ans3, ans4]; //array of options
for (let i = arr.length - 1; i > 0; i--) { ////randomizing array
const j = Math.floor(Math.random() * (i + 1));
const temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
while (userClicked === false) await timeout(50);
};
start = async() => {
for (let i = 0; i < 4; i++) {
await renderQuestion();
}
}
start();
#renderForm p {
display: inline;
}
.option {
/* display: inline-block; */
font-size: 1.5rem;
margin-block: 2rem;
margin-left: 1rem;
}
.button {
border: none;
outline: none;
font-size: 1rem;
margin-top: 1rem;
background-color: #081b0a;
color: #ffffff;
padding: 0.75rem 1rem;
border-radius: 0.5rem;
font-weight: 100;
font-family: monospace;
margin-bottom: 8rem;
}
<!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" />
<link rel="stylesheet" href="/style.css" />
<title>Document</title>
</head>
<body>
<section>
<form action="" id="renderForm">
<p id="question"></p>
<div class="option">
<input type="radio" name="ans" id="option1" value="ans1" required />
<p id="ans1">1</p>
</div>
<div class="option">
<input type="radio" name="ans" id="option2" value="ans2" required />
<p id="ans2">2</p>
</div>
<div class="option">
<input type="radio" name="ans" id="option3" value="ans3" required />
<p id="ans3">3</p>
</div>
<div class="option">
<input type="radio" name="ans" id="option4" value="ans4" required />
<p id="ans4">4</p>
</div>
<button class="button" id="next" type="submit">Next</button>
</form>
</section>
<script src="/js.js"></script>
</body>
</html>
At last, I have solved the issue. Initially "renderQuestion" was working every time (4 times here because of for loop) I clicked the "next" button. But whenever I move the eventlistener out of the loop "renderQuestion" function wasn't working on the "next" button click.
It's Because "userClicked" is a local variable and isn't being triggered if I click "next". So, I just changed it to a global variable and everything else same as #kinglish did.
const renderForm = document.querySelector("#renderForm");
const next = document.querySelector("#next");
document.addEventListener("click", (e) => {
if (e.target.id !== 'next') return
console.log("inside next");
let getSelectedValue = document.querySelector('input[name="ans"]:checked');
console.log(getSelectedValue.value);
getSelectedValue.checked = false;
userClicked = true;
e.preventDefault();
// renderForm.reset();
});
renderQuestion = async() => {
console.log("render");
const timeout = async(ms) => new Promise((res) => setTimeout(res, ms));
window.userClicked = false;
let arr = [ans1, ans2, ans3, ans4]; //array of options
for (let i = arr.length - 1; i > 0; i--) { ////randomizing array
const j = Math.floor(Math.random() * (i + 1));
const temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
while (userClicked === false) await timeout(50);
};
start = async() => {
for (let i = 0; i < 4; i++) {
await renderQuestion();
}
}
start();
#renderForm p
{
display: inline;
}
.option
{
/* display: inline-block; */
font-size: 1.5rem;
margin-block: 2rem;
margin-left: 1rem;
}
.button {
border: none;
outline: none;
font-size: 1rem;
margin-top: 1rem;
background-color: #081b0a;
color: #ffffff;
padding: 0.75rem 1rem;
border-radius: 0.5rem;
font-weight: 100;
font-family: monospace;
margin-bottom:8rem;
}
<!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" />
<link rel="stylesheet" href="/style.css" />
<title>Document</title>
</head>
<body>
<section>
<form action="" id="renderForm">
<p id="question"></p>
<div class="option">
<input type="radio" name="ans" id="option1" value="ans1" required />
<p id="ans1">1</p>
</div>
<div class="option">
<input type="radio" name="ans" id="option2" value="ans2" required />
<p id="ans2">2</p>
</div>
<div class="option">
<input type="radio" name="ans" id="option3" value="ans3" required />
<p id="ans3">3</p>
</div>
<div class="option">
<input type="radio" name="ans" id="option4" value="ans4" required />
<p id="ans4">4</p>
</div>
<button class="button" id="next" type="submit">Next</button>
</form>
</section>
<script src="/js.js"></script>
</body>
</html>
Related
I have a problem: Hover effect is not working after calling a function that changes the inner text of target elements. Basically, after loading the page it's working, but once I click a 'start' button which calls a function to change the content of the divs, hover effect is no longer working. Here is the code:
<div class="options">
<div class="option" id="opt1"><div>A: one</div></div>
<div class="option" id="opt2"><div>B: two</div></div>
<div class="option" id="opt3"><div>C: three</div></div>
<div class="option" id="opt4"><div>D: four</div></div>
</div>
<button class="button" onclick="loadQuestions()">Start</button>
.option {
border: 2px solid blue;
}
.option :hover {
box-shadow: 0 0 0 3px burlywood;
cursor: pointer;
}
const question = document.getElementById('question');
const opt1 = document.getElementById('opt1');
const opt2 = document.getElementById('opt2');
const opt3 = document.getElementById('opt3');
const opt4 = document.getElementById('opt4');
function loadQuestions() {
question.innerText = 'How old are you?';
opt1.innerText = 'A: 55';
opt2.innerText = 'B: 52';
opt3.innerText = 'C: 82';
opt4.innerText = 'D: 12';
}
You don't have a tag with id question so its throwing an error. Checkout following code:
<!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>
.option {
border: 2px solid blue;
}
.option:hover {
box-shadow: 0 0 0 3px burlywood;
cursor: pointer;
}
</style>
</head>
<body>
<div class="options">
<span id="question" class="question"></span>
<div class="option" id="opt1"><div>A: one</div></div>
<div class="option" id="opt2"><div>B: two</div></div>
<div class="option" id="opt3"><div>C: three</div></div>
<div class="option" id="opt4"><div>D: four</div></div>
</div>
<button class="button" onclick="javascript:loadQuestions()">Start</button>
<script type="text/javascript">
const question = document.getElementById("question");
const opt1 = document.getElementById("opt1");
const opt2 = document.getElementById("opt2");
const opt3 = document.getElementById("opt3");
const opt4 = document.getElementById("opt4");
const loadQuestions = () => {
question.innerText = "How old are you?";
opt1.innerText = "A: 55";
opt2.innerText = "B: 52";
opt3.innerText = "C: 82";
opt4.innerText = "D: 12";
};
</script>
<script src="./index.js" type="text/javascript"></script>
</body>
</html>
You have to specify a tag with question id.
If you enter 2 numbers in 2 fields, addition and multiplication will be displayed at the same time. After execution, he has to write e.g. Adding: (result of addition) Multiplication (result of multiplication) with one button . How to make from this code:
<!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>
<form name="frmadd">
Number 1<input type ="text" name="txt1" ><br>
Number 2<input type ="text" name="txt2" ><br>
addition :<input type ="text" name="txt3" disabled><br>
multiplication :<input type ="text" name="txt4" disabled><br>
<input type="button" value="Add" name="but1" onClick="addNum()">
</form>
<script>
function addNum()
{
num1=parseInt(document.frmadd.txt1.value);
num2=parseInt(document.frmadd.txt2.value);
num3=num1+num2;
num4=num1*num2;
document.frmadd.txt3.value=num3;
document.frmadd.txt4.value=num4;
}
</script>
</body>
</html>
You're really not following any best practices, but either way, your original code is working.
I'm not 100% sure what you mean with "make the result window appear without this window, field," but I assume you want the result to appear outside of the form, in some other element or window. You can use a modal.
The styling isn't good, but this solution gets the point across nicely:
const modal = document.querySelector('#modal');
const firstNum = document.frmadd.txt1;
const secondNum = document.frmadd.txt2;
const additionResult = document.querySelector('#add');
const multResult = document.querySelector('#multiply');
const addNum = () => {
const num1 = +firstNum.value;
const num2 = +secondNum.value;
if (!(num1 + num2)) return alert('Both inputs must be numbers!');
const added = num1 + num2;
const multiplied = num1 * num2;
additionResult.textContent = added;
multResult.textContent = multiplied;
modal.classList.remove('hidden');
setTimeout(hideModal, 3000);
};
const hideModal = () => {
modal.classList.add('hidden');
};
form {
display: flex;
flex-direction: column;
max-width: 300px;
}
#modal {
display: flex;
flex-direction: column;
max-width: 300px;
background: gainsboro;
box-shadow: 0px 0px 15px black;
position: fixed;
left: 10%;
top: 10%;
}
#modal.hidden {
display: none;
}
<form name="frmadd">
<label for="txt1">Num 1</label>
<input type="text" name="txt1" />
<label for="txt2">Num 2</label>
<input type="text" name="txt2" />
<input type="button" value="Add" name="but1" onClick="addNum()" />
</form>
<div id="modal" class="hidden">
<button onClick="hideModal()">X</button>
<h3>Addition Result:</h3>
<p id="add"></p>
<h3>Multiplication Result:</h3>
<p id="multiply"></p>
</div>
When I type in something in the text area element and then click on the read button nothing is being uttered. The console doesn't throw any error and I can't identify where I've gone wrong either. I went on the "SpeechUtterance" MDN documentation and as far as I can tell I've followed all the right steps. Help please!
const read = document.getElementById("read");
const pause = document.getElementById("pause");
const stop = document.getElementById("stop");
const speed = document.getElementById("speed");
const text = document.getElementById("text");
read.addEventListener("click", () => {
readText(text.value)
});
pause.addEventListener("click", () => {
pauseText();
});
function readText(dummy) {
var utterThis = new SpeechSynthesisUtterance(dummy);
if(speechSynthesis.speaking && speechSynthesis.paused) {
return speechSynthesis.resume()
}
if(speechSynthesis.speaking) return;
console.log(utterThis)
console.log(speechSynthesis)
utterThis.text = dummy;
utterThis.rate = speed.value
text.disabled = true;
speechSynthesis.speak(utterThis)
utterThis.addEventListener("end", () => {
text.disabled = false
})
}
function pauseText() {
if(speechSynthesis.speaking) speechSynthesis.pause();
}
body {
background-color: purple;
}
textarea {
margin-top: 50px;
margin: auto;
}
textarea:focus {
background: green;
}
.container {
display: flex;
justify-content: center;
}
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="index.css">
<title></title>
</head>
<body>
<textarea id="text" name="name" rows="30" cols="100"></textarea>
<div class="container">
<label for="speed">Speed</label>
<input id="speed" type="number" min="0" step="1" max="10">
<button id="read" type="button" name="button">Read</button>
<button id="pause" type="button" name="button">Pause</button>
<button id="stop" type="button" name="button">Stop</button>
</div>
</body>
<script src="index.js" charset="utf-8"></script>
</html>
I'm trying to get the two players to take turns in my tic-tac-toe project. In my playerContols function I've tried to put each player in a "current PLayer" housing variable that will switch out depending if one player went already but it just gets stuck on player 2 and just mark every cell with O's. I'm not entirely sure what's wrong with it
my code
const playgame = (() => {
const playerOne = {
Name: 'playerOne',
Marking: 'X'
};
const playerTwo = {
Name: 'PlayerTwo',
Marking: 'O'
};
function playerControls(e) {
let currentPlayer;
currentPlayer = playerOne;
e.target.textContent = currentPlayer.Marking;
if (currentPlayer === playerOne) {
currentPlayer = playerTwo;
e.target.textContent = currentPlayer.Marking;
} else {
currentPlayer = playerOne;
e.target.textContent = currentPlayer.Marking;
}
}
return {
playerControls
}
})();
const gameBoard = (() => {
const makeBoard = (rows, cols) => {
const theBoard = document.getElementById("GameBoard");
theBoard.style.gridTemplateColumns = `repeat(${cols}, 1fr)`;
theBoard.style.gridTemplateRows = `repeat(${rows}, 1fr)`;
for (i = 0; i < (rows * cols); i++) {
let gameDivs = document.createElement("div");
gameDivs.addEventListener("click", playgame.playerControls)
theBoard.appendChild(gameDivs).classList.add("newdivgrid");
}
};
makeBoard(3, 3);
})();
#GameBoard {
width: 600px;
height: 350px;
border: 1px solid darkblue;
margin-left: 27%;
text-align: center;
font-size: 500%;
font-family: cursive, sans-serif;
display: grid;
}
.newdivgrid {
border: 1px solid black;
grid-row-gap: 50px;
}
.newdivgrid:hover {
background-color: aqua;
}
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tic-Tac-Toe game</title>
<link rel="stylesheet" href="Tic-Tac-Toe.css">
</head>
<h1>Tic-Tac-Toe Project</h1>
<div id="PlayerSelectionContainer">
<h2>Select Players</h2>
<div class="Player1">
<h3>Player 1</h3>
<div class="playerSelectionButtons" data-player="playerOne">
<button data-player="human">Human</button>
<button data-player="computer">Computer</button>
</div>
</div>
<div class="Player2">
<h3>Player 2</h3>
<div class="playerSelectionButtons" data-player="playerTwo">
<button data-human-player="human">Human</button>
<button data-computer-player="computer">Computer</button>
</div>
</div>
</div>
<div id="GameBoard">
</div>
<div id="resultContainer">
<h3>Results</h3>
<div class="player1Results">
<h3>Player 1 Results</h3>
<textarea placeholder="0" disabled></textarea>
</div>
<div class="Player2Results">
<h3>Player 2 Results</h3>
<textarea placeholder="0" disabled></textarea>
</div>
</div>
<body>
<script src="Tic-Tac-Toe.js"></script>
</body>
</html>
The issue is that your currentPlayer variable declaration and initialization needs to live outside the playerControls function or else you're just resetting it to playerOne each time.
let currentPlayer;
currentPlayer = playerOne;
function playerControls(e){...}
Here is the fix in action:
const playgame = (()=> {
const playerOne = {
Name: 'playerOne',
Marking: 'X'
};
const playerTwo = {
Name: 'PlayerTwo',
Marking: 'O'
};
let currentPlayer;
currentPlayer = playerOne;
function playerControls(e){
e.target.textContent = currentPlayer.Marking;
if(currentPlayer === playerOne)
{
currentPlayer = playerTwo;
e.target.textContent = currentPlayer.Marking;
}else{
currentPlayer = playerOne;
e.target.textContent = currentPlayer.Marking;
}
}
return{playerControls}
})();
const gameBoard = (() => {
const makeBoard = (rows, cols) => {
const theBoard = document.getElementById("GameBoard");
theBoard.style.gridTemplateColumns = `repeat(${cols}, 1fr)`;
theBoard.style.gridTemplateRows = `repeat(${rows}, 1fr)`;
for (i=0; i<(rows * cols); i++){
let gameDivs = document.createElement("div");
gameDivs.addEventListener("click", playgame.playerControls)
theBoard.appendChild(gameDivs).classList.add("newdivgrid");
}
};
makeBoard(3,3);
})();
#GameBoard{
width: 600px;
height: 350px;
border: 1px solid darkblue;
margin-left: 27%;
text-align: center;
font-size: 500%;
font-family: cursive, sans-serif;
display: grid;
}
.newdivgrid{
border: 1px solid black;
grid-row-gap: 50px;
}
.newdivgrid:hover{
background-color: aqua;
}
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tic-Tac-Toe game</title>
<link rel="stylesheet" href="Tic-Tac-Toe.css">
</head>
<h1>Tic-Tac-Toe Project</h1>
<div id="PlayerSelectionContainer">
<h2>Select Players</h2>
<div class="Player1">
<h3>Player 1</h3>
<div class="playerSelectionButtons" data-player="playerOne">
<button data-player="human">Human</button>
<button data-player="computer">Computer</button>
</div>
</div>
<div class="Player2">
<h3>Player 2</h3>
<div class="playerSelectionButtons" data-player="playerTwo">
<button data-human-player="human">Human</button>
<button data-computer-player="computer">Computer</button>
</div>
</div>
</div>
<div id="GameBoard">
</div>
<div id="resultContainer">
<h3>Results</h3>
<div class="player1Results">
<h3>Player 1 Results</h3>
<textarea placeholder="0" disabled ></textarea>
</div>
<div class="Player2Results">
<h3>Player 2 Results</h3>
<textarea placeholder="0" disabled ></textarea>
</div>
</div>
<body>
<script src="Tic-Tac-Toe.js"></script>
</body>
</html>
i have a searchbox with a dropdown menu that filters items from the list when you enter letters. My problem is that when i click the X button, it clears the searchbox like expected but it doesn't reset the list of items under the box. Example : if i type javascript we will see javascript under the box but when i click the X button, it will still be written javascript and not the initial list.
So is there any possible way that i can reset it to the initial list using javascript ? ( NOTE : i cannot use jquery or any frameworks)
Thanks a lot.
function filter() {
var value, myList, name;
value = document.getElementById("value").value.toUpperCase();
myList = document.querySelectorAll(".myList > li");
Array.prototype.forEach.call(myList, function(element){
name = element.innerHTML;
element.style.display = name.toUpperCase().indexOf(value) > -1 ? 'list-item' : 'none';
});
}
document.getElementById('value').addEventListener('keyup', filter);
function myFunction() {
document.querySelector('.clearable').value = '';
}
ul {
list-style: none;
margin: 0;
padding: 0;
}
input[type="button"] {
margin-left: -30px;
border: none;
background-color: transparent;
outline: none;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="searchbox">
<input class="clearable" type="text" id="value" placeholder="Search" />
<input type="button" onclick="myFunction()" value="X">
<ul class="myList">
<li class="name">Javascript</li>
<li class="name">Java</li>
<li class="name">CSS</li>
<li class="name">Python</li>
<li class="name">C++</li>
</ul>
</div>
<script src="search.js"></script>
</body>
</html>
You could just reuse the code you have, loop over all of the li elements and re-show them on the myFunction call:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<style>
ul {
list-style: none;
margin: 0;
padding: 0;
}
input[type="button"] {
margin-left: -30px;
border: none;
background-color: transparent;
outline: none;
}
</style>
</head>
<body>
<div class="searchbox">
<input class="clearable" type="text" id="value" placeholder="Search" />
<input type="button" onclick="myFunction()" value="X">
<ul class="myList">
<li class="name">Javascript</li>
<li class="name">Java</li>
<li class="name">CSS</li>
<li class="name">Python</li>
<li class="name">C++</li>
</ul>
</div>
<script>
function filter() {
var value, myList, name;
value = document.getElementById("value").value.toUpperCase();
myList = document.querySelectorAll(".myList > li");
Array.prototype.forEach.call(myList, function(element){
name = element.innerHTML;
element.style.display = name.toUpperCase().indexOf(value) > -1 ? 'list-item' : 'none';
});
}
document.getElementById('value').addEventListener('keyup', filter);
function myFunction() {
var myList;
document.querySelector('.clearable').value = '';
myList = document.querySelectorAll(".myList li");
Array.prototype.forEach.call(myList, function(element){
element.style.display = 'list-item';
});
}
</script>
</body>
</html>