Add function to game on "True or False" on pure js - javascript

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>

Related

How to set a specific keyword/password sent through textarea that trigger JavaScript if statement

That's my first ever question, and it's about my very first project, it's an online RPG Forum/PlayByChat game. I know it's kinda of an ambitious project.
So, i'm stuck from months into a problem that i feel i'm close to solve, premise (I'm self-taught).
The idea is to activate an if statement by a keyword/password given by an NPC, if there isn't no keyword in the message that it's supposed to be sent, then nothing should to happen.
That's what i've been able to put together and modify a little bit (it's not the entire code, just the interested parts), it works but it gets activated by any words or even by just one letter/empty messagges, i recently added the attribute "required" to the textarea so the field needs to be fill before to send messagges (even though it works only for the first click, then gives the possibility to send empty messagges which of course triggers the JoinedInTheRoom function) but anyway, that's a minor problem, the main problem remains that i cannot figured out how to make work the if statement inside the event listener under the JoinedInTheRoom function with a pre-chosen keyword/password, let's say "Mellon" (not case-sensitive if possible).
i'll explain myself better.
Let's say i'm gonna write "Mellon fellas" in the TextArea inside the Chat Log, my idea is to simply trigger the JoinedInTheRoom function by the fact that "Mellon", the keyword, has been just sent inside the messagge, i hope that my intent it's clear.
Thanks in advance.
here is the code:
HTML - CHAT LOG + TEXT AREA AND BUTTON
<!-- CHAT LOG -->
<h3>Chat Log</h3>
<div class="ChatLog">
<div class="msg left-msg">
<div class="msg-img" style="background-image: url()"></div>
<div class="msg-bubble">
<div class="msg-info">
<div class="msg-info-name">System</div>
</div>
<div class="msg-text">Write the Keyword to join the room and unlock the NPCs and Quests</div>
</div>
</div>
</div>
<!-- TEXT AREA AND BUTTON -->
<form class="FormInputArea">
<textarea class="InputCamp" id="TextArea" placeholder="Explain your move in max 200 characters..." required></textarea>
<button class="button" type="submit" id="SendButton"> Send </button>
</form>
JAVA SCRIPT - CHAT LOG + TEXT AREA AND BUTTON
// CHAT LOG + TEXT AREA AND BUTTON
const ChatLog = get(".ChatLog");
const FormInputArea = get(".FormInputArea");
const InputCamp = get(".InputCamp");
JAVA SCRIPT - JoinedInTheRoom + NpcsUnlocked
// JoinedInTheRoom + NpcsUnlocked
function JoinedInTheRoom(side) {
const NpcsUnlocked = `
<div class="msg ${side}-msg">
<div class="msg-img" style="background-image: url(${BOT_IMG})"></div>
<div class="msg-bubble">
<div class="msg-info">
<div class="msg-info-name">${BOT_NAME}</div>
<div class="msg-info-time">${formatDate(new Date())}</div>
</div>
<div class="msg-text">You've joined the room, feel free to choose your quest.</div>
</div>
</div>
<div class="msg ${side}-msg">
<div class="msg-img" style="background-image: url(${BOT_IMG})"></div>
<div class="msg-bubble">
<div class="msg-info">
<div class="msg-info-name">${BOT_NAME}</div>
<div class="msg-info-time">${formatDate(new Date())}</div>
</div>
<div class="msg-text">
<p onclick="Npc1('RANDOM TASK DESCRIPTION 1')"> Npc 1. </p>
<p onclick="Npc2('RANDOM TASK DESCRIPTION 2')"> Npc 2. </p>
</div>
</div>
</div>
`;
ChatLog.insertAdjacentHTML("beforeend", NpcsUnlocked);
ChatLog.scrollTop += 500;
}
FormInputArea.addEventListener("submit", event => {
event.preventDefault();
const NpcsUnlocked = InputCamp.value;
if (!NpcsUnlocked) return;
const Delay = NpcsUnlocked.split(" ").length * 600;
setTimeout(() => {
JoinedInTheRoom("left", NpcsUnlocked);
InputCamp.value = " ";
}, Delay);
})
JAVA SCRIPT - PLAYER MESSAGE SCRIPT
// PLAYER MESSAGE SCRIPT
function AppendMessage(name, img, side, text) {
const msgHTML = `
<div class="msg ${side}-msg">
<div class="msg-img" style="background-image: url(${img})"></div>
<div class="msg-bubble">
<div class="msg-info">
<div class="msg-info-name">${name}</div>
<div class="msg-info-time">${formatDate(new Date())}</div>
</div>
<div class="msg-text">${text}</div>
</div>
</div>
`;
ChatLog.insertAdjacentHTML("beforeend", msgHTML);
ChatLog.scrollTop += 500;
}
FormInputArea.addEventListener("submit", event => {
event.preventDefault();
const msgText = InputCamp.value;
if (!msgText) return;
AppendMessage(PERSON_NAME, PERSON_IMG, "right", msgText);
InputCamp.value = "";
});
Searching on internet i found this 11 years old stackoverflow post that i think it might help me, it seems i might use "indexOf" for this job, i'm right ? Maybe you guys can help me make it a little bit more "modern" and apply it to my code ?
Link
You could use the .includes function, like below
if (msgText.includes("mellon")) {
JoinedInTheRoom();
}
It'll search the whole string and return true if "mellon" is found. However it will also return true if someone types a word containing mellon. "smellon" or "smellonies" would return true and run the JoinedInTheRoom function too

Get first div after button is clicked

I have the following bit about shop info:
<h3 class="-center"><shop>Shop name</shop></h3>
<button type="button" id="banff" onclick="showShop(this.id)"><shop-title><b>Bakery Shop</b></shop-title></button>
<p class="move-right"><shop-info>Shop Address · Shop number</shop-info></p>
<div id="shop" class="hidden">
<p><shop-info>Opening soon</shop-info></p>
</div>
What I'm trying to do is that once the button is clicked, it will get the first div after the button and toggle the hidden class without referencing the id, there will be multiple shops so I am trying to create one function which will work for all of them rather than create an individual function for each.
I thought I could do something like:
function showShop(id) {
const elem = document.getElementById(id);
alert(id);
const div = elem.closest('div');
div.classList.toggle('hidden');
}
But it's referencing the first div on the page rather than the first one after the button, where have I gone wrong here? Or do I need to go about this a different way?
Thanks in advance for any advice
What about connecting the button id with the div id?
something like:
<h3 class="-center"><shop>Shop name</shop></h3>
<button type="button" id="button-1" onclick="showShop(this.id)">
<shop-title><b>Bakery Shop</b></shop-title>
</button>
<p class="move-right"><shop-info>Shop Address · Shop number</shop-info></p>
<div id="shop-button-1" class="hidden">
<p><shop-info>Opening soon</shop-info></p>
</div>
and in the javascript code would be possible to simplify to:
function showShop(id) {
const elem = document.getElementById(id);
const div = document.getElementById('shop-' + id);
div.classList.toggle('hidden');
}
You should try and avoid using ids as they are difficult to keep track of. Instead you should navigate relative to the clicked button. Below is a script that can easily take care of multiple shop sections without using any ids:
document.querySelectorAll("button").forEach(btn=>{
for(var div=btn;div=div.nextElementSibling;)
if(div.tagName==="DIV") break;
if(div) btn.onclick=()=>div.classList.toggle("hidden")
})
.hidden {display:none}
<h3 class="-center"><shop>Shop name</shop></h3>
<button><shop-title><b>Bakery Shop</b></shop-title></button>
<p class="move-right"><shop-info>Shop Address · Shop number</shop-info></p>
<div class="hidden">Info on bakery shop:
<p><shop-info>Opening soon</shop-info></p>
</div>
<button><shop-title><b>Iron Monger</b></shop-title></button>
<p class="move-right"><shop-info>Shop Address · Shop number</shop-info></p>
<div class="hidden">Info on iron monger's:
<p><shop-info>already open!</shop-info></p>
</div>
<button><shop-title><b>Fish Monger</b></shop-title></button>
<p class="move-right"><shop-info>Shop Address · Shop number</shop-info></p>
<div class="hidden">Info on fish monger's:
<p><shop-info>will never open!</shop-info></p>
</div>
In my latest update I tweaked the snippet again so that now I do the "relative navigation to the associated <div>" only once: at the time when I define and add the onclick eventlistener.

JavaScript Limit Score Increase on click

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.

How to run a function after one is done (or after an item in an array loads)?

I'm trying to animate a chat. I have a function that loads the blue messages one at a time each time you press the input field/button. Now, I want the grey messages to appear one at a time automatically after the LAST blue message appears. The JS code I have now has them appearing automatically after the first blue message instead (with a one second delay).
HTML
<div class="messages">
<div class="blue invisible" id="msg1">Hi! I'm looking for an old friend. She attended Martin Grove a few years ago.</div>
<div class="blue invisible" id="msg2">Her name is Sam. *insert pic of Sam and MC*</div>
<div class="blue invisible" id="msg3">Did you know her or her last name by any chance? </div>
<div class="grey" id="msg4" style="display: none;">Hello there!</div>
<div class="grey" id="msg5" style="display: none;">Unfortunately, I did not have the pleasure of teaching Sam. Her last name and whereabouts are a mystery to me as well. </div>
</div>
<div class="input" id="chat-button" onClick="showMessage()">
</div>
CSS
.invisible {
display: none;
}
JS
const myHTMLCollection = document.getElementsByClassName("invisible");
const HTMLElementsArr = [...myHTMLCollection];
function blueMessage() {
if (HTMLElementsArr.length > 0) {
HTMLElementsArr.shift().classList.remove('invisible');
greyMessage();
}
}
function greyMessage() {
setTimeout("show_second()", 1000);
}
function show_second() {
document.getElementById("msg4").style.display = "block";
setTimeout("show_third()", 1000);
}
function show_third() {
document.getElementById("msg5").style.display = "block";
}
Call greyMessage() only when the array is empty, so replace:
greyMessage()
with:
if (!HTMLElementsArr.length) greyMessage();
As a side note, it is bad practice to pass a string as callback argument to setTimeout. Pass the function object (without parentheses). So, for example, replace:
setTimeout("show_second()", 1000);
with:
setTimeout(show_second, 1000);

JavaScript dom.style.display not working at all

So I have a button in my HTML and when I click that button, I want to remove some elements from the page.
currently I have a function something like this which sets the listener for that button.
function addListener_close(elem,i){
console.log("IN BUTTON");
elem.addEventListener("click", function(){
console.log("clicked");
//this returns 'block' which is the current display
console.log(document.getElementById("newsentry-author").style.display);
//set the display to none
document.getElementById("newsentry-author").style.display = "none";
//this returns 'none' which is the new value
console.log(document.getElementById("newsentry-author").style.display);
});}
However, in my HTML page, none of the elements' style changed after I click the button.
Where did I do wrong?
UPDATE HTML CODE
<div class="newsentry-clicked">
<img class= "newsimage" src="https://dynaimage.cdn.cnn.com/cnn/digital-images/org/044409cb-e6f2-4c5b-8eaf-26078a4edef2.jpg">
<div class="newsentry-text">
<div class="newsentry-title">Commuting in the time of coronavirus in the nation's largest subway system</div>
<div class="newsentry-author"><span style="font-weight: bold">Author: </span>Lebron James</div>
<div class="newsentry-source"><span style="font-weight: bold">Source: </span>google.com</div>
<div class="newsentry-date"><span style="font-weight: bold">Date: </span>2020/03/02</div>
<div class="newsentry-desc-clicked">Crowded trains each weekday carry more than 5 million people hardened by terror threats and track-dwelling rats, daylight assaults and diluvial water main breaks. COVID-19 is their latest worry. The spread of the virus has unleashed anxiety across the nation'…</div>
See Original Post
</div>
<div class="close-button">×</div>
The function is bound to .close-button using javascript addEventListener

Categories