event listener is firing multiple times - javascript

its a quiz app where clicking on replay button is supposed to restart the quiz and the given event listener code is used when the user selects an answer for question. it works completely fine during first time but when the quiz restarts, the event listener fires twice and if again replay is clicked , then the event listener for selecting correct option fires thrice
let chosen = document.getElementsByClassName("option");
console.log('options',chosen);
for (let choosed of chosen) {
choosed.addEventListener("click", (e) => {
clearInterval(myVar);
clearInterval(counterLine);
console.log("you have choosen");
next.classList.add("next-show");
next.classList.remove("next-hide");
if (questionCount == 10) {
finish.classList.add("next-show");
finish.classList.remove("next-hide");
}
let choosenOption = e.target.className;
choosenOption = choosenOption.substr(7, 14);
let correctOption = arrayOFCorrectAnswers[questionCount - 1];
if (correctOption.includes(choosenOption)) {
colorGreen(e.target);
score++;
} else {
colorRed(e.target);
}
});
}
<div class="question-answer">
<div class="question">
<h2 id="question">Question Incoming.....</h2>
</div>
<div class="options-answers">
<div class="option" id="option1">option1 </div>
<div class="option" id="option2">option2</div>
<div class="option" id="option3">option3</div>
<div class="option" id="option4">option4</div>
</div>
</div>
this is the html

I tried to follow the OP example and couldn't understand it very well. I did notice that it is a quiz without any form controls (not even a <button>). If you are interacting with the user and require input from said user it would behoove you to use <form>, <input>, <button>, etc.
The demo below has:
init() which wraps around all of the variables and functions acting as a closure. It passes 3 arrays.
init() is ran only once. It will register the 'submit' event to the <form> and register the 'click' event to a <button>. It also runs spawnQA() which changes the text of the question and the 4 options.
The options are <input type='radio'> and <label>
const questions = ['Question 1', 'Question 2', 'Question 3', 'Question 4'];
const options = [
['1.1', '1.2', '1.3', '1.4'],
['2.1', '2.2', '2.3', '2.4'],
['3.1', '3.2', '3.3', '3.4'],
['4.1', '4.2', '4.3', '4.4']
];
const answers = [1, 0, 3, 2];
const init = (questions, options, answers) => {
const form = document.forms.QA;
const io = form.elements;
const Q = document.getElementById('question');
const inst = document.querySelector('section');
const list = document.querySelector('ol');
const display = document.querySelector('.display');
let count = parseInt(io.counter.value);
let score = parseInt(io.correct.value);
const endQuiz = () => {
Q.textContent = "Quiz Completed";
inst.classList.toggle('hide');
list.classList.toggle('hide');
io.btn.classList.toggle('hide');
io.rst.classList.toggle('hide');
display.textContent = `Score: ${score}`;
return false;
};
const resetQuiz = event => {
inst.classList.toggle('hide');
list.classList.toggle('hide');
io.btn.classList.toggle('hide');
io.rst.classList.toggle('hide');
count = 0;
score = 0;
spawnQA(questions, options, answers, count);
form.reset();
};
const spawnQA = (questions, options, answers, count) => {
if (count >= answers.length) {
endQuiz();
return false;
}
const opts = [...document.querySelectorAll('.option')];
Q.textContent = `${count+1}. ${questions[count]}`;
opts.forEach((label, index) => {
label.textContent = options[count][index];
});
return false;
};
const nextQuestion = event => {
event.preventDefault();
const opts = [...io.options];
for (let [idx, opt] of opts.entries()) {
if (opt.checked) {
score = answers[count] === idx ? ++score : score;
}
}
count++;
io.counter.value = count;
io.correct.value = score;
spawnQA(questions, options, answers, count);
console.log('score: ' + score);
console.log('count: ' + count);
};
form.onsubmit = nextQuestion;
io.rst.onclick = resetQuiz;
spawnQA(questions, options, answers, count);
};
init(questions, options, answers);
.hide {
display: none
}
ol {
list-style-type: lower-alpha;
}
button {
cursor: pointer;
}
input,
label {
display: inline-block;
vertical-align: text-top;
}
/* SO Console Display - Right Side Column */
.as-console-wrapper {
width: 50% !important;
min-height: 100%;
margin-left: 50%;
font-variant: normal;
}
.as-console-row.as-console-row::after {
content: '';
padding: 0;
margin: 0;
border: 0;
width: 0;
}
<form id="QA">
<input id='correct' type='hidden' value='0'>
<input id='counter' type='hidden' value='0'>
<fieldset>
<legend id="question">Question Incoming.....</legend>
<ol>
<li>
<input id='option1' name='options' type='radio'>
<label for='option1' class='option'>Option 1</label>
</li>
<li>
<input id='option2' name='options' type='radio'>
<label for='option2' class='option'>Option 2</label>
</li>
<li>
<input id='option3' name='options' type='radio'>
<label for='option3' class='option'>Option 3</label>
</li>
<li>
<input id='option4' name='options' type='radio'>
<label for='option4' class='option'>Option 4</label>
</li>
</ol>
<section class='hide'>
<p class='display'></p>
<p>Click "Reset" to reset quiz.</p>
</section>
</fieldset>
<button id='btn'>Next</button>
<button id='rst' class='hide' type='button'>Reset</button>
</form>

Related

Is there a good way to have it so that when someone clicks a button, say option 3 on a scale of 1-5, that it would change the color of buttons 1-3?

