Background
I'm working on a survey in javascript which contains ten "yes" or "no" questions. I'd like to code the "yes" responses to = 1 and the "no" responses to = 0. I'm using the html tag so they can only choose one or the other.
Then I'd like to have the sum of their responses added up and divided by the total number of questions to yield a percentage. Depending on their percentage, I would then like to output some HTML text to a field below it with some helpful tips and advice relevant to their score.
To be clear, though I'm learning javascript now, I don't expect anyone to code the thing for me, but I'd just like to know where I should be looking for answers to make this happen. I'm thinking I need to use the if/else conditions to account for the yes/no responses and then maybe I need another function() to do the rest of the calculations.
I've tried a number of different variations of if/else statements but I'm just confused about how to 1) code the yes/no responses; 2) integrate them into if/else statements.
I'll add an abbreviated snippet of code with two sample questions that I'm trying to get working now.
function calc (form) {
var score1;
var score2;
var Result = form.Result.value;
if (form.Question1.options[form.Question1.selectedIndex].value = "Yes") { score1 = 1;
} else {
score1 = 0;
}
if (form.Question2.options[form.Question2.selectedIndex].value = "Yes") { score2 = 1;
} else {
score2 = 0;
}
Result = (((score1 + score2) / 10) * 100);
if (Result == 80) {
document.getElementById("recommendation").innerHTML = "<h4>Heading here</h4> <p>Advice goes here based on their result</p>"
}
}
To be fair, I believe you need to rethink your approach. Having an if-then-else approach for each question & answer can be quite tedious in maintaining or when you want to change the answers.
If you would create a data structure for your questions, you could use for-loops or saving indexes of the current question to handle an answer. The correct answer would then also be part of your data structure (but that is up to you, it could be a client/server request as well).
You already got some answers why your current approach doesn't work due to missing up assignment with equality operators, but I thought I would give you an alternative solution.
This solution will create a dynamic ui, and handle answers on questions when the next button is clicked. The questionaire here is unidirectional, you can only go forward :)
It is mainly to give you an idea how to approach it differently. I don't imagine you actually using this code as is.
The code has quite some inline comments, and is based on the usage of a generator function (which are not supported by Internet Explorer, but should be fine in any other browser)
Upon completion, the score would be displayed in a message box.
// data structure that takes question / answer / which is correct and the points attributed
const questions = [
{
question: 'What platform are you on?',
answers: ['Stackoverflow', 'codereview'],
correct: 0,
points: 5
},
{
question: 'What is the answer to everything',
answers: [42, 'I don\'t have a clue'],
correct: 0,
points: 1
},
{
question: 'How much is 7*6',
answers: ['I am not good with maths', 42],
correct: 1,
points: 10
}
];
// a simple generator that is used in the questionaire
function *questionsGenerator( questions ) {
yield* questions;
}
// creates a questionaire, with forward only options (due to the use of the generator function)
// all variables are locally scoped, when all questions were answered, the onCompleted callback would be called
// it returns an object with nextQuestion function though it could call nextButton internally, and you just have to call the function once if you would want to change it
const questionaire = ( query, nextButton, target, onCompleted ) => {
let score = 0;
let iterator = questionsGenerator( query );
let question = null;
let selectedAnswer = -1;
nextButton.addEventListener('click', nextQuestion);
function evaluateAnswer() {
if (!question) {
// no question yet
return;
}
if (selectedAnswer < 0) {
return;
}
if (question.correct === selectedAnswer) {
score += question.points;
}
return;
}
function nextQuestion() {
evaluateAnswer();
question = iterator.next();
// this is a bit of a hack to check if we just had the last question or not
if (question.done) {
nextButton.removeEventListener('click', nextQuestion);
onCompleted( score );
return;
}
question = question.value;
drawUI();
}
function drawUI() {
// disable next button
nextButton.setAttribute('disabled', true);
selectedAnswer = -1;
// remove existing items
Array.from( target.childNodes ).forEach( child => target.removeChild( child ) );
// create new questions (if available)
const title = document.createElement('h1');
title.innerHTML = question.question;
target.appendChild( title );
question.answers.map( (answer, i) => {
const el = document.createElement('input');
el.type = 'radio';
el.name = 'answer';
el.value = i;
el.id = 'answer' + i;
el.addEventListener('change', () => {
selectedAnswer = i;
nextButton.removeAttribute('disabled');
} );
const label = document.createElement('label');
label.setAttribute('for', el.id );
label.innerHTML = answer;
const container = document.createElement('div');
container.appendChild(el);
container.appendChild(label);
return container;
} ).forEach( a => target.appendChild( a ) );
}
return {
nextQuestion
}
};
// create a questionaire and start the first question
questionaire(
questions,
document.querySelector('#btnNext'),
document.querySelector('#questionaire'),
score => alert('You scored ' + score )
).nextQuestion();
<div id="questionaire">
</div>
<div class="toolstrip">
<button id="btnNext" type="button">Next</button>
</div>
If you want to check for equality use == or ===. == will check if the values are the same,
=== will also check if the types are the same.
For example:
0 == "0" => true
0 === "0" => false.
Related
I am not understanding why one method of checking the answer and updating the poll for the answers array in the poll object works while the other doesn't. my if else method adds a +1 for the correct answer but when the button is clicked again it's as if previous clicks never registered.
thank you!
const poll = {
question: 'whats your fav programming language',
options: ['0: JS', '1 : Python', '2:Rust', '3: C++'],
// this generates [0,0,0,0]
answers: new Array(4).fill(0),
registerNewAnswer() {
// get answer
const answer = Number(
prompt(
`${this.question}\n${this.options.join('\n')}\n(write option number)`
)
);
// register answer
// code below works but doesn't update the answers array if i click the button multiple times
// if(answer > 0 && answer < 5){
// this.answers[answer]++
// } else {
// alert("invalid number")
// };
// the short circuiting method below works and the answers array stays updated
typeof answer === 'number' &&
answer < this.answers.length &&
this.answers[answer]++;
this.displayresults();
this.displayresults('string')
},
displayresults(type = 'array'){
if(type === 'string'){
console.log(`poll results are ${this.answers.join(', ')}`)
} else if (type === 'array'){
console.log(this.answers)
};
}
};
document
.querySelector('.poll')
.addEventListener('click', poll.registerNewAnswer.bind(poll))
I've created an array of objects that I need my for loop to go through. However, when I run this code, I only ever seen the first question and its answers. What am I doing wrong? Thank you in advance!
// An array of objects containing questions, answers, and the correct answers.
const questionArray = [
{
Q: "TestingQ1",
A: [
"Testing2",
"Testing3",
"Testing4",
"Testing5"
],
Correct: "Testing2"
},
{
Q: "TestingQ2",
A: [
"Testing2-2",
"Testing3-2",
"Testing4-2",
"Testing5-2"
],
Correct: "Testing3-2"
}
];
// Set to zero to target the first index in an array of objects.
let questionIndex = 0;
// showQuestions is equal to elements with the "show-questions" class. Here, a section.
let showQuestions = document.querySelector(".show-questions");
// showAnswers is equal to the elements in the "show-answers" class. Here, a section.
let showAnswers = document.querySelector(".show-answers");
// results is equal to the "results" id. Here, a span.
let results = document.querySelector("#results");
// Create a function that displays questions
function displayQuestions() {
showQuestions.textContent = questionArray[questionIndex].Q;
console.log(showAnswers);
for (let i = 0; i < questionArray[questionIndex].A.length; i++) {
let answerButton = document.createElement("button");
answerButton.textContent = questionArray[questionIndex].A[i];
// Define the function here to check the answers
answerButton.onclick = function checkAnswers() {
// If the submission is equal to Correct...
if (answerButton.innerText === questionArray[questionIndex].Correct) {
// ...show this confirmation message.
results.textContent = "Right on, popcorn! That's correct.";
console.log(checkAnswers);
// If the submission is not equal to Correct...
} else {
// ...show this error message and...
results.textContent = "Sorry, no such luck. Try again!";
// ...deduct 10 seconds from the clock.
secondsLeft -= 10;
}
};
showAnswers.appendChild(answerButton);
}
You're not seeing a loop through your other questions because your for loop is bound to your first object.
for (let i = 0; i < questionArray[questionIndex].A.length; i++)
This means that your for loop should end with the length of the answers in your first object. After this your loop should close. To get your loop to go through all objects you can go through two ways per my knowledge:
My preferred way
A forEach loop on your array and then the for loop for each object as your have done.
Or
You can probably create a first forLoop for your array objects before the inner forLoop which you you have.
This way, there is a loop going through your questions and a loop to find your answer.
I hope this guides you on how to go about it.
You need to loop through all elements in that array. You are only displaying the first question.
Read more about forEach method on arrays here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
You should have something like:
function displayQuestions() {
questionArray.forEach(function (question, index) {
// Create a new div for each question.
let questionDiv = document.createElement("div");
// Add a class to the new div.
questionDiv.classList.add("question");
// Add the question to the new div.
questionDiv.innerHTML = question.Q;
// Append the new div to the showQuestions section.
showQuestions.appendChild(questionDiv);
// Create a new div for each answer.
let answerDiv = document.createElement("div");
// Add a class to the new div.
answerDiv.classList.add("answer");
// Add the answers to the new div.
question.A.forEach(function (answer) {
answerDiv.innerHTML += `<button class="answer-button">${answer}</button>`;
});
// Append the new div to the showQuestions section.
showQuestions.appendChild(answerDiv);
});
Check my git repo same application , and check the quiz.js file
I also attached the quiz.js code here
repo link: https://github.com/abdulhaseeb036/quiz-website
quiz.js code:
var questions = [
{
id : 1,
question : "Who is the owner of this Quiz app",
answer : "Haseeb Alam Rafiq",
option : [
"Haseeb Alam Rafiq",
"Muhammad Wasi",
"Mark zinger Burger",
"None of these"
]
},
{
id : 2,
question : "When was MR HASEEB ALAM born?",
answer : "12-dec-2000",
option : [
"13-aug-2000",
"12-may-1999",
"1-march-2001",
"12-dec-2000"
]
},
{
id : 3,
question : "Which university MR HASEEB ALAM RAFIQ complete Becholars degree?",
answer : "Lumber 1 university",
option : [
"Baharia university ",
"Lumber 1 university",
"IBM university",
"DHA suffah university"
]
},
]
// var welcome = document.getElementById(location.href="../index.html" ,"first-ip");
// console.log(welcome.value);
// counter
var counter = 0;
var userPoints = 0;
function nextq() {
var userAns = document.querySelector("li.option.active").innerHTML;
if (userAns == questions[counter].answer){
userPoints = userPoints + 5;
sessionStorage.setItem("points" ,userPoints);
}
if (counter == questions.length -1) {
location.href = "../end.html";
return;
}
console.log(userPoints);
counter++;
show(counter);
}
// on every onload mean refresh this function calls and in this function
// show(counter) function calls
window.onload = function() {
show(counter); //here counter value 0 means first q render on refresh page.
}
// this function render onclick new question with options
function show(counter) {
var question = document.getElementById("questions");
question.innerHTML=`
<h2>Q${counter +1}. ${questions[counter].question}</h2>
<ul>
<li class="option">${questions[counter].option[0]}</li>
<li class="option">${questions[counter].option[1]}</li>
<li class="option">${questions[counter].option[2]}</li>
<li class="option">${questions[counter].option[3]}</li>
</ul>
`;
toggleActive();
}
function toggleActive() {
let option1 = document.querySelectorAll("li.option");
for(let i = 0; i< option1.length; i++){
option1[i].onclick =function() {
for(let j = 0; j< option1.length; j++){
if(option1[j].classList.contains("active")){
option1[j].classList.remove("active");
}
option1[i].classList.add("active");
}
}
}
}
I am currently building a quiz app that automatically prints out a random arithmetic quiz with a countdown timer. I must say I am scared of being blocked for asking this question but I decided to take the risk.
This is the block of code that prints out a question from the array of questions:
function generateQuestion() {
question.textContent = quizzes[Math.trunc(Math.random() * quizzes.length)];
const questionNumber = question.textContent;
return questionNumber;
}
This is the code block that evaluates the arithmetic problem in the array, solves and returns feedback:
const correctAnswer = eval(generateQuestion()); // A stand-alone code not inside any function. Reason explained below
function checkAnswer() {
console.log(correctAnswer);
if (correctAnswer === Number(answer.value)) {
feedback.textContent = "Correct!";
score++;
document.querySelector(".score").textContent = score;
answer.value = "";
} else {
feedback.textContent = "Wrong!";
}
}
At first, it scores you correctly when you input the correct answer but subsequent questions are marked wrong.
I tried debugging by printing values to the console then I found out this block of code: const correctAnswer = eval(generateQuestion()); was still holding the previous value. I could not place it into the function checkAnswer() as this continued to return undefined.
Is there a way to go around this or am I missing something?
I'm making a pretty simple react quiz app in which you go from question to question via a 'next' button. Each time the next button is clicked a useState hook (passed as props to onAnswerUpdate) pushes an object with the question's title and the chosen answer to an array named 'answers':
const nextClickHandler = (e) => {
if(selected === '') {
return setError('Please select one option');
}
onAnswerUpdate(prevState => [...prevState, { q: data.question, a: selected }]);
setSelected('');
if(activeQuestion < numberOfQuestions - 1) {
onSetActiveQuestion(activeQuestion + 1);
}else ...
}
I implemented a basic previous button logic whose only function is to return to the previous question but I struggled to find a way to replace the object in the answers array corresponding to the correct question when I change the answer to an already answered question. I've tried replacing it using the activeQuestion number as an index to the array (like in the code sample below) and splice method but neither of them worked. Any help will be greatly appreciated.
const nextClickHandler = (e) => {
if(selected === '') {
return setError('Please select one option');
}
onAnswerUpdate(prevState => [...prevState, answers[activeQuestion] = { q: data.question, a: selected }]);
setSelected('');
if(activeQuestion < numberOfQuestions - 1) {
onSetActiveQuestion(activeQuestion + 1);
} else ...
Hook with prevState and an array always drive me crazy... I prefer to use an easier approach. If you need to replace an element inside an array that is on state, you could write:
...
let currAnswer = answer;
currAnswer[activeQuestion] = { q: data.question, a: selected };
onAnswerUpdate(currAnswer);
...
This question already has answers here:
javascript interval
(3 answers)
Closed 4 years ago.
$(function() {
let testOne = 'test one.';
let testTwo = 'test two';
let messageBox = $('messagebox');
let a = ['test:', testOne,'test2:', testTwo];
let i = 1
setInterval(cool, 1000)
function cool() {
messageBox.text(a[1])
}
});
Hi there,
I am new to JS. I am looking to have testOne and testTwo (going to add a few more) display in timers across my screen. I was given help to get this far.
I am trying to, for example, have a word and its English definition appear on the screen in a time loop, rotating the words in a loop. (kind of like a live screen-saver)
What am I missing?
Thank you for your time, help, and effort.
You've got a good start.
As others have mentioned, unless you're using a custom HTML element (i.e. <messagebox>), use # at the beginning of your selector to indicate that "messagebox" is an ID. See jQuery's ID Selector.
$('#messagebox')
Alternatively, use a class and the class selector.
$('.messagebox')
The index of the array element to display is currently hard-coded to 1. We want to increment it upon each iteration so the text will change. But we only want to count up to the number of array elements and then go back to the first one and start over.
Below, I'm using JavaScript's increment and remainder operators to increment i while limiting it to the number of elements in a. Note that the "postfix" method "returns the value before incrementing", so i starts at zero.
a[i++ % a.length]
Working example:
$(function() {
let $messageBox = $('#messagebox');
let testOne = 'test one.';
let testTwo = 'test two.';
let a = ['test:', testOne, 'test2:', testTwo];
let i = 0;
function cool() {
$messageBox.text(a[i++ % a.length])
}
setInterval(cool, 1000);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="messagebox"></div>
EDIT
I don't like letting i count up indefinitely. The math might get wonky after about 9 quadrillion loop iterations, which is how high JavaScript can safely count.
Safe in this context refers to the ability to represent integers exactly and to correctly compare them. For example, Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2 will evaluate to true, which is mathematically incorrect. -- developer.mozilla.org
console.log(Number.MAX_SAFE_INTEGER);
console.log(Number.MAX_SAFE_INTEGER + 1);
console.log(Number.MAX_SAFE_INTEGER + 2);
console.log(Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2);
So, here's what happens after about three million centuries:
$(function() {
let $messageBox = $('#messagebox');
let testOne = 'test one.';
let testTwo = 'test two.';
let a = ['test:', testOne, 'test2:', testTwo];
let i = 9007199254740990;
function cool() {
console.log(i);
$messageBox.text(a[i++ % a.length])
}
setInterval(cool, 1000);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="messagebox"></div>
That's not good enough.
We need this thing running well past the end of time.
Let's keep it safe:
$(function() {
let $messageBox = $('#messagebox');
let testOne = 'test one.';
let testTwo = 'test two.';
let a = ['test:', testOne, 'test2:', testTwo];
let i = 0;
function cycleText() {
console.log(i);
$messageBox.text(a[i]);
i = ++i % a.length;
setTimeout(cycleText, 1000);
}
cycleText();
});
body {
font-size: 2em;
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="messagebox"></div>
You can easily swap out the messages in your array and update an html element using your code. Instead of passing a hardcoded index in, just increment a number until it reaches the length of the array (n < array.length) and reset it to 0.
I personally would recommend making your messagebox element a div or something out of the box just for readability sake (so nobody comes in and gets confused where messagebox is coming from). However, if you have a specific use case for a custom html element, make sure you're doing it correctly.
https://jsfiddle.net/mswilson4040/oxbn8t14/2/
<messagebox>Initial Value...</messagebox> // custom HTML element called messagebox
$(function() {
let testOne = 'test one.';
let testTwo = 'test two';
let interval = -1;
let messageBox = $('messagebox');
let a = ['test:', testOne,'test2:', testTwo];
// let i = 1 <-- this isn't doing anything
setInterval(cool, 1000)
function cool() {
interval = interval < a.length ? interval += 1 : 0;
messageBox.text(a[interval])
}
});