This is my first time using react and I have somewhat limited experience with javascript in general. Trying to adapt a project I found on codepen for my project. The idea being creating a dynamic graded quiz that returns some output if a user selects the wrong answer.
This is what I have as my html:
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="{% static 'second/css/app/quiz_control.css' %}">
<script src="https://unpkg.com/react#16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js" crossorigin></script>
<script src="{% static 'second/js/app/quiz_control.js' %}" defer></script>
</head>
<main class="container">
<section id="riots-to-renaissance">
</section>
</main>
</html>
javascript:
const RawHTML = (props) => <span dangerouslySetInnerHTML={{__html: props.html}}></span>;
class QuestionImage extends React.Component {
constructor(props) {
super(props);
this.imgRef = React.createRef();
}
componentDidUpdate(prevProps, prevState) {
if (this.imgRef.current && prevProps.img.src !== this.props.img.src) {
this.imgRef.current.classList.add('fade-in');
let timer = setTimeout(() => {
this.imgRef.current.classList.remove('fade-in');
clearTimeout(timer);
}, 1000)
}
}
render() {
return (
<img ref={this.imgRef} className="img-fluid" src={this.props.img.src} alt={this.props.img.alt} />
);
}
}
const QuizProgress = (props) => {
return (
<div className="progress">
<p className="counter">
<span>Question {props.currentQuestion+1} of {props.questionLength}</span>
</p>
<div className="progress-bar" style={{'width': ((props.currentQuestion+1) / props.questionLength) * 100 + '%'}}></div>
</div>
);
}
const Results = (props) => {
return (
<div className="results fade-in">
<h1>Your score: {((props.correct/props.questionLength) * 100).toFixed()}%</h1>
<button type="button" onClick={props.startOver}>Try again <i className="fas fa-redo"></i></button>
</div>
);
}
class Quiz extends React.Component {
constructor(props) {
super(props);
this.updateAnswer = this.updateAnswer.bind(this);
this.checkAnswer = this.checkAnswer.bind(this);
this.nextQuestion = this.nextQuestion.bind(this);
this.getResults = this.getResults.bind(this);
this.startOver = this.startOver.bind(this);
this.state = {
currentQuestion: 0,
correct: 0,
inProgress: true,
questions: [{
question: "<em>A Raisin in the Sun</em> was the first play by an African-American woman to be produced on Broadway. Who was the playwright?",
options: [{
option: "Lorraine Hansberry",
correct: true
}, {
option: "Maya Angelou",
correct: false
}],
img: {
src: 'https://interactive.wttw.com/sites/default/files/dusable-to-obama_raisin-in-the-sun.jpg',
alt: 'Characters in A Raisin in the Sun'
},
feedback: "Lorraine Hansberry's (1930–1965) play opened in 1959 to critical acclaim and was a huge success. The play is about a black family who faces racism when moving into an all-white suburb. The play is drawn from a similar experience in Hansberry’s early life.",
moreUrl: 'https://interactive.wttw.com/dusable-to-obama/hansberrys-victory'
}, {
question: "The internationally famous Harlem Globetrotters basketball team started with players from which Chicago High School?",
options: [{
option: "Wendell Phillips High School",
correct: true
}, {
option: "DuSable High School",
correct: false
}],
img: {
src: 'https://interactive.wttw.com/sites/default/files/dusable/riots_renaissance_thumb_5.jpg',
alt: 'A Harlem Globetrotter holding a basketball in each hand'
},
feedback: "The athletes who would become Harlem Globetrotters first played together as students at Wendell Phillips High School on the south side of Chicago. Later, they played as a team under the banner of the South Side's Giles Post of the American Legion and then as the Savoy Big Five before taking on their current name. The team was based in Chicago for 50 years, from 1926 through 1976.",
moreUrl: 'https://interactive.wttw.com/dusable-to-obama/harlem-globetrotters'
}, {
question: "What Chicagoan is known as the father of Gospel Music?",
options: [{
option: "Thomas A. Dorsey",
correct: true
}, {
option: "Langston Hughes",
correct: false
}],
img: {
src: 'https://interactive.wttw.com/sites/default/files/dusable-to-obama_thomas-dorsey.jpg',
alt: 'Thomas Andrew Dorsey'
},
feedback: "Some.",
moreUrl: 'https://interactive.wttw.com/dusable-to-obama/dorseys-gospel'
}, {
question: "Which of these African-American women ran for the office of president of the United States?",
options: [{
option: "All of the above",
correct: true
}, {
option: "None of the above",
correct: false
}],
img: {
src: 'https://interactive.wttw.com/sites/default/files/dusable/achieving_dream_thumb_9.jpg',
alt: 'Carol Moseley-Braun'
},
feedback: "more text.\"",
moreUrl: 'https://interactive.wttw.com/dusable-to-obama/carol-moseley-braun'
}, {
question: "Who was Oscar Stanton De Priest?",
options: [{
option: "The first Catholic priest in Chicago",
correct: false
}, {
option: "A United States congressman",
correct: true
}],
img: {
src: 'https://interactive.wttw.com/sites/default/files/dusable-to-obama_oscar-stanton-de-priest.jpg',
alt: 'Oscar Stanton De Priest'
},
feedback: "Text again.",
moreUrl: 'https://interactive.wttw.com/dusable-to-obama/carol-moseley-braun'
}, {
question: "What musical artist was part of Chicago's Black Renaissance?",
options: [{
option: "Louis Armstrong",
correct: false
}, {
option: "Nat \"King\" Cole",
correct: true
}, {
option: "Curtis Mayfield",
correct: false
}],
img: {
src: 'https://interactive.wttw.com/sites/default/files/dusable-to-obama_nat-king-cole.jpg',
alt: 'Nat King Cole'
},
feedback: "Long text.",
moreUrl: 'https://interactive.wttw.com/dusable-to-obama/dorseys-gospel'
}, {
question: "Gwendolyn Brooks was:",
options: [{
option: "the first black woman to win a Pulitzer Prize in poetry.",
correct: false
}, {
option: "all of the above",
correct: true
}],
img: {
src: 'https://interactive.wttw.com/sites/default/files/dusable-to-obama_gwendolyn-brooks.jpg',
alt: 'Gwendolyn Brooks'
},
feedback: "Gwendolyn Brooks (1917–2000) is a jewel in Chicago’s literary history. She was a writer best known for her poetry describing life in the South Side community in which she lived."
}]
}
}
updateAnswer(e) {
//record whether the question was answered correctly
let answerValue = e.target.value;
this.setState((prevState, props) => {
let stateToUpdate = prevState.questions;
//convert boolean string to boolean with JSON.parse()
stateToUpdate[prevState.currentQuestion].answerCorrect = JSON.parse(answerValue);
return {questions: stateToUpdate};
});
}
checkAnswer(e) {
//display to the user whether their answer is correct
this.setState((prevState, props) => {
let stateToUpdate = prevState.questions;
stateToUpdate[prevState.currentQuestion].checked = true;
return {questions: stateToUpdate};
});
}
nextQuestion(e) {
//advance to the next question
this.setState((prevState, props) => {
let stateToUpdate = prevState.currentQuestion;
return {currentQuestion: stateToUpdate+1};
}, () => {
this.radioRef.current.reset();
});
}
getResults() {
//loop through questions and calculate the number right
let correct = this.state.correct;
this.state.questions.forEach((item, index) => {
if (item.answerCorrect) {
++correct;
}
if (index === (this.state.questions.length-1)) {
this.setState({
correct: correct,
inProgress: false
});
}
});
}
startOver() {
//reset form and state back to its original value
this.setState((prevState, props) => {
let questionsToUpdate = prevState.questions;
questionsToUpdate.forEach(item => {
//clear answers from previous state
delete item.answerCorrect;
delete item.checked;
});
return {
inProgress: true,
correct: 0,
currentQuestion: 0,
questions: questionsToUpdate
}
});
}
componentDidMount() {
//since we're re-using the same form across questions,
//create a ref to it so we can clear its state after a question is answered
this.radioRef = React.createRef();
}
render() {
if (!this.state.inProgress) {
return (
<section className="quiz">
<Results correct={this.state.correct} questionLength={this.state.questions.length} startOver={this.startOver} />
</section>
);
}
return (
<section className="quiz fade-in" aria-live="polite">
<QuizProgress currentQuestion={this.state.currentQuestion} questionLength={this.state.questions.length} />
<div className="question-container">
{this.state.questions[this.state.currentQuestion].img.src &&
<QuestionImage img={this.state.questions[this.state.currentQuestion].img} />
}
<p className="question"><RawHTML html={this.state.questions[this.state.currentQuestion].question} /></p>
<form ref={this.radioRef}>
{this.state.questions[this.state.currentQuestion].options.map((item, index) => {
return <div key={index}
className={"option" + (this.state.questions[this.state.currentQuestion].checked && !item.correct ? ' dim' : '') + (this.state.questions[this.state.currentQuestion].checked && item.correct ? ' correct' : '')}>
<input id={"radio-"+index} onClick={this.updateAnswer} type="radio" name="option" value={item.correct}
disabled={this.state.questions[this.state.currentQuestion].checked} />
<label htmlFor={"radio-"+index}><RawHTML html={item.option}/></label>
</div>
})}
</form>
<div className="bottom">
{this.state.questions[this.state.currentQuestion].feedback && this.state.questions[this.state.currentQuestion].checked
&& <div className="fade-in">
<p>
<RawHTML html={this.state.questions[this.state.currentQuestion].feedback} />
{this.state.questions[this.state.currentQuestion].moreUrl &&
<React.Fragment>
<a target="_blank" href={this.state.questions[this.state.currentQuestion].moreUrl}>Learn more</a>.
</React.Fragment>
}
</p>
</div>
}
{!this.state.questions[this.state.currentQuestion].checked &&
<button type="button" onClick={this.checkAnswer}
disabled={!('answerCorrect' in this.state.questions[this.state.currentQuestion])}>Check answer</button>
}
{(this.state.currentQuestion+1) < this.state.questions.length && this.state.questions[this.state.currentQuestion].checked &&
<button className="fade-in next" type="button" onClick={this.nextQuestion}>Next <i className="fa fa-arrow-right"></i></button>
}
</div>
{(this.state.currentQuestion+1) === this.state.questions.length && this.state.questions[this.state.currentQuestion].checked &&
<button type="button" className="get-results pulse" onClick={this.getResults}>Get Results</button>
}
</div>
</section>
)
}
}
document.addEventListener('DOMContentLoaded', () => {
ReactDOM.render(<Quiz />, document.getElementById('riots-to-renaissance'));
})
and css
#import url('https://fonts.googleapis.com/css?family=Open+Sans');
#green: #36ad3b;
#red: #ff1100;
#yellow: #f3c000;
#blue: #1d77cc;
#sans: "Open Sans", "Helvetica", "Arial", sans-serif;
#keyframes roll-in {
0% {
top: 10px;
opacity: 0;
}
100% {
top: 0;
opacity: 1;
}
}
#keyframes fade {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
#keyframes pulse {
from {
transform: scale3d(1, 1, 1);
}
50% {
transform: scale3d(1.05, 1.05, 1.05);
}
to {
transform: scale3d(1, 1, 1);
}
}
.pulse {
animation: pulse 1s infinite;
}
.fade-in {
animation: fade .75s ease;
}
.quiz {
margin: 2em auto;
min-height: 40vh;
font-size: 16px;
.progress {
position: relative;
transition: width .4s ease;
margin-bottom: 1em;
background: rgb(181, 181, 181);
border-radius: 0;
width: 100%;
height: 2em;
font-family: #sans;
.progress-bar {
background-color: #1d77cc;
}
.counter {
position: absolute;
right: 5px;
top: 0;
font-weight: normal;
color: #fff;
height: 100%;
font-family: #sans;
font-size:1.25em;
margin: auto .5em;
letter-spacing:.025em;
display: flex;
flex-direction: column;
justify-content: center;
}
}
form {
width:90%;
margin:1.5em auto;
}
.img-fluid {
margin: 2em auto;
max-width: 360px;
display: block;
}
.question {
font-weight:bold;
line-height:1.35;
margin-bottom:.75em;
}
.option {
margin-bottom: .25em;
transition: all .25s ease;
font-size: .9em;
}
button {
padding: .75em;
font-family: #sans;
background-color: #1d77cc;
border: 0;
color: #fff;
font-size: 1em;
transition: .25s all;
white-space: nowrap;
font-weight: bold;
cursor: pointer;
i {
margin-left: .15em;
}
&:disabled {
opacity: .5;
}
}
//custom radio controls
input[type="radio"] {
position: absolute;
left: -9999px;
& + label {
position: relative;
font-weight: normal;
padding-left: 28px;
cursor: pointer;
line-height: 20px;
display: inline-block;
color: #666;
&::before {
text-align: center;
content: '';
position: absolute;
left: 0;
top: 0;
width: 20px;
height: 20px;
border: 1px solid #ddd;
border-radius: 100%;
background: #fff;
}
&::after {
content: '';
width: 12px;
height: 12px;
background-color: #222;
position: absolute;
top: 4px;
left: 4px;
border-radius: 100%;
transition: all 0.2s ease;
}
}
}
.dim, .correct {
input[type="radio"] + label::before {
border: 0;
font-size: 1.2em;
animation: .25s roll-in ease;
}
input[type="radio"] + label::after {
display: none;
}
}
.correct input[type="radio"] + label:before {
content: '\f00C';
font-family: "FontAwesome"!important;
color: #green;
}
.dim input[type="radio"]:checked + label:before {
content: '\f00d';
font-family: "FontAwesome"!important;
color: #red;
}
input[type="radio"]:not(:checked) + label:after {
opacity: 0;
transform: scale(0);
}
input[type="radio"]:checked + label:after {
opacity: 1;
transform: scale(1);
}
//end custom radio controls
.dim {
opacity: 0.5;
}
.bottom {
width:90%;
margin:0 auto;
display: flex;
flex-wrap: wrap;
align-items: flex-start;
justify-content: space-between;
div {
flex: 1 1 70%;
font-size: .9em;
}
.next {
flex: 0 1 10%;
margin-left: 3em;
}
#media (max-width: 600px) {
div, .next {
flex-basis: 100%;
}
.next {
margin-left: 0;
}
}
}
.get-results {
display: block;
margin: 2em auto;
}
.results {
font-size: 1.1em;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 40vh;
h1 {
font-family: #sans;
}
button {
margin-top: 1em;
}
}
}
When I check my browser console, I get the error message:
Uncaught SyntaxError: Unexpected token '<'
Related
I have a working code of a 10 text questions' trivia game with a timer (html, javascript, css).
How the questions can be served from a simple .txt file (so I can scale)?
How the javascript can choose the questions in a random order, every time the games starts?
I have studied 1 to get an idea. However, my javascript coding skills are very limited. I would appreciate if you can point me to the right direction.
(function() {
const playButton = document.querySelector("#play");
const letsstartButton = document.querySelector("#lets-start");
const playagainButton = document.querySelector("#play-again");
const howToPlayButton = document.querySelector("#how-to-play-button");
const closeHowToButton = document.querySelector("#close-how-to");
const howToPlayScreen = document.querySelector(".how-to-play-screen");
const mainScreen = document.querySelector(".main-screen");
const triviaScreen = document.querySelector(".trivia-screen");
const resultScreen = document.querySelector(".result-screen");
playButton.addEventListener("click", startTrivia);
letsstartButton.addEventListener("click", startTrivia);
playagainButton.addEventListener("click", startTrivia);
howToPlayButton.addEventListener("click", function() {
howToPlayScreen.classList.remove("hidden");
mainScreen.classList.add("hidden");
});
closeHowToButton.addEventListener("click", function() {
howToPlayScreen.classList.add("hidden");
mainScreen.classList.remove("hidden");
});
const questionLength = 10;
let questionIndex = 0;
let score = 0;
let questions = [];
let timer = null;
function startTrivia() {
//show spinner
questionIndex = 0;
questions = [];
score = 0;
window.setTimeout(function() {
//get questions from server
mainScreen.classList.add("hidden");
howToPlayScreen.classList.add("hidden");
resultScreen.classList.add("hidden");
triviaScreen.classList.remove("hidden");
questions = [{
answers: ["Roma", "Athens", "London", "Japan"],
correct: "Roma",
text: "YOUR TEXT HERE."
},
{
answers: ["Roma1", "Athens1", "London1", "Japan1"],
correct: "Athens1",
text: "YOUR TEXT HERE."
},
{
answers: ["Roma2", "Athens2", "London2", "Japan2"],
correct: "London2",
text: "YOUR TEXT HERE."
},
{
answers: ["Roma3", "Athens3", "London3", "Japan3"],
correct: "London3",
text: "YOUR TEXT HERE."
},
{
answers: ["Roma4", "Athens4", "London4", "Japan4"],
correct: "Athens4",
text: "YOUR TEXT HERE."
},
{
answers: ["Roma5", "Athens5", "London5", "Japan5"],
correct: "Athens5",
text: "YOUR TEXT HERE."
},
{
answers: ["Roma6", "Athens6", "London6", "Japan6"],
correct: "Roma6",
text: "YOUR TEXT HERE."
},
{
answers: ["Roma7", "Athens7", "London7", "Japan7"],
correct: "Japan7",
text: "YOUR TEXT HERE."
},
{
answers: ["Roma8", "Athens8", "London8", "Japan8"],
correct: "London8",
text: "YOUR TEXT HERE."
},
{
answers: ["Roma9", "Athens9", "London9", "Japan9"],
correct: "Japan9",
text: "YOUR TEXT HERE."
}
];
const questionCount = document.getElementById("question-count");
questionCount.innerHTML = questionLength.toString();
displayNextQuestion();
}, 50);
}
const isTriviaCompleted = function() {
return questionLength === questionIndex;
};
function displayNextQuestion() {
const answersContainer = document.getElementById("answers-container");
const answerButtons = answersContainer.querySelectorAll(".default-button");
answerButtons.forEach(function(element) {
element.disabled = false;
element.classList.remove("correct");
element.classList.remove("wrong");
});
if (isTriviaCompleted()) {
showScores();
} else {
startProgressbar();
timer = window.setTimeout(function() {
guess(null);
}, 10000);
setQuizText("This is from");
const textElement = document.getElementById("question-placement");
textElement.innerHTML = questions[questionIndex].text;
const choices = questions[questionIndex].answers;
for (let i = 0; i < choices.length; i++) {
const element = document.getElementById(`answer${i}`);
element.innerHTML = choices[i];
element.addEventListener("click", handleAnswerClick);
}
showProgress();
}
}
function handleAnswerClick(e) {
const el = e.currentTarget;
const answer = el.innerHTML;
el.removeEventListener("click", handleAnswerClick);
guess(answer);
}
function showProgress() {
const questionIndexElement = document.getElementById("question-index");
const index = questionIndex + 1;
questionIndexElement.innerHTML = index.toString();
}
function guess(answer) {
clearTimeout(timer);
const answersContainer = document.getElementById("answers-container");
const answerButtons = answersContainer.querySelectorAll(".default-button");
answerButtons.forEach((element) => {
element.disabled = true;
if (element.innerHTML === questions[questionIndex].correct) {
element.classList.add("correct");
}
});
stopProgressbar();
if (questions[questionIndex].correct === answer) { // correct answer
score++;
setQuizText("Fantastic … Correct!");
} else if (answer) { // incorrect answer
setQuizText("Nice try … You were close.");
answerButtons.forEach((element) => {
if (element.innerHTML === answer) {
element.classList.add("wrong");
}
});
} else {
setQuizText("Your time is out! Oh no!");
}
questionIndex++;
window.setTimeout(function() {
displayNextQuestion();
}, 2500);
}
function setQuizText(text) {
const el = document.getElementById("trivia-text");
el.innerHTML = text;
}
function showScores() {
const scoreElement = document.getElementById("score");
const scoreTotalElement = document.getElementById("score-total");
const scoreNameElement = document.getElementById("score-name");
scoreElement.innerHTML = score.toString();
scoreTotalElement.innerHTML = questionLength.toString();
if (score < 4) {
scoreNameElement.innerHTML = "Newbie";
} else if (score < 7) {
scoreNameElement.innerHTML = "Rookie";
} else if (score < 10) {
scoreNameElement.innerHTML = "Expert";
} else {
scoreNameElement.innerHTML = "Grandmaster";
}
triviaScreen.classList.add("hidden");
resultScreen.classList.remove("hidden");
}
function startProgressbar() {
// select div turn into progressbar
const progressbar = document.getElementById("progress-bar");
progressbar.innerHTML = "";
// create div changes width show progress
const progressbarInner = document.createElement("span");
// Append progressbar to main progressbardiv
progressbar.appendChild(progressbarInner);
// When all set start animation
progressbarInner.style.animationPlayState = "running";
}
function stopProgressbar() {
const progressbar = document.getElementById("progress-bar");
const progressbarInner = progressbar.querySelector("span");
progressbarInner.style.animationPlayState = "paused";
}
}());
*,
*:after,
*:before {
box-sizing: border-box;
}
body {
margin: 0;
font-family: 'Verdana', cursive;
text-transform: uppercase;
color: #ccc;
letter-spacing: 2px;
}
.container {
background: #999999;
}
.wrapper {
max-width: 800px;
margin: auto;
}
.screen-section {
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
padding: 20px 20px 70px;
position: relative;
}
.hidden {
display: none;
}
.trivia-screen-step {
color: #ccc;
}
.trivia-image-wrapper {
max-width: 100%;
margin: 50px auto;
position: relative;
}
.trivia-image {
max-width: 100%;
width: 300px;
position: relative;
z-index: 1;
}
.trivia-timer {
width: 550px;
max-width: 100%;
height: 20px;
border-radius: 3em;
margin-bottom: 50px;
padding: 5px 6px;
}
.trivia-timer span {
display: inline-block;
background: linear-gradient(90deg, #fff, #06c);
height: 10px;
vertical-align: top;
border-radius: 3em;
animation: progressbar-countdown;
animation-duration: 10s;
animation-iteration-count: 1;
animation-fill-mode: forwards;
animation-play-state: paused;
animation-timing-function: linear;
}
#keyframes progressbar-countdown {
0% {
width: 100%;
}
100% {
width: 0%;
}
}
.trivia-question {
margin-bottom: 50px;
}
.how-to-play-screen .default-button {
margin-bottom: 60px;
margin-top: 30px;
}
.button-container {
display: flex;
flex-wrap: wrap;
margin-bottom: 50px;
width: 600px;
max-width: 100%;
}
.button-outer {
flex-basis: 100%;
text-align: center;
margin-bottom: 20px;
max-width: 100%;
}
.default-button {
background: #333333;
border-radius: 3em;
font-family: 'Verdana', cursive;
font-size: 18px;
color: #fff;
letter-spacing: 2.45px;
padding: 10px 8px;
text-transform: uppercase;
transition: background .2s;
outline: none;
width: 250px;
cursor: pointer;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
}
.default-button:hover {
background: #222;
}
.default-button[disabled] {
background: transparent;
color: #222;
cursor: default;
}
.default-button[disabled]:hover {
background: transparent;
}
.default-button.correct {
cursor: default;
background: #2bb24a;
color: #fff;
}
.default-button.correct:hover {
background: #2bb24a;
color: #fff;
}
.default-button.wrong {
cursor: default;
background: #F6484C;
color: #fff;
}
.default-button.wrong:hover {
background: #F6484C;
color: #fff;
}
.title {
font-size: 32px;
margin-top: 100px;
}
.text {
line-height: 24px;
font-size: 16px;
font-family: 'Verdana', sans-serif;
text-align: center;
color: #ffffff;
text-transform: none;
}
.trivia-logo {
position: absolute;
bottom: 20px;
}
.trivia-corner-logo {
position: absolute;
left: 0;
top: 15px;
width: 100px;
}
.close-button {
position: absolute;
top: 50px;
right: 0;
background: transparent;
border: none;
color: #fff;
font-family: 'Verdana', cursive;
font-size: 34px;
outline: none;
text-transform: none;
cursor: pointer;
transition: color .2s;
}
.close-button:hover {
color: #eee;
}
.score-name {
margin: 0 0 28px;
font-size: 46px;
}
.score {
font-size: 18px;
margin-bottom: 10px;
}
.description {
text-align: center;
font-family: Verdana, sans-serif;
font-size: 16px;
color: #fff;
text-transform: none;
line-height: 24px;
display: inline-block;
margin-bottom: 30px;
}
#media screen and (min-width: 700px) {
.screen-section {
padding: 50px;
}
<div class="container">
<div class="wrapper">
<div class="screen-section main-screen">
<div class="trivia-image-wrapper">
<img alt="LOGO" src="./assets/Game-Logo.png" class="trivia-image">
</div>
<h1>Trivia</h1>
<div class="button-container">
<div class="button-outer">
<button class="default-button" id="play" type="button">Play</button>
</div>
<div class="button-outer">
<button class="default-button" id="how-to-play-button" type="button">How to play?</button>
</div>
</div>
<div class="trivia-logo">
<img alt="logo" src="./assets/-Logo.png">
</div>
</div>
<div class="screen-section hidden how-to-play-screen">
<img alt="LOGO" src="./assets/Game-Logo.png" class="trivia-corner-logo">
<button class="close-button" id="close-how-to" type="button">X</button>
<h2 class="title">How to Play</h2>
<p>Answer questions to score points.</p>
<button class="default-button" id="lets-start" type="button">Ok. Let's start</button>
<div class="trivia-logo">
<img alt="logo" src="./assets/-Logo.png">
</div>
</div>
<div class="screen-section hidden trivia-screen">
<div class="trivia-screen-step"><span id="question-index">1</span> out of <span id="question-count">10</span></div>
<div class="trivia-image-wrapper">
<p id="question-placement"></p>
</div>
<div class="trivia-timer" id="progress-bar"></div>
<div class="trivia-question" id="trivia-text"></div>
<div class="button-container" id="answers-container">
<div class="button-outer">
<button class="default-button" id="answer0" type="button"></button>
</div>
<div class="button-outer">
<button class="default-button" id="answer1" type="button"></button>
</div>
<div class="button-outer">
<button class="default-button" id="answer2" type="button"></button>
</div>
<div class="button-outer">
<button class="default-button" id="answer3" type="button"></button>
</div>
</div>
<div class="trivia-logo">
<img alt="logo" src="./assets/-Logo.png">
</div>
</div>
<div class="screen-section hidden result-screen">
<div class="trivia-image-wrapper">
<img alt="Trivia LOGO" src="./assets/Game-Logo.png" class="trivia-image">
</div>
<p class="score"><span id="score">0</span> out of <span id="score-total">10</span></p>
<h1 class="score-name" id="score-name">Trivia</h1>
<span class="description">you will learn more.</span>
<button class="default-button" id="play-again" type="button">Play again</button>
<div class="trivia-logo">
<img alt=" logo" src="./assets/-Logo.png">
</div>
</div>
</div>
</div>
In your case the best way would be to create an API (for example https://fastapi.tiangolo.com/) that would return a random question, but if you want to have it in a separate file you would simply have to move the variable questions to another javascript file and import it into the <head> of your web page.
To randomize the JSON you can use the following function:
function shuffleQuestions(questions) {
for (let i = questions.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[questions[i], questions[j]] = [questions[j], questions[i]];
}
}
(function() {
const playButton = document.querySelector("#play");
const letsstartButton = document.querySelector("#lets-start");
const playagainButton = document.querySelector("#play-again");
const howToPlayButton = document.querySelector("#how-to-play-button");
const closeHowToButton = document.querySelector("#close-how-to");
const howToPlayScreen = document.querySelector(".how-to-play-screen");
const mainScreen = document.querySelector(".main-screen");
const triviaScreen = document.querySelector(".trivia-screen");
const resultScreen = document.querySelector(".result-screen");
playButton.addEventListener("click", startTrivia);
letsstartButton.addEventListener("click", startTrivia);
playagainButton.addEventListener("click", startTrivia);
howToPlayButton.addEventListener("click", function() {
howToPlayScreen.classList.remove("hidden");
mainScreen.classList.add("hidden");
});
closeHowToButton.addEventListener("click", function() {
howToPlayScreen.classList.add("hidden");
mainScreen.classList.remove("hidden");
});
const questionLength = 10;
let questionIndex = 0;
let score = 0;
let questions = [];
let timer = null;
function startTrivia() {
//show spinner
questionIndex = 0;
questions = [];
score = 0;
window.setTimeout(function() {
//get questions from server
mainScreen.classList.add("hidden");
howToPlayScreen.classList.add("hidden");
resultScreen.classList.add("hidden");
triviaScreen.classList.remove("hidden");
questions = [{
answers: ["Roma", "Athens", "London", "Japan"],
correct: "Roma",
text: "YOUR TEXT HERE."
},
{
answers: ["Roma1", "Athens1", "London1", "Japan1"],
correct: "Athens1",
text: "YOUR TEXT HERE."
},
{
answers: ["Roma2", "Athens2", "London2", "Japan2"],
correct: "London2",
text: "YOUR TEXT HERE."
},
{
answers: ["Roma3", "Athens3", "London3", "Japan3"],
correct: "London3",
text: "YOUR TEXT HERE."
},
{
answers: ["Roma4", "Athens4", "London4", "Japan4"],
correct: "Athens4",
text: "YOUR TEXT HERE."
},
{
answers: ["Roma5", "Athens5", "London5", "Japan5"],
correct: "Athens5",
text: "YOUR TEXT HERE."
},
{
answers: ["Roma6", "Athens6", "London6", "Japan6"],
correct: "Roma6",
text: "YOUR TEXT HERE."
},
{
answers: ["Roma7", "Athens7", "London7", "Japan7"],
correct: "Japan7",
text: "YOUR TEXT HERE."
},
{
answers: ["Roma8", "Athens8", "London8", "Japan8"],
correct: "London8",
text: "YOUR TEXT HERE."
},
{
answers: ["Roma9", "Athens9", "London9", "Japan9"],
correct: "Japan9",
text: "YOUR TEXT HERE."
}
];
shuffleQuestions(questions);
const questionCount = document.getElementById("question-count");
questionCount.innerHTML = questionLength.toString();
displayNextQuestion();
}, 50);
}
const isTriviaCompleted = function() {
return questionLength === questionIndex;
};
function displayNextQuestion() {
const answersContainer = document.getElementById("answers-container");
const answerButtons = answersContainer.querySelectorAll(".default-button");
answerButtons.forEach(function(element) {
element.disabled = false;
element.classList.remove("correct");
element.classList.remove("wrong");
});
if (isTriviaCompleted()) {
showScores();
} else {
startProgressbar();
timer = window.setTimeout(function() {
guess(null);
}, 10000);
setQuizText("This is from");
const textElement = document.getElementById("question-placement");
textElement.innerHTML = questions[questionIndex].text;
const choices = questions[questionIndex].answers;
for (let i = 0; i < choices.length; i++) {
const element = document.getElementById(`answer${i}`);
element.innerHTML = choices[i];
element.addEventListener("click", handleAnswerClick);
}
showProgress();
}
}
function handleAnswerClick(e) {
const el = e.currentTarget;
const answer = el.innerHTML;
el.removeEventListener("click", handleAnswerClick);
guess(answer);
}
function showProgress() {
const questionIndexElement = document.getElementById("question-index");
const index = questionIndex + 1;
questionIndexElement.innerHTML = index.toString();
}
function guess(answer) {
clearTimeout(timer);
const answersContainer = document.getElementById("answers-container");
const answerButtons = answersContainer.querySelectorAll(".default-button");
answerButtons.forEach((element) => {
element.disabled = true;
if (element.innerHTML === questions[questionIndex].correct) {
element.classList.add("correct");
}
});
stopProgressbar();
if (questions[questionIndex].correct === answer) { // correct answer
score++;
setQuizText("Fantastic … Correct!");
} else if (answer) { // incorrect answer
setQuizText("Nice try … You were close.");
answerButtons.forEach((element) => {
if (element.innerHTML === answer) {
element.classList.add("wrong");
}
});
} else {
setQuizText("Your time is out! Oh no!");
}
questionIndex++;
window.setTimeout(function() {
displayNextQuestion();
}, 2500);
}
function setQuizText(text) {
const el = document.getElementById("trivia-text");
el.innerHTML = text;
}
function showScores() {
const scoreElement = document.getElementById("score");
const scoreTotalElement = document.getElementById("score-total");
const scoreNameElement = document.getElementById("score-name");
scoreElement.innerHTML = score.toString();
scoreTotalElement.innerHTML = questionLength.toString();
if (score < 4) {
scoreNameElement.innerHTML = "Newbie";
} else if (score < 7) {
scoreNameElement.innerHTML = "Rookie";
} else if (score < 10) {
scoreNameElement.innerHTML = "Expert";
} else {
scoreNameElement.innerHTML = "Grandmaster";
}
triviaScreen.classList.add("hidden");
resultScreen.classList.remove("hidden");
}
function startProgressbar() {
// select div turn into progressbar
const progressbar = document.getElementById("progress-bar");
progressbar.innerHTML = "";
// create div changes width show progress
const progressbarInner = document.createElement("span");
// Append progressbar to main progressbardiv
progressbar.appendChild(progressbarInner);
// When all set start animation
progressbarInner.style.animationPlayState = "running";
}
function stopProgressbar() {
const progressbar = document.getElementById("progress-bar");
const progressbarInner = progressbar.querySelector("span");
progressbarInner.style.animationPlayState = "paused";
}
function shuffleQuestions(questions) {
for (let i = questions.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[questions[i], questions[j]] = [questions[j], questions[i]];
}
}
}());
*,
*:after,
*:before {
box-sizing: border-box;
}
body {
margin: 0;
font-family: 'Verdana', cursive;
text-transform: uppercase;
color: #ccc;
letter-spacing: 2px;
}
.container {
background: #999999;
}
.wrapper {
max-width: 800px;
margin: auto;
}
.screen-section {
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
padding: 20px 20px 70px;
position: relative;
}
.hidden {
display: none;
}
.trivia-screen-step {
color: #ccc;
}
.trivia-image-wrapper {
max-width: 100%;
margin: 50px auto;
position: relative;
}
.trivia-image {
max-width: 100%;
width: 300px;
position: relative;
z-index: 1;
}
.trivia-timer {
width: 550px;
max-width: 100%;
height: 20px;
border-radius: 3em;
margin-bottom: 50px;
padding: 5px 6px;
}
.trivia-timer span {
display: inline-block;
background: linear-gradient(90deg, #fff, #06c);
height: 10px;
vertical-align: top;
border-radius: 3em;
animation: progressbar-countdown;
animation-duration: 10s;
animation-iteration-count: 1;
animation-fill-mode: forwards;
animation-play-state: paused;
animation-timing-function: linear;
}
#keyframes progressbar-countdown {
0% {
width: 100%;
}
100% {
width: 0%;
}
}
.trivia-question {
margin-bottom: 50px;
}
.how-to-play-screen .default-button {
margin-bottom: 60px;
margin-top: 30px;
}
.button-container {
display: flex;
flex-wrap: wrap;
margin-bottom: 50px;
width: 600px;
max-width: 100%;
}
.button-outer {
flex-basis: 100%;
text-align: center;
margin-bottom: 20px;
max-width: 100%;
}
.default-button {
background: #333333;
border-radius: 3em;
font-family: 'Verdana', cursive;
font-size: 18px;
color: #fff;
letter-spacing: 2.45px;
padding: 10px 8px;
text-transform: uppercase;
transition: background .2s;
outline: none;
width: 250px;
cursor: pointer;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
}
.default-button:hover {
background: #222;
}
.default-button[disabled] {
background: transparent;
color: #222;
cursor: default;
}
.default-button[disabled]:hover {
background: transparent;
}
.default-button.correct {
cursor: default;
background: #2bb24a;
color: #fff;
}
.default-button.correct:hover {
background: #2bb24a;
color: #fff;
}
.default-button.wrong {
cursor: default;
background: #F6484C;
color: #fff;
}
.default-button.wrong:hover {
background: #F6484C;
color: #fff;
}
.title {
font-size: 32px;
margin-top: 100px;
}
.text {
line-height: 24px;
font-size: 16px;
font-family: 'Verdana', sans-serif;
text-align: center;
color: #ffffff;
text-transform: none;
}
.trivia-logo {
position: absolute;
bottom: 20px;
}
.trivia-corner-logo {
position: absolute;
left: 0;
top: 15px;
width: 100px;
}
.close-button {
position: absolute;
top: 50px;
right: 0;
background: transparent;
border: none;
color: #fff;
font-family: 'Verdana', cursive;
font-size: 34px;
outline: none;
text-transform: none;
cursor: pointer;
transition: color .2s;
}
.close-button:hover {
color: #eee;
}
.score-name {
margin: 0 0 28px;
font-size: 46px;
}
.score {
font-size: 18px;
margin-bottom: 10px;
}
.description {
text-align: center;
font-family: Verdana, sans-serif;
font-size: 16px;
color: #fff;
text-transform: none;
line-height: 24px;
display: inline-block;
margin-bottom: 30px;
}
#media screen and (min-width: 700px) {
.screen-section {
padding: 50px;
}
<div class="container">
<div class="wrapper">
<div class="screen-section main-screen">
<div class="trivia-image-wrapper">
<img alt="LOGO" src="./assets/Game-Logo.png" class="trivia-image">
</div>
<h1>Trivia</h1>
<div class="button-container">
<div class="button-outer">
<button class="default-button" id="play" type="button">Play</button>
</div>
<div class="button-outer">
<button class="default-button" id="how-to-play-button" type="button">How to play?</button>
</div>
</div>
<div class="trivia-logo">
<img alt="logo" src="./assets/-Logo.png">
</div>
</div>
<div class="screen-section hidden how-to-play-screen">
<img alt="LOGO" src="./assets/Game-Logo.png" class="trivia-corner-logo">
<button class="close-button" id="close-how-to" type="button">X</button>
<h2 class="title">How to Play</h2>
<p>Answer questions to score points.</p>
<button class="default-button" id="lets-start" type="button">Ok. Let's start</button>
<div class="trivia-logo">
<img alt="logo" src="./assets/-Logo.png">
</div>
</div>
<div class="screen-section hidden trivia-screen">
<div class="trivia-screen-step"><span id="question-index">1</span> out of <span id="question-count">10</span></div>
<div class="trivia-image-wrapper">
<p id="question-placement"></p>
</div>
<div class="trivia-timer" id="progress-bar"></div>
<div class="trivia-question" id="trivia-text"></div>
<div class="button-container" id="answers-container">
<div class="button-outer">
<button class="default-button" id="answer0" type="button"></button>
</div>
<div class="button-outer">
<button class="default-button" id="answer1" type="button"></button>
</div>
<div class="button-outer">
<button class="default-button" id="answer2" type="button"></button>
</div>
<div class="button-outer">
<button class="default-button" id="answer3" type="button"></button>
</div>
</div>
<div class="trivia-logo">
<img alt="logo" src="./assets/-Logo.png">
</div>
</div>
<div class="screen-section hidden result-screen">
<div class="trivia-image-wrapper">
<img alt="Trivia LOGO" src="./assets/Game-Logo.png" class="trivia-image">
</div>
<p class="score"><span id="score">0</span> out of <span id="score-total">10</span></p>
<h1 class="score-name" id="score-name">Trivia</h1>
<span class="description">you will learn more.</span>
<button class="default-button" id="play-again" type="button">Play again</button>
<div class="trivia-logo">
<img alt=" logo" src="./assets/-Logo.png">
</div>
</div>
</div>
</div>
My Js code below is a quiz game. Each question takes up a full page, which means we have to change all the content every time the user presses the next button.
My issue is, i can't seem to get the image to switch to the next by fully replacing it. In my case the new images just go to the bottom of the first.
// Questions will be asked//
const Questions = [
{ id: 0, i: "images/trump.jpg", a: [{ text: "George Washington", isCorrect: false }, { text: "John Adams", isCorrect: false }, { text: "James Madison", isCorrect: false }, { text: "Donald John Trump", isCorrect: true } ] },
{ id: 1, i: "http://www.google.com/intl/en_com/images/logo_plain.png", a: [{ text: "Lampang", isCorrect: false, isSelected: false }, { text: "phuket", isCorrect: false }, { text: "Ayutthaya", isCorrect: false }, { text: "Bangkok", isCorrect: true } ] },
{ id: 2, i: "http://www.google.com/intl/en_com/images/logo_plain.png", a: [{ text: "surat", isCorrect: false }, { text: "vadodara", isCorrect: false }, { text: "gandhinagar", isCorrect: true }, { text: "rajkot", isCorrect: false } ] }
]
// Set start//
var start = true;
// Iterate//
function iterate(id) {
// Getting the result display section//
var result = document.getElementsByClassName("result");
result[0].innerText = "";
//Adding an image//
var img = document.createElement("img");
img.src = Questions[id].i;
var src = document.getElementById("image");
src.appendChild(img);
// Getting the options//
const op1 = document.getElementById('op1');
const op2 = document.getElementById('op2');
const op3 = document.getElementById('op3');
const op4 = document.getElementById('op4');
// Providing option text//
op1.innerText = Questions[id].a[0].text;
op2.innerText = Questions[id].a[1].text;
op3.innerText = Questions[id].a[2].text;
op4.innerText = Questions[id].a[3].text;
// Providing the true or false value to the options//
op1.value = Questions[id].a[0].isCorrect;
op2.value = Questions[id].a[1].isCorrect;
op3.value = Questions[id].a[2].isCorrect;
op4.value = Questions[id].a[3].isCorrect;
var selected = "";
// Show selection for op1//
op1.addEventListener("click", () => {
op1.style.backgroundColor = "lightgoldenrodyellow";
op2.style.backgroundColor = "lightskyblue";
op3.style.backgroundColor = "lightskyblue";
op4.style.backgroundColor = "lightskyblue";
selected = op1.value;
})
// Show selection for op2//
op2.addEventListener("click", () => {
op1.style.backgroundColor = "lightskyblue";
op2.style.backgroundColor = "lightgoldenrodyellow";
op3.style.backgroundColor = "lightskyblue";
op4.style.backgroundColor = "lightskyblue";
selected = op2.value;
})
// Show selection for op3//
op3.addEventListener("click", () => {
op1.style.backgroundColor = "lightskyblue";
op2.style.backgroundColor = "lightskyblue";
op3.style.backgroundColor = "lightgoldenrodyellow";
op4.style.backgroundColor = "lightskyblue";
selected = op3.value;
})
// Show selection for op4//
op4.addEventListener("click", () => {
op1.style.backgroundColor = "lightskyblue";
op2.style.backgroundColor = "lightskyblue";
op3.style.backgroundColor = "lightskyblue";
op4.style.backgroundColor = "lightgoldenrodyellow";
selected = op4.value;
})
// Grabbing the evaluate button//
const evaluate = document.getElementsByClassName("evaluate");
// Evaluate method//
evaluate[0].addEventListener("click", () => {
if (selected == "true") {
result[0].innerHTML = "True";
result[0].style.color = "green";
} else {
result[0].innerHTML = "False";
result[0].style.color = "red";
}
})
}
if (start) {
iterate("0");
}
// Next button and method//
const next = document.getElementsByClassName('next')[0];
var id = 0;
next.addEventListener("click", () => {
setTimeout(() => {
start = false;
if (id < 2) {
id++;
iterate(id);
console.log(id);
}
})
})
:root {
--primary: #1D1D1F;
--secondary: #858786;
--erro: #FF5757;
text-align: center;
align-items: center;
align-self: center;
font-family: SF Pro Display, SF Pro Icons, AOS Icons, Helvetica Neue, Helvetica, Arial, sans-serif;
}
.column {
justify-items: center;
justify-content: center;
float: left;
width: 50%;
justify-content: center;
}
.main-container {
margin: 50px;
border-radius: 20px;
background-color: #F5F8FA;
}
/* Clear floats after the columns */
.main-container:after {
content: "";
display: table;
clear: both;
}
.main-container img {
width: 320px;
height: 320px;
border-radius: 20px;
object-position: center;
object-fit: cover;
}
.center-cropped img {
border-radius: 2px;
width: 50%;
height: 50%;
object-position: center;
object-fit: cover;
}
.option-container {
margin-top: 50%;
margin-bottom: 50%;
grid-column: 1;
margin: 10px;
padding: 5px;
width: 100%;
height: auto;
}
.quiz-image {
margin: 10px;
padding: 10px;
width: 100%;
}
.bottom-left {
position: absolute;
bottom: 8px;
left: 16px;
}
.option {
border-radius: 10px;
border-width: 0px;
margin: 10px;
padding: 10px;
width: 50%;
height: auto;
font-size: 1rem;
font-weight: 600;
color: white;
background-color: #1da1f2;
}
.option:hover {
background-color: #e1e8ed;
}
.title h1 {
font-size: 4rem;
font-weight: 400;
padding: 10px;
color: #1d1d1d;
}
.title h2 {
font-size: 1.5rem;
font-weight: 400;
color: #1D1D1D;
}
h2 {
font-size: 3rem;
font-weight: 300;
color: #1D1D1D;
}
.question-container {
margin: 10px;
padding: 5px;
width: 80vw;
height: 10vh;
background-color: #c7dddf;
font-size: x-large;
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="title">
<h1>Guess The President</h1>
</div>
<div class="main-container" >
<div class="result"></div>
<div class="column">
<div class="quiz-image" id="image"></div>
</div>
<div class="column">
<div class="option-container">
<button class="option" onclick="" id="op1">option1</button>
<button class="option" id="op2">option2</button>
<button class="option" id="op3">option3</button>
<button class="option" id="op4">option4</button>
</div>
</div>
</div>
<div class="navigation">
<button class="evaluate">Evaluate</button>
<button class="next">Next</button>
</div>
Where you have your
src.appendChild(img);
Change it to this:
src.innerHTML = "";
src.appendChild(img);
This will clear out the previous image and the new one will be added in.
Hope this helps.
Just for the heck of it. Not really an answer, but thought your question used jQuery but your code did not use it (sorry, Andy, it's a mess!). The following code is dynamic and will adapt to how many questions you have, each may contain a varying amount of different answers.
// Questions will be asked//
const questions = [
{ id: 0, i: "https://upload.wikimedia.org/wikipedia/commons/thumb/5/56/Donald_Trump_official_portrait.jpg/330px-Donald_Trump_official_portrait.jpg", a: [{ text: "George Washington", isCorrect: false }, { text: "John Adams", isCorrect: false }, { text: "James Madison", isCorrect: false }, { text: "Donald John Trump", isCorrect: true } ] },
{ id: 1, i: "https://upload.wikimedia.org/wikipedia/commons/thumb/a/af/Bangkok_Montage_2021.jpg/375px-Bangkok_Montage_2021.jpg", a: [{ text: "Lampang", isCorrect: false, isSelected: false }, { text: "phuket", isCorrect: false }, { text: "Ayutthaya", isCorrect: false }, { text: "Bangkok", isCorrect: true } ] },
{ id: 2, i: "https://upload.wikimedia.org/wikipedia/commons/thumb/7/77/Akshardham_Gandhinagar_Gujarat.jpg/405px-Akshardham_Gandhinagar_Gujarat.jpg", a: [{ text: "surat", isCorrect: false }, { text: "vadodara", isCorrect: false }, { text: "gandhinagar", isCorrect: true }, { text: "rajkot", isCorrect: false } ] }
];
// Set start//
const game = {
active: true,
currentQuestion: 0,
selectedAnswer: null,
evaluated: false,
score: 0
};
function updateAnswerStatus() {
const progressText = `${game.currentQuestion + 1} / ${questions.length} - ${game.score} pts`;
const hasNext = game.currentQuestion < questions.length - 1;
$('#result').text(progressText);
$('#btnEvaluate').attr('disabled', game.evaluated || !game.selectedAnswer);
$('#btnNext').attr('disabled', !game.evaluated || !hasNext);
}
function selectAnswer(selectedAnswer) {
if (!game.evaluated) {
game.selectedAnswer = selectedAnswer;
$('#optionList .option').each(function () {
const option = $(this);
const answer = option.data('answer');
if (answer === selectedAnswer) {
option.addClass('selected');
} else {
option.removeClass('selected');
}
});
updateAnswerStatus()
}
}
function evaluateAnswer() {
if (!game.evaluated && game.selectedAnswer) {
game.evaluated = true;
$('#optionList .option').each(function () {
const option = $(this);
const answer = option.data('answer');
if (answer === game.selectedAnswer) {
option.addClass( answer.isCorrect ? 'correct' : 'incorrect');
game.score = game.score + (answer.isCorrect ? 1 : 0);
}
});
updateAnswerStatus();
}
}
function createOption(answer) {
return $('<button>')
.data({ answer })
.text(answer.text)
.addClass('option')
.on('click', function() {
selectAnswer(answer);
})
;
}
function renderCurrentQuestion() {
const question = questions[game.currentQuestion];
if (question) {
const optList = $('#optionList').empty();
const image = $('#image').empty();
game.selectedAnswer = null;
game.evaluated = false;
image.append($('<img>').attr('src', question.i));
for (const answer of question.a) {
optList.append( createOption(answer) );
}
}
updateAnswerStatus();
};
// next question?
$('#btnNext').on('click', function() {
game.currentQuestion = game.currentQuestion + 1;
renderCurrentQuestion();
}).attr('disabled', true);
$('#btnEvaluate').on('click', function() {
evaluateAnswer();
});
if (game.active) {
renderCurrentQuestion();
}
:root {
--primary: #1D1D1F;
--secondary: #858786;
--erro: #FF5757;
text-align: center;
align-items: center;
align-self: center;
font-family: SF Pro Display, SF Pro Icons, AOS Icons, Helvetica Neue, Helvetica, Arial, sans-serif;
}
.column {
justify-items: center;
justify-content: center;
float: left;
width: 50%;
justify-content: center;
}
.main-container {
margin: 50px;
border-radius: 20px;
background-color: #F5F8FA;
}
/* Clear floats after the columns */
.main-container:after {
content: "";
display: table;
clear: both;
}
.main-container img {
width: 320px;
height: 320px;
border-radius: 20px;
object-position: center;
object-fit: cover;
}
.center-cropped img {
border-radius: 2px;
width: 50%;
height: 50%;
object-position: center;
object-fit: cover;
}
.option-container {
margin-top: 50%;
margin-bottom: 50%;
grid-column: 1;
margin: 10px;
padding: 5px;
width: 100%;
height: auto;
}
.quiz-image {
margin: 10px;
padding: 10px;
width: 100%;
}
.bottom-left {
position: absolute;
bottom: 8px;
left: 16px;
}
.option {
border-radius: 10px;
border-width: 0px;
margin: 10px;
padding: 10px;
width: 50%;
height: auto;
font-size: 1rem;
font-weight: 600;
color: white;
background-color: #1da1f2;
}
.option:hover {
color: #1da1f2;
background-color: #d1d8dd;
}
.option.selected {
background-color: #bbbb33;
}
.option.selected:hover {
color: white;
background-color: #777733;
}
.option.selected.incorrect {
background-color: #bb3333;
}
.option.selected.correct {
background-color: #33bb33;
}
.title h1 {
font-size: 4rem;
font-weight: 400;
padding: 10px;
color: #1d1d1d;
}
.title h2 {
font-size: 1.5rem;
font-weight: 400;
color: #1D1D1D;
}
h2 {
font-size: 3rem;
font-weight: 300;
color: #1D1D1D;
}
.question-container {
margin: 10px;
padding: 5px;
width: 80vw;
height: 10vh;
background-color: #c7dddf;
font-size: x-large;
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="title">
<h1>Guess the answer</h1>
</div>
<div class="main-container" >
<div id="result" class="result"></div>
<div class="column">
<div id="image"class="quiz-image"></div>
</div>
<div class="column">
<div id="optionList" class="option-container"></div>
</div>
</div>
<div class="navigation">
<button id="btnEvaluate" class="evaluate">Evaluate</button>
<button id="btnNext" class="next">Next</button>
</div>
I have question about Vue.js. How can i fix this? I didn't find anything in documentation. I've got this error: "[vue/require-v-for-key]
Elements in iteration expect to have 'v-bind:key' directives."
And this: "Elements in iteration expect to have 'v-bind:key' directives."
I have this i my Roulette.vue
<template>
<div class="roulette">
<h1>Roulette</h1>
<div class="radio" v-for="genre in genres"> **here**
<input
#change="onGenrePick"
type="radio"
name="genre"
v-bind:id="genre.id"
v-bind:value="genre.id">
<label v-bind:for="genre.id">{{genre.name}}</label>
</div>
<Button class="btn" :onClick="roll">Roll</Button>
<MovieCard
v-if="!!movie"
v-bind:image="serviceGetImagePath(movie.poster_path)"
v-bind:title="movie.title"
v-bind:releaseDate="serviceFormatYear(movie.release_date)"
v-bind:id="movie.id"
v-bind:voteAverage="movie.vote_average"/>
</div>
</template>
<script>
import MovieCard from '../components/MovieCard'
import Button from '../components/Button'
import {movieService} from '../mixins/movieService'
export default {
name: 'Roulette',
components: {Button, MovieCard},
mixins: [movieService],
mounted: async function () {
this.genres = await this.serviceGetGenres()
},
data: () => ({
genres: [],
pickedGenres: new Set(),
movie: null
}),
methods: {
async roll() {
const genreIds = Array.from(this.pickedGenres)
const movies = await this.serviceGetMoviesByGenre(genreIds)
this.movie = movies[this.getRandom(movies.length)]
},
onGenrePick(e) {
this.pickedGenres.add(e.target.value)
},
getRandom(max) {
return Math.floor(Math.random() * max - 1)
}
}
}
</script>
<style scoped lang="scss">
.roulette {
margin: 40px;
}
.btn {
display: block;
min-width: 220px;
}
.radio {
display: inline-block;
margin: 20px 10px;
> label {
margin-left: 5px;
}
}
</style>
And in my UpcomingMovies.vue also
<template>
<div class="wrapper" v-if="movies.length">
<MovieCard
v-for="movie in movies" **here**
v-bind:image="serviceGetImagePath(movie.poster_path)"**here**
v-bind:title="movie.title"**here**
v-bind:releaseDate="serviceFormatYear(movie.release_date)"**here**
v-bind:id="movie.id"**here**
v-bind:voteAverage="movie.vote_average"/>**here**
<div class="loader">
<Button class="loader__btn" :onClick="loadNextPage">Load</Button>
</div>
<router-link :to="routes.roulette.path">
<div class="roulette">
<img src="../assets/roulette.png" alt="Roulette">
</div>
</router-link>
</div>
<Loader v-else/>
</template>
<script>
import Button from '../components/Button'
import MovieCard from '../components/MovieCard'
import Loader from '../components/Loader'
import { movieService } from '../mixins/movieService'
import routes from '../router/routes'
export default {
name: 'UpcomingMovies',
mixins: [movieService],
components: { Button, MovieCard, Loader },
data: () => ({
movies: [],
page: 1,
routes
}),
mounted() {
this.getMovies(this.page)
},
methods: {
getMovies: async function (page) {
const movies = await this.serviceGetMovies(page)
this.movies.push(...movies)
},
loadNextPage() {
this.getMovies(++this.page)
}
}
}
</script>
<style scoped lang="scss">
.wrapper {
width: 100%;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
margin-top: 40px;
margin-bottom: 40px;
}
.loader {
width: 100%;
text-align: center;
margin-top: 40px;
margin-bottom: 40px;
&__btn {
border: 5px dashed white;
background-color: transparent;
border-radius: 50%;
width: 80px;
height: 80px;
font-weight: bold;
text-transform: uppercase;
transition: border-radius 100ms ease-in-out, width 120ms ease-in-out 120ms;
&:hover {
border-radius: 0;
background-color: rgba(white, 0.1);
width: 200px;
}
}
}
.roulette {
cursor: pointer;
position: fixed;
right: 25px;
bottom: 25px;
> img {
opacity: .8;
animation: rotate 5s infinite;
width: 70px;
height: auto;
transition: opacity 220ms linear;
&:hover {
opacity: 1;
}
}
}
#keyframes rotate {
0% {
transform: rotate(0);
}
100% {
transform: rotate(360deg);
}
}
</style>
Vue internally uses unique keys for each loop to determine which element to update at which point during an update process. Therefore every v-for needs a v-bind:key attribute to work properly. In your special case this would be as the following:
<div class="radio" v-for="genre in genres" v-bind:key="someUniqueId"> **here**
You can use the current loop index as ID or anything else.
I'm a beginner developer , currently I'm working on a weather app project with vue.js . I have some problems with components and databinding with vue. Can anyone explain it to me with some code sample?!
I've read some forums, and watched some tutorial videos about custom components , tried it as well but i think I made some mistakes.
Specificly i would like to know, how can i bind my API url data to a dropdown box, that the location changes with every selection.
note: Used API is an open weather API, based on latitude and longitude locations.
var myDropdown = Vue.component('my-dropdown', {
template: '#dropdown',
data: function() {
return {
isOpen: false,
selected: null,
options: [
'Heidenheim an der Brenz',
'Giengen',
'Ulm',
]
}
},
methods: {
toggle: function() {
this.isOpen = !this.isOpen;
},
show: function() {
this.isOpen = true;
},
hide: function() {
this.isOpen = false;
},
set: function(option) {
this.selected = option;
this.hide();
}
},
mounted: function() {
console.log('My dropdown component is mounted!')
}
});
let weatherApp = new Vue({
el: '#app',
data: {
town: '',
Temp: '',
minTemp: '',
maxTemp:'',
wind: '',
description: '',
icon: '',
hdh: 'https://fcc-weather-api.glitch.me/api/current?lat=48.6833&lon=10.15',
ulm: 'https://fcc-weather-api.glitch.me/api/current?lat=48.39841&lon=9.99155',
giengen: 'https://fcc-weather-api.glitch.me/api/current?lat=48.39841&lon=9.99155'
},
methods: {
getWeather() {
var url = '';
axios
.get(url)
.then(response => {
this.town = response.data.name
this.Temp = response.data.main.temp;
this.minTemp = response.data.main.temp_min;
this.maxTemp = response.data.main.temp_max;
this.wind = response.data.wind.speed;
this.description = response.data.weather[0].description;
this.icon = response.data.weather[0].icon;
})
.catch(error => {
console.log(error);
});
},
},
beforeMount() {
this.getWeather();
},
});
body {
background: url(https://shiftyjelly.files.wordpress.com/2013/11/w.jpg?w=960&h=400);
background-repeat: no-repeat;
font-family: 'Montserrat', sans-serif;
font-weight: 100;
text-shadow: 0px 0px 2px #000000;
color: #ffffff;
width: 960px;
height: 400px;
}
#weather {
padding: 15px;
}
#temperature {
position: absolute;
font-size: 40px;
top: 240px;
left: 420px;
color: black;
}
#temp-values {
text-align: right;
position: relative;
text-justify: distribute;
display: block;
top: 60px;
left: -200px;
color: black;
}
#info {
padding: 15px;
}
#name {
top: 10px;
left: 300px;
font-size: 40px;
color: black;
position: relative;
}
.wind {
top: 180px;
left: 380px;
color: black;
position: relative;
}
#icon {
color: black;
font-size: 20px;
left: -180px;
top: 120px;
position: relative;
}
#my-dropdown {
cursor: pointer;
position: absolute;
left: 0%;
top: 0%;
min-width: 250px;
height: 40px;
}
#selected {
position: relative;
z-index: 2;
display: block;
width: 100%;
height: 40px;
padding: 0 20px;
background: rgba(05, 46, 41, 0.1);
border-radius: 10px;
font: 1.25rem/40px 'Ubuntu', Helvetica, Arial, sans-serif;
text-shadow: 2px 2px 0px #000;
color: rgb(0, 237, 255);
}
#selected: after {
opacity: 0.5;
display: inline-block;
margin-left: 10px;
content: '▼';
color: black;
}
#selected:hover: after {
opacity: 1;
}
#options {
position: absolute;
left: 0;
top: 100%;
z-index: 1;
width: 100%;
margin-top: 3px;
background: rgba(05, 46, 41, 0.1);
border-radius: 10px;
}
#option {
padding: 5px 20px;
border-bottom: 1px solid rgba(0, 0, 0, 0.25);
font: 1.2rem 'Vollkorn', Georgia, Times, serif;
color: rgb(0, 237, 255);
text-shadow: 2px 2px 0px #000;
}
#option:hover {
background-color: rgba(0, 0, 0, 0.05);
}
#option:last-child {
border-bottom: none;
}
#fade-enter-active, .fade-leave-active {
transition: all 0.25s ease-out;
}
#fade-enter, .fade-leave-active {
opacity: 0.5;
transform: translateY(-30px);
}
* { box-sizing: border-box; }
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.1/vue.js"></script>
<title>Weather App</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<template id="dropdown">
<div id="my-dropdown">
<div id="selected" #click="toggle">Select Your Town Here</div>
<div id="options" v-show="isOpen">
<div id="option" v-for="option in options" #click="set(option)">
{{ option }}
</div>
</div>
</div>
</template>
<body>
<div id="app">
<my-dropdown></my-dropdown>
<div id="weather">
<span id="name">{{town}}</span>
<span id="icon">{{description}}</span>
<span id="temperature">{{Temp}}°</span><br>
<span id="temp-values">Min: {{minTemp}}° <br> Max: {{maxTemp}}°</span>
</div>
<div id="info">
<img class="wind" height="40px" width="40px" src="https://www.svgrepo.com/show/71601/wind.svg">
<span class="wind">{{wind}} m/s</span>
</div>
</div>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</body>
</html>
Here is a fiddle
First, it's best to keep all of your data in one place rather than hard-coding the town names in the dropdown component. You also don't need to store the whole url each time. So remove options from the dropdown. You'll pass the town data to it via prop.
1) Restructure your app's town data into an array of objects like this:
data: {
...
towns: [
{abbr: 'hdh', name: 'Heidenheim an der Brenz', lat: '48.6833', lon: '10.15'},
{abbr: 'ulm', name: 'Ulm', lat: '48.39841', lon: '9.99155'},
{abbr: 'giengen', name: 'Giengen', lat: '48.39841', lon: '9.99155'}
]
}
2) Pass the town data via prop named "options" to the dropdown component:
<my-dropdown :options="towns"></my-dropdown>
3) Change the dropdown label to {{ option.name }}
4) Add the prop to the component:
props: ['options']
5) Emit a custom event when the town changes:
set: function(option) {
this.$emit('change-town', option);
...
}
6) Process that event in the parent template's getWeather:
<my-dropdown :options="towns" #change-town="getWeather"></my-dropdown>
7) Generate the URL and send the request:
getWeather(option) {
const urlpath = 'https://fcc-weather-api.glitch.me/api/current?'
const qs = `lat=${option.lat}&lon=${option.lon}`;
const url = path + qs;
axios.get(url)...
...
}
trying to replace customer js with
export default[
{ id: 0, name: 'some one', title: 'zero' },
{ id: 1, name: 'test last', title:'one'},
{ id: 2, name: 'test first', title:'two'},
{ id: 3, name: 'test second', title:'three'},
{ id: 4, name: 'test third', title:'four'},
{ id: 5, name: 'test fourth', title:'five'},
{ id: 6, name: 'test fifth', title:'six'}
];
My app is fine while using the above raw json.
since i am trying to get the date using axios in same file as per below code
Something i am missing to get the data into app.
import axios from 'axios';
export default {
data () {
return {
info: []
}
},
mounted () {
axios
.get('http://localhost/json/test.json')
.then(response => (this.info = response))
}
}
for getting data from url into customer.js
not working in case.
while my app.vue is
<template>
<div id="app">
<Autocomplete :items="customers"
filterby="name"
#change="onChange"
title="Look for a customer"
#selected="customerSelected"/>
</div>
</template>
<script>
import customers from './assets/customers';
import Autocomplete from './components/Autocomplete'
export default {
name: 'App',
mounted() {
this.customers = customers;
},
data() {
return {
customers: []
};
},
methods: {
customerSelected(customer) {
console.log(`Customer Selected:\nid: ${customer.id}\nname: ${customer.name}\ntitle:${customer.title}`);
},
onChange(value) {
// do something with the current value
}
},
components: {
Autocomplete
}
}
</script>
<style>
#app {
margin: 0px auto;
margin-top: 60px;
width: 400px;
}
</style>
and Autocomplete.vue is
<template>
<div class="autocomplete">
<div class="input" #click="toggleVisible" v-text="selectedItem ? selectedItem[filterby] : ''"></div>
<div class="placeholder" v-if="selectedItem == null" v-text="title"></div>
<button class="close" #click="selectedItem = null" v-if="selectedItem">x</button>
<div class="popover" v-show="visible">
<input type="text"
ref="input"
v-model="query"
#keydown.up="up"
#keydown.down="down"
#keydown.enter="selectItem"
placeholder="Start Typing...">
<div class="options" ref="optionsList">
<ul>
<li v-for="(match, index) in matches"
:key="index"
:class="{ 'selected': (selected == index)}"
#click="itemClicked(index)"
v-text="match[filterby]"></li>
</ul>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
items: {
default: [],
type: Array
},
filterby: {
type: String
},
title: {
default: 'Select One...',
type: String
},
shouldReset: {
type: Boolean,
default: true
}
},
data() {
return {
itemHeight: 39,
selectedItem: null,
selected: 0,
query: '',
visible: false
};
},
methods: {
toggleVisible() {
this.visible = !this.visible;
setTimeout(() => {
this.$refs.input.focus();
}, 50);
},
itemClicked(index) {
this.selected = index;
this.selectItem();
},
selectItem() {
if (!this.matches.length) {
return;
}
this.selectedItem = this.matches[this.selected];
this.visible = false;
if (this.shouldReset) {
this.query = '';
this.selected = 0;
}
this.$emit('selected', JSON.parse(JSON.stringify(this.selectedItem)));
},
up() {
if (this.selected == 0) {
return;
}
this.selected -= 1;
this.scrollToItem();
},
down() {
if (this.selected >= this.matches.length - 1) {
return;
}
this.selected += 1;
this.scrollToItem();
},
scrollToItem() {
this.$refs.optionsList.scrollTop = this.selected * this.itemHeight;
}
},
computed: {
matches() {
this.$emit('change', this.query);
if (this.query == '') {
return [];
}
return this.items.filter((item) => item[this.filterby].toLowerCase().includes(this.query.toLowerCase()))
}
}
}
</script>
<style scoped>
.autocomplete {
width: 100%;
position: relative;
}
.input {
height: 40px;
border-radius: 3px;
border: 2px solid lightgray;
box-shadow: 0 0 10px #eceaea;
font-size: 25px;
padding-left: 10px;
padding-top: 10px;
cursor: text;
}
.close {
position: absolute;
right: 2px;
top: 4px;
background: none;
border: none;
font-size: 30px;
color: lightgrey;
cursor: pointer;
}
.placeholder {
position: absolute;
top: 11px;
left: 11px;
font-size: 25px;
color: #d0d0d0;
pointer-events: none;
}
.popover {
min-height: 50px;
border: 2px solid lightgray;
position: absolute;
top: 46px;
left: 0;
right: 0;
background: #fff;
border-radius: 3px;
text-align: center;
}
.popover input {
width: 95%;
margin-top: 5px;
height: 40px;
font-size: 16px;
border-radius: 3px;
border: 1px solid lightgray;
padding-left: 8px;
}
.options {
max-height: 150px;
overflow-y: scroll;
margin-top: 5px;
}
.options ul {
list-style-type: none;
text-align: left;
padding-left: 0;
}
.options ul li {
border-bottom: 1px solid lightgray;
padding: 10px;
cursor: pointer;
background: #f1f1f1;
}
.options ul li:first-child {
border-top: 2px solid #d6d6d6;
}
.options ul li:not(.selected):hover {
background: #8c8c8c;
color: #fff;
}
.options ul li.selected {
background: orange;
color: #fff;
font-weight: 600;
}
</style>
In most of the cases, response consists of several properties and you will need response.data.
if this didn't work then print the response, using console.log(response) and inspect it such that which key holds the JSON data.