I am coding a survey page for a client and they want it so that if someone selects "3" out of a 5 button question scale that buttons 1-3 have a green backgroun...4 would have 1-4, etc.
Currently, if you click a button from the number scale only the one you click is highlighted green. What I want to have is so that it changes the background color of all buttons leading up to the one clicked (If you click 2 then it would change the 1 and 2 buttons to green).
Any help appreciated
Main code for the survey here (1 button example- the rest follow the same format):
<section class="l-reviews pt-30 pb-15">
<div class="contain">
<div class="row">
<div class="col-md-12">
<div class="reviews-wrapper">
<div class="reviews-top-header">
<p id="">Thank you for taking part. Please complete this survey to let us know how we’re
doing.</p>
<p>Please rate the following on a 1-5 scale (1 = Least, 5 = Most)</p>
</div>
<div class="reviews-body">
<form method='post' name='front_end' action="">
<div class="form-control">
<p>1. Were the payroll process and benefits options explained to you fully?</p>
<div class="input-holder">
<input type='hidden' name='title' value='' />
<input type='hidden' name='email' value='' />
<input type="radio" data='Unsatisfied' name='satisfaction' value='20' id='sat-1' /><label for="sat-1"></label>
<input type="radio" data='Not Very Satisfied' name='satisfaction' value='40' id='sat-2' /><label for="sat-2"></label>
<input type="radio" data='Neutral' name='satisfaction' value='60' id='sat-3' /><label for="sat-3"></label>
<input type="radio" data='Satisfied' name='satisfaction' value='80' id='sat-4' /><label for="sat-4"></label>
<input type="radio" data='Highly Satisfied' name='satisfaction' value='100' id='sat-5' /><label for="sat-5"></label>
<div class="error">
<p>Please select at least one option.</p>
</div>
</div>
</div>
<button type="button" class="send-btn">Submit</button>
<input type="hidden" name="action" value="review" />
</form>
<div class='success-form'>
<h3>Your review was submitted successfully</h3>
</div>
</div>
CSS for one button:
input[type=radio]:not(.regular-radio) {
display: none;
}
#wr-1+label,
#application-rating-1+label,
#goals-rating-1+label,
#refer-rating-1+label,
#sat-1+label {
background: url('/wp-content/themes/theme52950/images/reviews-faces/button-1.png');
height: 55px;
width: 109px;
display: inline-block;
padding: 0 0 0 0px;
background-repeat: no-repeat;
}
#wr-1:checked+label,
#application-rating-1:checked+label,
#goals-rating-1:checked+label,
#refer-rating-1:checked+label,
#sat-1:checked+label {
background: url('/wp-content/themes/theme52950/images/reviews-faces/1-hover.png');
height: 55px;
width: 109px;
display: inline-block;
padding: 0 0 0 0px;
background-repeat: no-repeat;
}
JavaScript:
<script>
$(document).ready(function() {
console.log(emailsArray);
$('input[type=radio]').click(function() {
avgOne = parseInt($('input[name=satisfaction]:checked').val());
avgTwo = parseInt($('input[name=working_rating]:checked').val());
avgThree = parseInt($('input[name=application_rating]:checked').val())
avgFour = parseInt($('input[name=goals_rating]:checked').val())
avgFive = parseInt($('input[name=refer_rating]:checked').val())
avgOfAll = ((avgOne + avgTwo + avgThree + avgFour + avgFive) / 5);
if (avgOfAll < 80) {
$('.addtl-comments').show();
} else {
$('.addtl-comments').hide();
}
})
if (checkOne && checkTwo && checkThree && checkFour && checkFive && addtlComments && checkEmailExist && emailNotDupe) {
$('.reviews-body form').submit();
const portalId = '3112753';
const formId = '23212b77-0a27-4833-93a9-766bdf8c3a9b';
const url = 'https://api.hsforms.com/submissions/v3/integration/submit/' + portalId + '/' + formId;
const body = {
context: {
pageUri: window.location.href,
pageName: $(document).find("title").text()
},
fields: [{
name: 'email',
value: getUrlParameter('email')
}, {
name: 'how_satisfied_are_you_with_the_overall_experience_in_working_with_',
value: document.querySelector('input[name="satisfaction"]:checked').getAttribute('data')
}, {
name: 'how_would_you_rate_working_with_your_recruiter_',
value: document.querySelector('input[name="working_rating"]:checked').getAttribute('data')
}, {
name: 'how_would_you_rate_the_application_process_',
value: document.querySelector('input[name="application_rating"]:checked').getAttribute('data')
}, {
name: 'how_satisfied_were_you_with_communication_throughout_your_interview_process_',
value: document.querySelector('input[name="goals_rating"]:checked').getAttribute('data')
}, {
name: 'how_likely_would_you_be_to_recommend_to_other_candidates_',
value: document.querySelector('input[name="refer_rating"]:checked').getAttribute('data')
}]
};
Any help appreciated.
A minimal reproducable example would have been sufficient. Just for fun: here's an idea for you:
document.addEventListener(`click`, handle);
function handle(evt) {
if (evt.target.dataset.scoreid) {
const score = +evt.target.dataset.scoreid;
const stars = document.querySelectorAll(`[data-scoreid]`)
.forEach((el, i) =>
el.classList[i <= score ? `add` : `remove`](`starred`));
const rateEl = evt.target.closest(`#rate`);
rateEl.classList.remove(`single`, `score`);
rateEl.classList.add(score + 1 === 1 ? `single` : `scored`);
rateEl.dataset.score = score + 1;
}
}
document.body.insertAdjacentHTML(
`beforeEnd`,
`<div id="rate" date-score="0" data-single="0">
${[...Array(5)].map((v, i) => `<span data-scoreid="${i}"></span>`)
.join(``)}
</div>`);
#rate {
font-size: 2rem;
}
#rate.scored:after {
content: 'you scored 'attr(data-score)' stars' ;
color: green;
}
#rate.single:after {
content: 'you scored 'attr(data-score)' star' ;
color: green;
}
#rate span {
cursor: pointer;
}
#rate span:before {
content: '\2606'
}
#rate span.starred:before {
content: '\2605';
color: green;
}

