'How to open a modal with a form inside when any button within my table is clicked in one function
I have a modal within a div I want to pop up when any button inside my table is clicked. it will be fetching data from an API as the question and the form is for the user to answer and it will display either correct or incorrect and each button will show it's been 'used' after being clicked. The table has 5 columns and 6 rows including the title row. I am trying to create an onclick function for this.
<tr>
<td><button id="21" data-category="255">500</button></td>
<td><button id="22" data-category="190">500</button></td>
<td><button id="23" data-category="135">500</button></td>
<td><button id="24" data-category="145">500</button></td>
<td><button id="25" data-category="130">500</button></td>
</tr>
</table>
</div>
<div class="board">
<!-- categories get injected here -->
</div>
<!-- answers container -->
<div class="card-modal">
<button type="button" class="open-modal" data-open="modal1">...</button>
<div class=card-modal-inner>
<button type="button" class="close" data-dismiss="modal">×</button>
<h2 class="clue-text"><!-- clue gets added here --></h2>
<form autocomplete="off">
<input name="user-answer" type="text" />
<button type="submit">Answer</button>
</form>
<div class="result">
<p class="result_success">CORRECT</p>
<p class="result_fail">INCORRECT</p>
<p class="result_correct-answer">
The correct answer is <span class="result_correct-answer-text"><!--answer gets injected here--></span>
</p>
</div>
</div>
</div>
</div>
Solution 1:[1]
So here is the edit after the comment you've made to clear what you wanted to do.
I have made the code, with a really deep commenting, you should understand each part of it, and why they are written on. I've tried to keep it simple, and not too technical, keeping the logic simple, not using fancy methods or properties, just the essential that you should know as a beginner.
I have added a bunch of console.log()
for you to be able to understand what is happening, and when, if you were to try the code out. Even though the comments are extremely clear, and the logic self-explainatory.
Do not hesitate to comment for me to explain something, but I am sure that you'll get everything. I have added a little trick that will prevent users from cheating by sending another answer.
const tr = document.querySelector("#tr")
const trChildrens = Array.from(tr.children)
const modal = document.querySelector("#modal")
const answerInput = document.querySelector("#answerInput")
const answerButton = document.querySelector("#answerButton")
const result = document.querySelector("#result")
const isCorrect = document.querySelector("#isCorrect")
let modalToggle = false // We're setting it in the global scope in case you need to access it further on.
let answer = "" // Same reason, else we'd put it on the function scope, which would works in your case. Answer is empty per default.
// We will use forEach, which will apply the code for each entry of our array, see it as a for loop, and read about it if you don't know about it.
trChildrens.forEach(child => {
child.addEventListener("click", () => {
console.log("Hey, I've been clicked, so I'll open up the modal!")
modalToggle = !modalToggle // We're putting modalToggle to what it's not currently: false if it's true, true if it's false.
if(modalToggle) { // In the case of modalToggl to be set to true, we want to display the modal.
modal.style.display = "block" // Replacing the display:none of the modal to the display:block to show it.
}
})
})
answerButton.addEventListener("click", answerCheck) // Simply execute answerCheck() when clicking the Answer button.
function answerCheck() {
// When the answer button is clicked, we want to put the current value of the input as the answer.
answer = answerInput.value // .value allows us to get the value of an input;
answerButton.disabled = true // Once the uesr press the "Answer" button, he shall not be allowed to do it again.
answerButton.removeEventListener("click", answerCheck) // prevent people to use a trick where they would remove the disabled in DevTools, and resend answer to get it correct while they were not.
// This will need to be coded with your API logic, I will code it with the logic in mind, you'll have to adapt.
// At this point, you'll have to call your API to get the correct answer, store it in a variable and just use it in the if statement.
if (answer != "This will be the result of the API") {
console.log("The answer is different from the API result, the answer is not correct!")
isCorrect.textContent = "incorrect"
} else {
console.log("The answer is corresponding to the API result, the answer is correct!")
isCorrect.textContent = "correct"
}
result.textContent = "Your API result."
}
Here is the HTML part of the code with the id
being set.
<table>
<tr id="tr">
<td><button id="21" data-category="255">500</button></td>
<td><button id="22" data-category="190">500</button></td>
<td><button id="23" data-category="135">500</button></td>
<td><button id="24" data-category="145">500</button></td>
<td><button id="25" data-category="130">500</button></td>
</tr>
</table>
</div>
<div class="board">
<!-- categories get injected here -->
</div>
<!-- answers container -->
<div id="modal" class="card-modal" style="display: none;">
<button type="button" class="open-modal" data-open="modal1">...</button>
<div class=card-modal-inner>
<button type="button" class="close" data-dismiss="modal">×</button>
<h2 class="clue-text">
<!-- clue gets added here -->
</h2>
<form autocomplete="off">
<input id="answerInput" name="user-answer" type="text" />
<button id="answerButton" type="button">Answer</button>
</form>
<div class="result">
<p class="result_correct-answer">
The correct answer is <span id="result" class="result_correct-answer-text">
<!--answer gets injected here-->
</span>. Your answer was <span id="isCorrect"></span>
</p>
</div>
</div>
</div>
All you have to do is the API part on the end of the answerCheck()
function. Store the result in a variable, and just replace my "string"
to your variable to makes it make sense.
Solution 2:[2]
So far everything looks fine, my biggest issue was getting the modal to open and once the question is answered in the form it will update the score. I'm a bit lost as to what I'm actually missing in the javascript for the modal to open up and show the question from the data being fetched with the form for the answer submission.
class GuessingTrivia {
constructor(element, options = {}) {
this.categoryIds = [255, 190, 135, 145, 130];
// Default Categories pulled from https://jservice.io/search:
// 3 letter words 255, movies 130,food and drink 190, mythology 135, nonfinction 145
/*fetch this data from API */
this.categories = { 255: [], 190: [], 135: [], 145: [], 130: [] };
// Form elements
this.formElement = document.querySelector(".form");
this.gameBoardElement = document.querySelector(".table");
this.scoreCountElement = document.querySelector(".score-count");
const tr = document.querySelector("#tr");
const trChildrens = Array.from(tr.children);
const modal = document.querySelector("#modal");
const answerInput = document.querySelector("#answerInput");
const answerButton = document.querySelector("#answerButton");
const result = document.querySelector("#result");
const isCorrect = document.querySelector("#isCorrect");
function getQuestionsByCategory(id) {
const data = this.categories[id];
return data.map((singleObject) => singleObject.question);
initGame() {
this.categoryIds.forEach((category) => this.fetchDataByCategory(category));
// this.fetchCategories();
}
}
}
}
const game = new GuessingTrivia(document.querySelector(".app"), {});
game.initGame();
const table = document.querySelector("table");
const tableBtns = table.querySelectorAll("button");
console.log(tableBtns);
tableBtns.forEach((btn) => {
btn.addEventListener("click", (e) => {
const category = e.target.dataset.category;
const question = game.getQuestionsByCategory(category)[Number(id) + 1];
console.log(question);
});
});
// ID for this clue
const clueId = categoryIndex + "-" + index;
category.clues.push(clueId);
let modalToggle = false;
let answer = ""; // Answer is empty as default.
// applying the code for each entry of the array
trChildrens.forEach((child) => {
child.addEventListener("click", () => {
console.log("Button has been clicked,open up the modal");
modalToggle = !modalToggle;
if (modalToggle) {
// display the modal if its true
modal.style.display = "block";
}
});
});
answerButton.addEventListener("click", answerCheck); // check answer when clicking the Answer button
function answerCheck() {
// adding the current value of the input as the answer when user types it in
answer = answerInput.value;
answerButton.disabled = true;
answerButton.removeEventListener("click", answerCheck); //so user cannot cheat
// Calling API to get the correct answer
async function fetchDataByCategory(id) {
const response = await fetch(`https://jservice.io/api/category?id=${id}`);
const data = await response.json();
const answerCheck = data.answerCheck;
this.categories[id] = [clues[0], clues[1], clues[2], clues[3], clues[4]];
console.log(this.categories);
}
if (answer != "The result of the API") {
console.log("answer is incorrect");
isCorrect.textContent = "incorrect";
} else {
console.log("answer is correct!");
isCorrect.textContent = "correct";
}
result.textContent = "API result.";
}
// Score up update from 0
updateScore(change);
this.score = 0;
this.updateScore(0);
this.score =+ change;
this.scoreCountElement.textContent = this.score;
this.updateScore(this.currentClue.value)
this.clues[clueId] = {
question: clue.question,
answer: clue.answer,
value: (index + 1) * 100
};
* {
box-sizing:border-box;
margin: 0;
padding: 0;
}
html {
overflow-x:auto;
overflow-y: auto;
height: 100%;
margin: auto;
}
.top-heading {
display: flex;
justify-content: space-between;
align-items: center;
}
body {
background-color:#2a3698;
height:100%;
font-family:Verdana, Arial, Helvetica, sans-serif;
font-size: 2vw;
text-align:center;
padding: 1em;
/*overflow-y: hidden*/
}
table{
border:3px solid;
width: 100%;
text-align: center;
justify-content: space-around;
border-radius: 5px;
height: 40rem;
position: relative;
margin: auto;
margin-bottom: 1em;
padding: 5px;
transition: .1s;
z-index: 1;
}
button {
background-color: #2a3698;
border-style: bold;
display:inline-block;
height:100px;
width: 185px;
padding: 14px;
font-size: 25px;
font-weight: bold;
box-shadow: 5px 10px;
}
th{
justify-content: space-evenly;
}
th, td {
padding-top: 10px;
padding-bottom: 20px;
padding-left: 30px;
padding-right: 40px;
}
.score {
color: green;
font-size: 1em;
margin-left: 0.2em;
display: flex;
align-items: center;
font-weight: bold;
font-family :Verdana, Arial, Helvetica, sans-serif;
}
.score-count{
color:white;
}
/*table{
visibility:hidden;
}
.card-modal {
opacity: 0;
pointer-events: none;
transition: opacity 0.4s;
position: fixed;
align-items: center;
justify-content: center;
}
.card-modal.visible {
opacity: 1;
pointer-events: initial;
}
.card-modal.showing-result .result {
display: block;
}
.modal-container form {
display: flex;
}
.modal-container form button[type="submit"] {
padding-left: 2em;
padding-right: 2em;
cursor: pointer;
font-family: inherit;
background: var(--yellow);
border: 0;
font-size: inherit;
border-top-right-radius: 0.4em;
border-bottom-right-radius: 0.4em;
}
.modal-container .clue-text {
margin-bottom: 2em;
}*/
.modal-container {
border:0;
border-radius: 5px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
padding: 10px 25px;
position:fixed;
top:0;
left:0;
height: 100vh;
width: 200vh;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 0.3);
opacity: 0;
pointer-events: none;
transition: opacity 0.4s;
}
.modal {
background-color: white;
width: 800px;
height: 600px;
max-width: 100%;
padding: 50px 50px;
border-radius: 5px;
text-align: center;
}
.modal h2 {
margin: 0;
}
.modal p {
font-size: 24px;
opacity: 0.7;
}
.modal-container form {
display: flex;
}
.modal-container .clue-text {
margin-bottom: 2em;
}
.modal-container form button[type="submit"] {
padding-left: 2em;
padding-right: 2em;
cursor: pointer;
font-family: inherit;
background: green;
border: 0;
font-size: inherit;
border-top-right-radius: 0.4em;
border-bottom-right-radius: 0.4em;
}
.result,
.modal-container.showing-result form {
display:none;
}
.modal-container .visible {
opacity: 1;
pointer-events: auto;
}
.modal-container .showing-result .result {
display: block;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Jeopardy Game</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div class="app">
<!-- top header area for categories-->
<header class="top-heading">
<h1>Jeopardy Trivia</h1>
<p class="score">Score <span class="score-count"> </span></p>
</header>
<div>
<table>
<th>
<tr>
<th>3 Letter Words</th>
<th>Food & Drink</th>
<th>Mythology</th>
<th>Nonfiction</th>
<th>Movies</th>
</tr>
</th>
<tr id="tr">
<td><button id="1" data-category="255">100</button></td>
<td><button id="2" data-category="190">100</button></td>
<td><button id="3" data-category="135">100</button></td>
<td><button id="4" data-category="145">100</button></td>
<td><button id="5" data-category="130">100</button></td>
</tr>
<tr id="tr">
<td><button id="6" data-category="255">200</button></td>
<td><button id="7" data-category="190">200</button></td>
<td><button id="8" data-category="135">200</button></td>
<td><button id="9" data-category="145">200</button></td>
<td><button id="10" data-category="130">200</button></td>
</tr>
<tr id="tr">
<td><button id="11" data-category="255">300</button></td>
<td><button id="12" data-category="190">300</button></td>
<td><button id="13" data-category="135">300</button></td>
<td><button id="14" data-category="145">300</button></td>
<td><button id="15" data-category="130">300</button></td>
</tr>
<tr id="tr">
<td><button id="16" data-category="255">400</button></td>
<td><button id="17" data-category="190">400</button></td>
<td><button id="18" data-category="135">400</button></td>
<td><button id="19" data-category="145">400</button></td>
<td><button id="20" data-category="130">400</button></td>
</tr>
<tr id="tr">
<td><button id="21" data-category="255">500</button></td>
<td><button id="22" data-category="190">500</button></td>
<td><button id="23" data-category="135">500</button></td>
<td><button id="24" data-category="145">500</button></td>
<td><button id="25" data-category="130">500</button></td>
</tr>
</table>
</div>
<div class="board">
<!-- categories get injected here -->
</div>
<!-- answers container -->
<div id="modal" class="card-modal" style="display: none;">
<button type="button" class="open-modal" data-open="modal1">...</button>
<div class=card-modal-inner>
<button type="button" class="close" data-dismiss="modal">×</button>
<h2 class="clue-text">
<!-- clue gets added here -->
</h2>
<form autocomplete="off">
<input id="answerInput" name="user-answer" type="text" />
<button id="answerButton" type="button">Answer</button>
</form>
<div class="result">
<p class="result_correct-answer">
The correct answer is <span id="result" class="result_correct-answer-text">
<!--answer gets injected here-->
</span>. Your answer was <span id="isCorrect"></span>
</p>
</div>
</div>
<script src="index.js"></script>
</body>
</html>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | airelav |