I'm building a quiz game and I have a working checkAnswer() function and increaseCorrectScore() function.
Once the user has clicked a button (one out of three buttons is correct) it will either return correct or wrong then a 'Next' button appears and the user will click to load the nextQuestion() function (function not yet called in code below).
My problem: at the moment the user can click on the wrong answer, but can then still click on the correct answer and score a point. They can also keep clicking on the correct answer to cheat in the game. How can I:
stop the user from clicking on the correct answer if they have first clicked on a wrong answer?
stop the user from repeatedly clicking on the correct answer to increase their score before clicking 'Next' for the next question?
I would like to leave the Next button and the functionality that has the user clicking for the next question, rather than simply running nextQuestion() after the first click on an answer.
function checkAnswer() {
if (this.textContent === newFlags[currentFlagIndex].country) {
let correct = true
let correctAnswer = `CORRECT!`
document.getElementById('result').innerHTML = correctAnswer;
nextButton.classList.remove('hide');
setStatusClass(document.body, correct)
increaseCorrectScore();
} else {
let wrong = false
let wrongAnswer = `WRONG!`
document.getElementById('result').innerHTML = wrongAnswer;
nextButton.classList.remove('hide');
setStatusClass(document.body, wrong)
}
}
/**
* Gets the current score from the DOM and increments it by 1
*/
function increaseCorrectScore() {
let currentScore = parseInt(document.getElementById('correct').innerText);
document.getElementById('correct').innerText = ++currentScore;
}
The HTML:
<!-- GAME AREA -->
<div class="container">
<div id="timer"></div>
<div class="flag"><img src="" id="flag"></div>
<div id="answer-buttons" class="answer-box">
<button class="btn" id="answer-1">Country 1</button>
<button class="btn" id="answer-2">Country 2</button>
<button class="btn" id="answer-3">Country 3</button>
</div>
<div id="result" class="result"></div>
<div class="answer-box"><button class="next-btn hide" id="next-question">Next flag >></button></div>
<div class="score">Score: <span id="correct" class="score">0</span></div>
</div>
Thanks for comments - have decided to hide the buttons until user clicks Next.
Related
I'm currently utilizing .closest in vanilla js to find a div(modal container) with a certain class, then on click of the button closes that div(modal container). all works fine, but the issue i'm running into now is I need to find either one of two divs(two different types of modals). So depending which one of these "div (modal containers)" are closest to the button - close that modal.
<div class="modal-a">
<div class="modal__header">
<button class="btn" data-close-btn>close</button>
</div>
</div>
<div class="modal-b">
<button class="btn" data-close-btn>close</button>
</div>
//-------------- Javacript
const closeBtn = querySelectorAll(['data-close-btn]);
closeBtn.forEach(button => {
const modal = button.closest('.modal-a'); // works as expected
button.addEventListener('click', (e)=> closeModal(modal));
});
function closeModal(modal) {
modal.classList.remove('.active');
}
what im trying to achieve
const modal = button.closest('.modal-a') || button.closest('.modal-b');
const modal = button.closest('.modal-a, .modal-b');
both these obviously fails the first time but works thereafter although in the console there is always a failure on click, so how do i write this to only use the relevant selector?
Hope this makes sense what im trying to explain.
You can use a single eventlistener and event delegation for that:
document.addEventListener("click", e => {
target = e.target.closest(".modal-a, .modal-b")
if(!target || !e.target.matches("[data-close-btn]")) return;
alert("You clicked a modal-Button")
})
<div class="modal-a">
<button class="btn" data-close-btn>close</button>
</div>
<div class="modal-b">
<button class="btn" data-close-btn>close</button>
</div>
<h2>Button outside of any Modal</h2>
<button class="btn" data-close-btn>close</button>
I am making a on js "True or False" game. Assertion titles appear on the screen one by one. The user responds to each statement in turn. There are 5 statements in total. - If the user clicked on the False button then the following statement appears. And if it's true then a description appears. You can go to the next question by clicking on the white circle under the headings, which turns into red if an answer has already been given to it (any) or by clicking on further.
What is the best way to make the questions change without reloading the page and make the navigation circles are active in pure js? The available code and an approximate view of the functionality are below:
<h1>statement 1</h1>
<div id="text" style="display:none;">Text of statement</div>
<button onclick="chpok('text')"><h2 onclick="chpok('arrow')">True</h2></button>
<button onclick="chpok('arrow')"><h2>False</h2></button>
<h2 id="arrow" onclick="chpok('block1')" style="display:none;">Next</h2>
<div class="block1" id="block1" style="display: none;">
<h1>statement 2</h1>
<div id="text2" style="display:none;">Text of statement</div>
<button onclick="chpok('text2')"><h2 onclick="chpok('arrow2')">True</h2></button>
<button onclick="chpok('arrow2')"><h2>False</h2></button>
<h2 id="arrow2" onclick="chpok('block2')" style="display:none;">Next</h2>
</div>
<div class="block2" id="block2" style="display: none;">
<h1>statement 3</h1>
<div id="text3" style="display:none;">Text of statement</div>
<button onclick="chpok('text3')"><h2 onclick="chpok('arrow3')">True</h2></button>
<button onclick="chpok('arrow3')"><h2>False</h2></button>
<h2 id="arrow3" style="display:none;">Next</h2>
</div>
<script>function chpok(id) {
elem=document.getElementById(id);
state=elem.style.display;
if (state=='none') elem.style.display='';
}</script>
This is a very broad question, but I'm gonna try and answer it by breaking it down a bit.
There's actually three issues in this question.
How do i display information from JS in the DOM?
How can i act on a button press to update this information?
How can we track which questions was answered?
Let's start with issue #1: For a task like this it helps making the question structure in JavaScript or JSON first. Every question has 4 pieces of information: statement, description, correctAnswer, answer.
We also need to create a function that handles assigning the values into the DOM. In this example we will call it selectTask(id) we're gonna run this on launch.
For Issue #2 we can reuse the selectTask(id) function to display the next answer, but we should add another function called answer(value) which takes what you've answered as an argument, this function sets the answer field of your statement object and calls selectTask for the next task. To run JS function you can use "onclick".
Finally, for issue #3, we have to apply some styling to the overview buttons to indicate which are correct or false, and whether they're answered at all. Luckily we have our answer field for each task already so we can use this. I also added selectTask in the onclick of each of the overview buttons to enable navigation back and forth.
var currentTask = 0;
var tasks = [
{
statement:"Food and water is essential to surviving",
description: "Consuming edibles and liquids is a crucial part of human survival.",
correctAnswer: true,
answer:null
},
{
statement:"Asking questions on SA is stupid",
description: "Despite what some may think, asking question on stack overflow doesn't make you a bad programmer",
correctAnswer: false,
answer:null
},
{
statement:"Computer science is fun!",
description: "Enough said.",
correctAnswer: true,
answer:null
}
]
function selectTask(id){
if(id < 0 || id >= tasks.length) return; //Invalid ID entered.
currentTask = id;
document.getElementById("statement").innerText = tasks[currentTask].statement
document.getElementById("description").innerText = tasks[currentTask].description
}
function answer(value){
tasks[currentTask].answer = value;
console.log(value, currentTask)
for(var task in tasks){
if(tasks[task].answer === null){ /* do nothing*/}
else if(tasks[task].answer === tasks[task].correctAnswer)
document.getElementById("task"+task).style.backgroundColor = "green"
else if(tasks[task].answer !== tasks[task].correctAnswer)
document.getElementById("task"+task).style.backgroundColor = "red"
}
if(currentTask < tasks.length-1){
selectTask(currentTask+1)
}
}
selectTask(0);
<div>
<p id="statement"></p>
<p id="description"></p>
<button onclick="javascript:answer(true)">True</button>
<button onclick="javascript:answer(false)">False</button>
<div id="overview">
<button id="task0" onclick="javascript:selectTask(0)">1</button>
<button id="task1" onclick="javascript:selectTask(1)">2</button>
<button id="task2" onclick="javascript:selectTask(2)">3</button>
</div>
</div>
I have a rather large slideshow type site. All the slides are on one html page so I can't use the browser back function. Each slide has a back button. I can't specifically specify where that back button should go because the slide show is non-linear. So I could arrive at any particular slide from any other slide. Is there a way of making a history array or anything that I could use for this back button that would function as a back button?
I should say, I am a beginner. I'm still learning javascript.
Edit: I forgot to mention that I'm trying to do this purely in javascript/html/css.
I've got the back button working in javascript, but only one step backwards. I just made it so each button clicked updates a variable with the current slide's ID. Then the back button just "points" to that variable. The problem is, it won't go back more than one step because once you go back once the variable is never updated to the ID that you just went back from. Hope that makes sense.
You can use the History API:
When jumps from slide #1 to #5, call history.pushState:
var stateObject = {page: "5"};
history.pushState(stateObject, "Slide #5", "#5");
When navigates back, simply call history.back(), and add a popstate listener to show the desired slide:
window.addEventListener("popstate", function(event) {
$(".page").hide();
$("#p" + event.state.page).show();
}
The event.state here is the stateObject you pushed when you call history.pushState. For example, when user navigates back to Slide #5, the event.state is {page: "5"}.
function jump() {
var target = this.getAttribute("data-value");
document.querySelector(".on").classList.remove("on");
document.getElementById("s"+target).classList.add("on");
var stateObject = {page: target};
history.pushState(stateObject, "Slide #" + target, "#" + target);
}
document.querySelectorAll(".jump").forEach(function(element) {
element.addEventListener("click", jump);
});
document.querySelector(".back").addEventListener("click", function() {
history.back();
});
window.addEventListener("popstate", function(event) {
if (event.state === null) {
// the first page has no stateObject
document.querySelector(".on").classList.remove("on");
document.getElementById("s1").classList.add("on");
} else if (typeof event.state.page !== "undefined") {
document.querySelector(".on").classList.remove("on");
document.getElementById("s" + event.state.page).classList.add("on");
}
});
.page {display: none;}
.page.on {display: block;}
<button class="back">Back</button>
<div class="page on" id="s1">
Slide #1
<div class="action">
<button class="jump" data-value="3">Go to #3</button>
</div>
</div>
<div class="page" id="s2">
Slide #2
<div class="action">
<button class="jump" data-value="1">Go to #1</button>
</div>
</div>
<div class="page" id="s3">
Slide #3
<div class="action">
<button class="jump" data-value="2">Go to #2</button>
</div>
</div>
See:
https://developer.mozilla.org/en-US/docs/Web/API/History_API
https://developer.mozilla.org/en-US/docs/Web/Events/popstate
I have ten buttons with a button for next and previous,
when I click the next button, it should show the next two buttons (hiding the rest).
The reverse should happen when the previous button is clicked (Show the previous two buttons (hide the rest)).
thanks.
my html code is :
<div>
<button class="menu">M1</button>
<button class="menu">M2</button>
<button class="menu">M3</button>
<button class="menu">M4</button>
<button class="menu">M5</button>
<button class="menu">M6</button>
<button class="menu">M7</button>
<button class="menu">M8</button>
<button class="menu">M9</button>
<button class="menu">M10</button>
</div>
<div>
<button class="action" id="btnNext">Next</button>
<button class="action" id="btnPreview">Previous</button>
</div>
Initially make every button hidden except first two.
$("#btnNext").on('click', function(){
var vBtn = $(".menu:visible:last");
$(".menu").hide();
vBtn.next().show();
vBtn.next().next().show();
});
$("#btnPreview").on('click', function(){
var vBtn = $(".menu:visible:first");
$(".menu").hide();
vBtn.prev().show();
vBtn.prev().prev().show();
});
I manually coded not tested please check
$("button.menu").not(':eq(0),:eq(1)').hide();
var count = 1;
$("#btnNext").click(function() {
$("button.menu").hide();
count = count + 2;
$("button.menu").eq(count-1).show();
$("button.menu").eq(count).show();
});
$("#btnPreview").click(function() {
$("button.menu").hide();
count = count - 2;
$("button.menu").eq(count-1).show();
$("button.menu").eq(count).show();
});
I'm very new to JavaScript and jQuery. I know my code is not the prettiest, but I'm trying to start somewhere.
I have a series of questions that are shown one at a time, and want to create some sort of validation to prevent the user from going to the next question if a radio button on the first button hasn't been selected.
HTML (I have four of these .questionContainers
<div class="questionContainer">
<div class="question">
How much storage do you need?
</div>
<div class="answers">
<ul>
<li>
<label>
<input type="radio" id="storage">1GB or less
</label>
</li>
<li>
<label>
<input type="radio" id="storage">More than 1GB
</label>
</li>
</ul>
</div>
<div class="btnContainer">
<div class="next">
<a class="btnNext">Next</a>
</div>
</div>
</div>
JavaScript
(function(){
var Questions = {
container : $('.questionContainer'),
init : function() {
this.start();
if($('input[type=radio]:checked').length > 0){
this.container.find('a.btnNext').on('click', this.next);
}
},
start : function() {
this.container.not(':first').addClass('hide');
},
next : function() {
var container = $('.questionContainer');
$(this).closest(container).hide(function(){
$(this).closest(container).next().show();
});
}
};
Questions.init();
})();
The specific line that isn't working:
if($('input[type=radio]:checked').length > 0) {
this.container.find('a.btnNext').on('click', this.next);
}
The Problem
When I add the if statement and click a radio button followed by next, it does not go to the next question. I am not receiving any errors in the console.
This binds to the click event only if something is checked at the time that the start function is called (not what you want - it will never be true unless you pre-select a radio button for the user without their action):
if($('input[type=radio]:checked').length > 0){
this.container.find('a.btnNext').on('click', this.next);
}
Try replacing it with this instead:
this.container.find('a.btnNext').on('click', function () {
if($('input[type=radio]:checked').length > 0){
this.next();
}
});
This way you always bind to the click event, but the function that is bound only allows next to be called if something is checked at the time that the function is called (i.e. when the next button is clicked).