javascript check all and uncheck all checkbox

I have select all check box with some option when i click select all i select all the option and when i remove the select all i remove it from all option and the code below work for that.
What i try to do is when i unselect one of the option the select all box should be unselected and if i select all the option without selecting the select all option the check all box should be selected.
How can i do that?
let checkboxes = document.querySelectorAll("input[type = 'checkbox']");
function checkAll(myCheckBox) {
if (myCheckBox.checked == true) {
checkboxes.forEach(function(checkbox) {
checkbox.checked = true;
});
} else {
checkboxes.forEach(function(checkbox) {
checkbox.checked = false;
});
}
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
min-height: 100vh;
display: grid;
place-content: center;
}
input[type="checkbox"] {
margin-bottom: 10px;
cursor: pointer;
}
input[type="checkbox"]:not(:first-child) {
margin-left: 20px;
}
<div class="container">
<input type="checkbox" id="check-all" onchange="checkAll(this)">
<label for="check-all">Select All</label>
<br/>
<input type="checkbox" id="option-a">
<label for="option-a">Option A</label>
<br/>
<input type="checkbox" id="option-b">
<label for="option-b">Option B</label>
<br/>
<input type="checkbox" id="option-c">
<label for="option-c">Option C</label>
<br/>
</div>
You can add a change event listener to all of those checkboxes (except for the automatic select all checkbox).
So in this demo I used a disciminant being the class auto that only the "select all" checkbox has.
Then I select all elements being input but not having the class auto.
And for each of those I add an event listener for the change event that will uncheck the "select all" checkbox if any of those was unchecked and that will check the "select all" checkbox if otherwise all of them are checked.
let checkboxes = document.querySelectorAll("input[type='checkbox']");
let cbActual = document.querySelectorAll('input[type=checkbox]:not([class=auto])');
cbActual.forEach(
cb => {
cb.addEventListener('change', (event)=>{
if(!event.target.checked)
document.getElementById('check-all')
.checked = false;
else{
if( [...cbActual].every(cb => cb.checked === true) )
document.getElementById('check-all')
.checked = true;
}
});
}
);
function checkAll(myCheckBox) {
if (myCheckBox.checked == true) {
checkboxes.forEach(function(checkbox) {
checkbox.checked = true;
});
} else {
checkboxes.forEach(function(checkbox) {
checkbox.checked = false;
});
}
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
min-height: 100vh;
display: grid;
place-content: center;
}
input[type="checkbox"] {
margin-bottom: 10px;
cursor: pointer;
}
input[type="checkbox"]:not(:first-child) {
margin-left: 20px;
}
<div class="container">
<input type="checkbox" id="check-all" onchange="checkAll(this)" class="auto">
<label for="check-all">Select All</label>
<br/>
<input type="checkbox" id="option-a">
<label for="option-a">Option A</label>
<br/>
<input type="checkbox" id="option-b">
<label for="option-b">Option B</label>
<br/>
<input type="checkbox" id="option-c">
<label for="option-c">Option C</label>
<br/>
</div>
When checkall is clicked either all or none are selected. When one of the options are checked/unchecked the status of checkall is decided based on the filter function.
I changed the markup of the form a bit. You can "group" check boxes on their name. And try avoiding IDs in a form -- in general it is better to use the name attribute.
document.addEventListener('DOMContentLoaded', e => {
document.forms.form01.addEventListener('change', result_change);
});
function result_change(e) {
let form = e.target.form;
/* e.target.form.option could either be a NodeList or just one Element.
A iterable is needed. */
let options = (form.option.length) ? form.option : [form.option];
switch (e.target.name) {
case 'checkall':
let checked = e.target.checked;
[...options].forEach(option => option.checked = checked);
break;
case 'option':
let allchecked = ([...options].filter(option => !option.checked).length == 0) ? ? true;
form.checkall.checked = allchecked ? true : false;
break;
}
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
min-height: 100vh;
display: grid;
place-content: center;
}
input[type="checkbox"] {
margin-bottom: 10px;
cursor: pointer;
}
input[type="checkbox"]:not(:first-child) {
margin-left: 20px;
}
<form name="form01">
<input type="checkbox" name="checkall">
<label for="check-all">Select All</label>
<br/>
<input type="checkbox" name="option" value="a">
<label for="option-a">Option A</label>
<br/>
<input type="checkbox" name="option" value="b">
<label for="option-b">Option B</label>
<br/>
<input type="checkbox" name="option" value="c">
<label for="option-c">Option C</label>
<br/>
</form>
Prefer to use event propagation to handle the change events of all checkboxes in one listener.
To make use of event propagation, you have to be able to distinguish them from the "Select all" checkbox. To distinuish them, you can:
Use a class (on either the "Select all" or all other checkboxes).
Group the checkboxes (except the "Select all" checkbox) in an element.
Check if the changing checkbox is the first (i.e. the "Select all") checkbox.
...
I chose to use a grouping element so that the "Select all" checkbox is not included:
const cbAll = document.getElementById("cb-all");
const cbGroup = document.getElementById("cb-group");
// NodeList has .forEach() but not .every(). Transform to an array, which has both.
const checkboxes = Array.from(cbGroup.querySelectorAll("input[type=checkbox]"));
cbAll.addEventListener("change", () => {
// If cbAll.checked changes, cbAll.checked should override all other checkboxes' checked.
checkboxes.forEach(cb => cb.checked = cbAll.checked);
});
// This listener will be called if *any* checkbox (in cbGroup) changes.
// Update cbAll.checked to true if all checkboxes are checked; otherwise false.
cbGroup.addEventListener("change", () => {
const areAllChecked = checkboxes.every(cb => cb.checked);
cbAll.checked = areAllChecked;
});
label {display: block}
#cb-group {margin-inline-start: 1.2rem}
<label for="cb-all"><input id="cb-all" type="checkbox"> Select all</label>
<div id="cb-group">
<label for="cb-1"><input id="cb-1" type="checkbox"> First option</label>
<label for="cb-2"><input id="cb-2" type="checkbox"> Second option</label>
<label for="cb-3"><input id="cb-3" type="checkbox"> Third option</label>
</div>
Alternatively you can add listeners to all checkboxes individually.
Checkboxes can also be in a "third" state: Indeterminate. Note: This is not a true state, as checkboxes can only be either checked or unchecked. An indeterminate checkbox hides its checkedness under the pretence of being indeterminate.
This is most commonly used for checkboxes like this "Select all" checkbox; where a checkbox describes the state of a group of checkboxes.
The above example can be modified to make use of it:
const cbAll = document.getElementById("cb-all");
const cbGroup = document.getElementById("cb-group");
const checkboxes = Array.from(cbGroup.querySelectorAll("input[type=checkbox]"));
cbAll.addEventListener("change", () => {
checkboxes.forEach(cb => cb.checked = cbAll.checked);
});
cbGroup.addEventListener("change", () => {
const amountChecked = checkboxes.reduce((amount, cb) => {
// Number(bool) returns 1 if bool === true; otherwise 0.
return amount + Number(cb.checked);
}, 0);
const areAllChecked = amountChecked === checkboxes.length;
const areSomeChecked = amountChecked > 0;
cbAll.checked = areAllChecked;
cbAll.indeterminate = areSomeChecked && !areAllChecked;
});
label {display: block}
#cb-group {margin-inline-start: 1.2rem}
<label for="cb-all"><input id="cb-all" type="checkbox"> Select all</label>
<div id="cb-group">
<label for="cb-1"><input id="cb-1" type="checkbox"> First option</label>
<label for="cb-2"><input id="cb-2" type="checkbox"> Second option</label>
<label for="cb-3"><input id="cb-3" type="checkbox"> Third option</label>
</div>

How do I dynamically get the value of an element from an array of elements?

I have a form with 3 checkboxes. I'm trying to the value of whichever checkbox is clicked on. I'm able to get the value of a hardcoded checkbox index (checkbox[0] for example), but I can't get the value of checkbox[i] with vanilla JS.
document.addEventListener("DOMContentLoaded", function() {
var checkboxes = document.getElementsByClassName('checkbox');
var listType = document.getElementById('ListType');
for (var i = 0; i < checkboxes.length; i++) {
checkboxes[i].addEventListener('click', function() {
var inputByIndex = checkboxes[0].value; //I can get the value of the first element, but I can't get the value of whichever checkbox is checked. checkbox[i] doesn't work.
listType.classList.add(inputByIndex);
var spanType = document.getElementById("type");
spanType.innerText = inputByIndex;
});
}
});
input {
margin: 20px;
}
#ListType.basiclist {
color: red;
}
#ListType.accordionlist {
color: blue;
}
#ListType.internalonly {
color: pink;
}
<form id="ListTypes">
<label for "basicList"><input type="checkbox" id="basicList" class="checkbox" name="basicList" value="basiclist"/>Basic List</label>
<label for "accordionList"><input type="checkbox" id="accordionList" class="checkbox" name="accordionList" value="accordionlist"/>Accordion List</label>
<label for "internalOnly"><input type="checkbox" id="internalOnly" class="checkbox" name="internalOnly" value="internalonly" />Internal Use Only</label>
</form>
<div id="ListType">
List Type: <span id="type"></span>
</div>
Fiddle
You can use event.currentTarget to access the element on which event has occurred.
The currentTarget read-only property of the Event interface identifies the current target for the event, as the event traverses the DOM.
document.addEventListener("DOMContentLoaded", function() {
var checkboxes = document.getElementsByClassName('checkbox');
var listType = document.getElementById('ListType');
for (var i = 0; i < checkboxes.length; i++) {
checkboxes[i].addEventListener('click', function(event) {
var inputByIndex = event.currentTarget.value;
listType.classList.add(inputByIndex);
var spanType = document.getElementById("type");
spanType.innerText = inputByIndex;
});
}
});
input {
margin: 20px;
}
#ListType.basiclist {
color: red;
}
#ListType.accordionlist {
color: blue;
}
#ListType.internalonly {
color: pink;
}
<form id="ListTypes">
<label for "basicList"><input type="checkbox" id="basicList" class="checkbox" name="basicList" value="basiclist"/>Basic List</label>
<label for "accordionList"><input type="checkbox" id="accordionList" class="checkbox" name="accordionList" value="accordionlist"/>Accordion List</label>
<label for "internalOnly"><input type="checkbox" id="internalOnly" class="checkbox" name="internalOnly" value="internalonly" />Internal Use Only</label>
</form>
<div id="ListType">
List Type: <span id="type"></span>
</div>
In the for loop, use let instead of var to make it work:
document.addEventListener("DOMContentLoaded", function() {
var checkboxes = document.getElementsByClassName('checkbox');
var listType = document.getElementById('ListType');
for (let i = 0; i < checkboxes.length; i++) {
checkboxes[i].addEventListener('click', function() {
var inputByIndex = checkboxes[i].value; //I can get the value of the first element, but I can't get the value of whichever checkbox is checked. checkbox[i] doesn't work.
listType.classList.add(inputByIndex);
var spanType = document.getElementById("type");
spanType.innerText = inputByIndex;
});
}
});
input {
margin: 20px;
}
#ListType.basiclist {
color: red;
}
#ListType.accordionlist {
color: blue;
}
#ListType.internalonly {
color: pink;
}
<form id="ListTypes">
<label for "basicList"><input type="checkbox" id="basicList" class="checkbox" name="basicList" value="basiclist"/>Basic List</label>
<label for "accordionList"><input type="checkbox" id="accordionList" class="checkbox" name="accordionList" value="accordionlist"/>Accordion List</label>
<label for "internalOnly"><input type="checkbox" id="internalOnly" class="checkbox" name="internalOnly" value="internalonly" />Internal Use Only</label>
</form>
<div id="ListType">
List Type: <span id="type"></span>
</div>
the checkboxes list doesn't exist within the closure of the onclick funcion. Instead use this.value.
JS fiddle
Delegate
You need to think of the CSS for more than one listType color or use a set of radio buttons
document.addEventListener("DOMContentLoaded", function() {
document.getElementById('ListTypes').addEventListener("click", function(e) {
const tgt = e.target;
if (tgt.type && tgt.type === 'checkbox') {
const values = [...tgt.form.querySelectorAll("[type=checkbox]:checked")].map(chk => chk.value);
document.getElementById("type").textContent = values.join(", ")
document.getElementById("ListType").classList.add(...values);
}
});
});
input {
margin: 20px;
}
#ListType.basiclist {
color: red;
}
#ListType.accordionlist {
color: blue;
}
#ListType.internalonly {
color: pink;
}
<form id="ListTypes">
<label for "basicList"><input type="checkbox" id="basicList" class="checkbox" name="basicList" value="basiclist"/>Basic List</label>
<label for "accordionList"><input type="checkbox" id="accordionList" class="checkbox" name="accordionList" value="accordionlist"/>Accordion List</label>
<label for "internalOnly"><input type="checkbox" id="internalOnly" class="checkbox" name="internalOnly" value="internalonly" />Internal Use Only</label>
</form>
<div id="ListType">
List Type: <span id="type"></span>
</div>
You just need to select them all using the method you would like (I used querySelectorAll & ) and do an iteration over them (I used forEach()).
This is the most simple function you can ever find.
const checkboxes = document.querySelectorAll('input[type="checkbox"]');
checkboxes.forEach(singleCheckbox => {
singleCheckbox.addEventListener('click', () => alert(singleCheckbox.id))
});
<label for="first">First<input type="checkbox" id="first"/></label>
<label for="second">Second<input type="checkbox" id="second"/></label>
<label for="third">Third<input type="checkbox" id="third"/></label>
Just to make clear what was actually the problem with your code...
At the time you click handler will be fired the for loop will end its work and thus, the value of i will become exactly checkboxes.length, and of course, there is no checkbox with such an index, because the last of them has index (checkboxes.length - 1). So the reason is that the code inside of the handler is executed after for loop ends its work.
The solutions were already provided by other users.

Update quiz tracker answer DIVs using JavaScript

I am developing a simple quiz app with vanilla JavaScript and HTML. The functionality to know if the chosen answer is correct or incorrect and display some feedback is working, but not the functionality to set the .quiz__marker div's data-attribute to "Correct" or "Incorrect" when the question's radio button is selected. My simplified code is included in the snippet below.
I have set the .quiz__marker divs to either be green or red depending on if the correct or incorrect answer is selected. As you can see, all of the circles are set to green or red when the radio button is selected.
How should I adjust my JS code to only apply the color change to a single circle? Also, what would I need to implement to have the color maintained throughout the quiz duration?
I am learning JavaScript as I go. Vanilla JS answers are preferred.
Cheers!
Update - Included full quiz.js for review purposes
function answerTracker() {
const tracker = document.querySelector(".quiz__tracker");
// Each quiz contains eight or less questions
const questions = 8;
for (let i = 1; i <= questions; i++) {
let trackerMarker = document.createElement("div");
trackerMarker.className = "quiz__marker";
trackerMarker.id = "marker_" + i;
trackerMarker.setAttribute("data-answer", "");
tracker.appendChild(trackerMarker);
}
}
function questionMarkers() {
let labels = document.querySelectorAll("div[class*='rsform-block-question'] .formControlLabel");
let count = labels.length;
for (let i = 0; i < count; i++) {
let marker = `<span class="question-marker">${i + 1}</span>`;
labels[i].insertAdjacentHTML("beforebegin", marker);
}
}
const runQuiz = (() => {
// Generate tracker
answerTracker();
// Append question markers to labels
questionMarkers();
// Set quiz score to zero
let score = 0;
let markers = document.querySelectorAll(".quiz__marker");
let answers = document.querySelectorAll(".rsform-radio");
answers.forEach(function (answer) {
answer.addEventListener("click", (e) => {
let target = e.target;
let container = target.closest(".formContainer");
let correct = container.querySelector("div[class$='true']");
let wrong = container.querySelector("div[class$='false']");
let feedback = container.querySelector("div[class$='feedback']");
let question = container.querySelector("div[class*='rsform-block-question']");
let next = container.querySelector(".js-btn--next.success");
let submit = container.querySelector(".js-btn--submit.rsform-submit-button");
if (e.target.value == "t") {
// If answer is correct, increment the user's score by one
score++;
correct.style.display = "block";
wrong.style.display = "none";
feedback.style.display = "block";
document.getElementById("marker_" + e.target.dataset.num).setAttribute("data-answer", "Correct");
} else {
correct.style.display = "none";
wrong.style.display = "block";
feedback.style.display = "block";
document.getElementById("marker_" + e.target.dataset.num).setAttribute("data-answer", "Incorrect");
}
if (next !== null && next !== "") {
next.style.display = "block";
}
if (submit !== null && submit !== "") {
submit.style.display = "block";
}
question.style.display = "none";
});
});
// If this is the final question in the quiz, assign the score variable to the final-score hidden HTML field
// document.getElementById("final-score").value = score;
})();
function answerTracker() {
const tracker = document.querySelector(".quiz__sidebar");
// Each quiz contains eight questions
const questions = 8;
for (let i = 1; i <= questions; i++) {
let trackerMarker = document.createElement("div");
trackerMarker.className = "quiz__marker";
trackerMarker.setAttribute("data-answer", "");
tracker.appendChild(trackerMarker);
}
}
const runQuiz = (() => {
// Generate answer tracker
answerTracker();
// Set quiz score to zero
let score = 0;
// Get node list of .quiz__marker DIVs
let markers = document.querySelectorAll(".quiz__marker");
// Get node list of answer radio buttons
let answers = document.querySelectorAll(".rsform-radio");
answers.forEach(function(answer) {
answer.addEventListener("click", (e) => {
let target = e.target;
let container = target.closest(".formContainer");
let correct = container.querySelector("div[class$='true']");
let wrong = container.querySelector("div[class$='false']");
let feedback = container.querySelector("div[class$='feedback']");
let question = container.querySelector("div[class*='rsform-block-question']");
let next = container.querySelector(".js-btn--next.success");
if (e.target.value == "t") {
// If answer is correct, increment the user's score by one
score++;
correct.style.display = "block";
wrong.style.display = "none";
feedback.style.display = "block";
} else {
correct.style.display = "none";
wrong.style.display = "block";
feedback.style.display = "block";
}
// Loop through node list and set data-attribute to "Correct" or "Incorrect" depending on e.target value
Array.from(markers).forEach(link => {
if (e.target.value == "t") {
link.setAttribute("data-answer", "Correct");
} else {
link.setAttribute("data-answer", "Incorrect");
}
});
// If question has been answered, display the next button
if (next !== null && next !== "") {
next.style.display = "block";
}
});
});
// If this is the final question in the quiz, assign the score variable to the final-score hidden HTML field
// document.getElementById("final-score").value = score;
})();
.quiz__sidebar .quiz__marker {
display: inline-block;
width: 20px;
height: 20px;
margin: 6px;
background-color: grey;
border-radius: 50%;
}
.quiz__sidebar .quiz__marker[data-answer="Correct"] {
background-color: green;
}
.quiz__sidebar .quiz__marker[data-answer="Incorrect"] {
background-color: red;
}
form.quiz__form .formHidden,
form.quiz__form div[class$="true"],
form.quiz__form div[class$="false"],
form.quiz__form div[class$="feedback"],
form.quiz__form .rsform-block .rsform-button {
display: none;
}
<div class="quiz">
<div class="quiz__sidebar"></div>
<form class="quiz__form">
<div class="formContainer">
<div class="rsform-block-question-one">
<input type="radio" name="form[question-one]" value="f" id="question-one0" class="rsform-radio">
<label for="question-one0">To attract a mate</label>
<br>
<input type="radio" name="form[question-one]" value="t" id="question-one1" class="rsform-radio">
<label for="question-one1">To defend themselves against predators</label>
<br>
<input type="radio" name="form[question-one]" value="f" id="question-one2" class="rsform-radio">
<label for="question-one2">To mark their territory</label>
<br>
<input type="radio" name="form[question-one]" value="f" id="question-one3" class="rsform-radio">
<label for="question-one3">Because they like the smell</label>
</div>
<div class="rsform-block-question-one-true">
<p>
<strong>Correct!</strong>
</p>
</div>
<div class="rsform-block-question-one-false">
<p>
<strong>Nice try</strong>
</p>
</div>
<div class="rsform-block-question-one-feedback">
<p>Skunks spray an extremely offensive odour to make predators go away and leave them alone. If they feel their life is in danger, they have no other defence as they cannot run fast, climb, burrow under the ground, or fight.</p>
</div>
<div class="rsform-block">
<button type="button" class="btn--next js-btn--next rsform-button success button" style="display: none;">
Next
</button>
</div>
</div>
<div class="formContainer formHidden">
<div class="rsform-block-question-two">
<input type="radio" name="form[question-two]" value="f" id="question-two0" class="rsform-radio">
<label for="question-two0">1 cup</label>
<br>
<input type="radio" name="form[question-two]" value="f" id="question-two1" class="rsform-radio">
<label for="question-two1">Endless supply</label>
<br>
<input type="radio" name="form[question-two]" value="t" id="question-two2" class="rsform-radio">
<label for="question-two2">2 tbsp</label>
<br>
<input type="radio" name="form[question-two]" value="f" id="question-two3" class="rsform-radio">
<label for="question-two3">1 tsp</label>
</div>
<div class="rsform-block-question-two-true">
<p>
<strong>Correct!</strong>
</p>
</div>
<div class="rsform-block-question-two-false">
<p>
<strong>Nice try</strong>
</p>
</div>
<div class="rsform-block-question-two-feedback">
<p>Skunks have only 2 tbsp (30ml) of spray and it takes them more than a week to re-supply. They cannot spray every day as they will simply run out. Skunks use spraying as a last resort only as if they run out, they are defenceless against predators.</p>
</div>
<div class="rsform-block">
<button type="button" class="btn--next js-btn--next rsform-button success button" style="display: none;">
Next
</button>
</div>
</div>
</form>
</div>
You need to add some characteristic "markers" for the quiz markers, for example an id with the question number:
for (let i = 1; i <= questions; i++) {
let trackerMarker = document.createElement("div");
trackerMarker.className = "quiz__marker";
trackerMarker.id = "marker_" + i; // specific id for each marker
trackerMarker.setAttribute("data-answer", "");
tracker.appendChild(trackerMarker);
}
and then don't loop through every marker - set the color for the marker with the same number as the question. Something like:
document.getElementById("marker_" + questionNumber).setAttribute("data-answer", "Correct");
I can't see the code to handle click on the next button, but it doesn't matter for the question.
function answerTracker() {
const tracker = document.querySelector(".quiz__sidebar");
// Each quiz contains eight questions
const questions = 8;
for (let i = 1; i <= questions; i++) {
let trackerMarker = document.createElement("div");
trackerMarker.className = "quiz__marker";
trackerMarker.id = "marker_" + i;
trackerMarker.setAttribute("data-answer", "");
tracker.appendChild(trackerMarker);
}
}
const runQuiz = (() => {
// Generate answer tracker
answerTracker();
// Set quiz score to zero
let score = 0;
// Get node list of .quiz__marker DIVs
let markers = document.querySelectorAll(".quiz__marker");
// Get node list of answer radio buttons
let answers = document.querySelectorAll(".rsform-radio");
answers.forEach(function(answer) {
answer.addEventListener("click", (e) => {
let target = e.target;
let container = target.closest(".formContainer");
let correct = container.querySelector("div[class$='true']");
let wrong = container.querySelector("div[class$='false']");
let feedback = container.querySelector("div[class$='feedback']");
let question = container.querySelector("div[class*='rsform-block-question']");
let next = container.querySelector(".js-btn--next.success");
if (e.target.value == "t") {
// If answer is correct, increment the user's score by one
score++;
correct.style.display = "block";
wrong.style.display = "none";
feedback.style.display = "block";
document.getElementById("marker_" + e.target.dataset.num).setAttribute("data-answer", "Correct");
} else {
correct.style.display = "none";
wrong.style.display = "block";
feedback.style.display = "block";
document.getElementById("marker_" + e.target.dataset.num).setAttribute("data-answer", "Incorrect");
}
// If question has been answered, display the next button
if (next !== null && next !== "") {
next.style.display = "block";
}
});
});
// If this is the final question in the quiz, assign the score variable to the final-score hidden HTML field
// document.getElementById("final-score").value = score;
})();
.quiz__sidebar .quiz__marker {
display: inline-block;
width: 20px;
height: 20px;
margin: 6px;
background-color: grey;
border-radius: 50%;
}
.quiz__sidebar .quiz__marker[data-answer="Correct"] {
background-color: green;
}
.quiz__sidebar .quiz__marker[data-answer="Incorrect"] {
background-color: red;
}
form.quiz__form .formHidden,
form.quiz__form div[class$="true"],
form.quiz__form div[class$="false"],
form.quiz__form div[class$="feedback"],
form.quiz__form .rsform-block .rsform-button {
display: none;
}
<div class="quiz">
<div class="quiz__sidebar"></div>
<form class="quiz__form">
<div class="formContainer">
<div class="rsform-block-question-one">
<input type="radio" name="form[question-one]" data-num="1" value="f" id="question-one0" class="rsform-radio">
<label for="question-one0">To attract a mate</label>
<br>
<input type="radio" name="form[question-one]" data-num="1" value="t" id="question-one1" class="rsform-radio">
<label for="question-one1">To defend themselves against predators</label>
<br>
<input type="radio" name="form[question-one]" data-num="1" value="f" id="question-one2" class="rsform-radio">
<label for="question-one2">To mark their territory</label>
<br>
<input type="radio" name="form[question-one]" data-num="1" value="f" id="question-one3" class="rsform-radio">
<label for="question-one3">Because they like the smell</label>
</div>
<div class="rsform-block-question-one-true">
<p>
<strong>Correct!</strong>
</p>
</div>
<div class="rsform-block-question-one-false">
<p>
<strong>Nice try</strong>
</p>
</div>
<div class="rsform-block-question-one-feedback">
<p>Skunks spray an extremely offensive odour to make predators go away and leave them alone. If they feel their life is in danger, they have no other defence as they cannot run fast, climb, burrow under the ground, or fight.</p>
</div>
<div class="rsform-block">
<button type="button" class="btn--next js-btn--next rsform-button success button" style="display: none;">
Next
</button>
</div>
</div>
<div class="formContainer formHidden">
<div class="rsform-block-question-two">
<input type="radio" name="form[question-two]" data-num="2" value="f" id="question-two0" class="rsform-radio">
<label for="question-two0">1 cup</label>
<br>
<input type="radio" name="form[question-two]" data-num="2" value="f" id="question-two1" class="rsform-radio">
<label for="question-two1">Endless supply</label>
<br>
<input type="radio" name="form[question-two]" data-num="2" value="t" id="question-two2" class="rsform-radio">
<label for="question-two2">2 tbsp</label>
<br>
<input type="radio" name="form[question-two]" data-num="2" value="f" id="question-two3" class="rsform-radio">
<label for="question-two3">1 tsp</label>
</div>
<div class="rsform-block-question-two-true">
<p>
<strong>Correct!</strong>
</p>
</div>
<div class="rsform-block-question-two-false">
<p>
<strong>Nice try</strong>
</p>
</div>
<div class="rsform-block-question-two-feedback">
<p>Skunks have only 2 tbsp (30ml) of spray and it takes them more than a week to re-supply. They cannot spray every day as they will simply run out. Skunks use spraying as a last resort only as if they run out, they are defenceless against predators.</p>
</div>
<div class="rsform-block">
<button type="button" class="btn--next js-btn--next rsform-button success button" style="display: none;">
Next
</button>
</div>
</div>
</form>
</div>

How to write code for counts prices from checked inputs

I started to learn javascript 2 weeks ago. I want to do my first program which counts all prices from checked input.
But i dont have idea how to do this.
I found something on internet and stackoverflow, but i dont know to do this for my inputs.
Could you help me?
My code
// this function counts all the values
function showPrice() {
var priceOne = document.getElementsByName('price1');
var priceTwo = document.getElementsByName('price2');
var priceThree = document.getElementsByName('price3');
}
You can do this by getting a list of elements for all the inputs, check the ones that are checked, get their values, convert them to numbers and sum them.
Beginning with a list of ids, use Array.map() to transform the list into a list of string ids (1 -> test1).
Then apply getElementById() to each id to get the HTML element.
Then from the element extract and convert the value if the input is checked, otherwise map the element to 0.
Use Array.reduce to sum the values.
Using getElementById, find your output span and set its innerHTML to the total.
function showPrice() {
const total = [1,2,3,4,5,6,7,8,9]
.map(id => `test${id}`)
.map(id => document.getElementById(id))
.map(el => el && el.checked ? Number(el.value) : 0)
.reduce((sum, value) => sum + value, 0);
document.getElementById('getPrice').innerHTML = total;
}
.priceInput {
display: block;
border: 1px solid black;
padding: 10px;
width: 300px;
text-align: center;
margin-left: auto;
margin-right: auto;
}
.priceOutput {
display: block;
border: 1px solid black;
padding: 10px;
width: 300px;
text-align: center;
margin-left: auto;
margin-right: auto;
}
<div class="priceInput">
<p>Part 1</p>
<label for="test1">test1</label>
<input type="radio" name="price1" id="test1" value="10">
<label for="test2">test2</label>
<input type="radio" name="price1" id="test2" value="25">
<label for="test3">test3</label>
<input type="radio" name="price1" id="test3" value="12">
</div>
<div class="priceInput">
<p>Part 2</p>
<label for="test4">test4</label>
<input type="radio" name="price2" id="test4" value="5">
<label for="test5">test5</label>
<input type="radio" name="price2" id="test5" value="7">
<label for="test6">test6</label>
<input type="radio" name="price2" id="test6" value="2">
</div>
<div class="priceInput">
<p>Part 3</p>
<label for="test7">test7</label>
<input type="radio" name="price3" id="test7" value="250">
<label for="test8">test8</label>
<input type="radio" name="price3" id="test8" value="720">
<label for="test9">test9</label>
<input type="radio" name="price3" id="test9" value="410">
</div>
<br>
<div class="priceOutput">
<button onclick="showPrice()">Show Price</button>
<p>Your Price is : <span id="getPrice">0<!--Here i want to get price1(value) + price2(value) + price3(value)--></span> $</p>
</div>
By looking at your code, it seems that you want to get the option the user selected out of the radio buttons and not all radio buttons (referring to the getElementsByName). To do so, you can go over the nodeList returned and see if any of the elements have the checked property.
for(let i = 0; i < priceOne.length; i++){
if(priceOne[i].checked) {
//Get value of the price selected with .value
let priceOneValue = priceOne[i].value;
}
}
After doing this for all your input types, you can sum them up.
JO_VA here is my own code. Can you check it? It's easier for my understanding.
I found just 1 problem and it is : when i remove from HTML input tag "checked" and i klikn only for 1 or 2 radio options, it show NaN in paragraph for price.
BTW sorry for my english (i ussually dont use english language)
function showPrice(){
var pricePlace = document.getElementById("getPrice");
getVal1();
getVal2();
getVal3();
var InputNumber = Number(inputVal1) + Number(inputVal2) + Number(inputVal3);
pricePlace.innerHTML = InputNumber;
}
var inputVal1;
function getVal1(){
var a = document.getElementById("test1");
var b = document.getElementById("test2");
var c = document.getElementById("test3");
if (c.checked) {
inputVal1 = c.value;
}
if (b.checked) {
inputVal1 = b.value;
}
if (a.checked) {
inputVal1 = a.value;
}
}
var inputVal2;
function getVal2(){
var a = document.getElementById("test4");
var b = document.getElementById("test5");
var c = document.getElementById("test6");
if (c.checked) {
inputVal2 = c.value;
}
if (b.checked) {
inputVal2 = b.value;
}
if (a.checked) {
inputVal2 = a.value;
}
}
var inputVal3;
function getVal3(){
var a = document.getElementById("test7");
var b = document.getElementById("test8");
var c = document.getElementById("test9");
if (c.checked) {
inputVal3 = c.value;
}
if (b.checked) {
inputVal3 = b.value;
}
if (a.checked) {
inputVal3 = a.value;
}
}
Or you can check it in https://codepen.io/soorta/pen/gqEGyQ

Categories

Resources