const btn_start = document.getElementById("start");
let container = document.getElementById("container");
let questionTag = document.getElementById("question");
let answerTag = document.getElementsByClassName("answer");
const nxt_question_btn = document.getElementById("next");
const end_quiz_btn = document.getElementById("end");
btn_start.addEventListener("click", startQuiz);
nxt_question_btn.addEventListener("click", nextQuestion);
let currentQuestionIndex = 0;
let myQuestions = [
{
question: "What's 2+2?",
answers: [
{ text: "4", correct: true },
{ text: "2", correct: false },
{ text: "10", correct: false },
{ text: "1", correct: false },
],
},
{
question: "What's 10+10?",
answers: [
{ text: "20", correct: true },
{ text: "2", correct: false },
{ text: "18", correct: false },
{ text: "0", correct: false },
],
},
{
question: "What's 30+30?",
answers: [
{ text: "60", correct: true },
{ text: "24", correct: false },
{ text: "100", correct: false },
{ text: "50", correct: false },
],
},
{
question: "What's 10+30?",
answers: [
{ text: "40", correct: true },
{ text: "44", correct: false },
{ text: "70", correct: false },
{ text: "10", correct: false },
],
},
];
function startQuiz() {
container.style.visibility = "visible";
btn_start.style.visibility = "hidden";
end.style.visibility = "hidden";
showQuestion(myQuestions[0]);
}
function showQuestion(questionAndAnswers) {
const shuffledAnswers = _.shuffle(questionAndAnswers.answers);
questionTag.innerText = questionAndAnswers.question;
shuffledAnswers.forEach(({ text, correct }, i) => {
answerTag[i].innerText = text;
answerTag[i].dataset.correct = correct;
});
}
document.querySelectorAll(".answer").forEach((answer) => {
answer.addEventListener("click", (event) => {
if (event.target.dataset) {
answer.style.border = "1px solid black";
}
});
});
function nextQuestion() {
const nextIndex = currentQuestionIndex + 1;
if (nextIndex <= myQuestions.length - 1) {
showQuestion(myQuestions[nextIndex]);
currentQuestionIndex = nextIndex;
} else {
end.style.visibility = "visible";
nxt_question_btn.style.visibility = "hidden";
}
}
<button id="start" type="button">Start quiz</button>
<div id="container">
<h2>Quiz</h2>
<div class="time">
<span>Time left:</span>
<p id="time"> 30</p>
</div>
<h3 id="question"></h3>
<div class="answers">
<button id="answer1" class="answer"></button>
<button id="answer2" class="answer"></button>
<button id="answer3" class="answer"></button>
<button id="answer4" class="answer"></button>
</div>
<button id="next" class="btns">Next Question</button>
<button id="end" class="btns">End Quiz</button>
</div>
In my Javascript quiz game, when an answer is selected, we load up another question with other answers but the problem that occurs is the selected answers get a black border but when the next question is loaded up, the border stays. How do I make it go back to normal when the next question is loaded?
You could use border: initial to revert is back to default.
The initial CSS keyword applies the initial (or default) value of a property to an element. It can be applied to any CSS property. This includes the CSS shorthand all, with which initial can be used to restore all CSS properties to their initial state.
In your code, you could use:
let buttons = document.querySelectorAll('.answer')
buttons.forEach(button => {
button.addEventListener('click', function() {
this.style.border = "1px solid black";
buttons.forEach(each => {
if (each != button) each.style.border = "initial"
})
})
})
<button id="answer1" class="answer"></button>
<button id="answer2" class="answer"></button>
<button id="answer3" class="answer"></button>
<button id="answer4" class="answer"></button>
Or if the style of border you doesn't satisfy with, you could define a new class and use toggle to add / remove the classList.
*Also, note that your code is currently displaying reference error
In your "next question" function, set the button's style properties to none. Then put a setTimeout function where you set the same element's style property to '' (empty string). This should reset it.
const btn_start = document.getElementById("start");
let container = document.getElementById("container");
let questionTag = document.getElementById("question");
let answerTag = document.getElementsByClassName("answer");
const nxt_question_btn = document.getElementById("next");
const end_quiz_btn = document.getElementById("end");
btn_start.addEventListener("click", startQuiz);
nxt_question_btn.addEventListener("click", nextQuestion);
let currentQuestionIndex = 0;
let myQuestions = [
{
question: "What's 2+2?",
answers: [
{ text: "4", correct: true },
{ text: "2", correct: false },
{ text: "10", correct: false },
{ text: "1", correct: false },
],
},
{
question: "What's 10+10?",
answers: [
{ text: "20", correct: true },
{ text: "2", correct: false },
{ text: "18", correct: false },
{ text: "0", correct: false },
],
},
{
question: "What's 30+30?",
answers: [
{ text: "60", correct: true },
{ text: "24", correct: false },
{ text: "100", correct: false },
{ text: "50", correct: false },
],
},
{
question: "What's 10+30?",
answers: [
{ text: "40", correct: true },
{ text: "44", correct: false },
{ text: "70", correct: false },
{ text: "10", correct: false },
],
},
];
function startQuiz() {
container.style.visibility = "visible";
btn_start.style.visibility = "hidden";
end.style.visibility = "hidden";
showQuestion(myQuestions[0]);
}
function showQuestion(questionAndAnswers) {
const shuffledAnswers = _.shuffle(questionAndAnswers.answers);
questionTag.innerText = questionAndAnswers.question;
shuffledAnswers.forEach(({ text, correct }, i) => {
answerTag[i].innerText = text;
answerTag[i].dataset.correct = correct;
});
}
document.querySelectorAll(".answer").forEach((answer) => {
answer.addEventListener("click", (event) => {
if (event.target.dataset) {
answer.style.border = "1px solid black";
}
});
});
function nextQuestion() {
document.querySelectorAll('.answer').forEach((answer) => {
answer.style.border = 'none';
setTimeout(function() {
answer.style.border = ''
})
})
const nextIndex = currentQuestionIndex + 1;
if (nextIndex <= myQuestions.length - 1) {
showQuestion(myQuestions[nextIndex]);
currentQuestionIndex = nextIndex;
} else {
end.style.visibility = "visible";
nxt_question_btn.style.visibility = "hidden";
}
}
<button id="start" type="button">Start quiz</button>
<div id="container">
<h2>Quiz</h2>
<div class="time">
<span>Time left:</span>
<p id="time"> 30</p>
</div>
<h3 id="question"></h3>
<div class="answers">
<button id="answer1" class="answer"></button>
<button id="answer2" class="answer"></button>
<button id="answer3" class="answer"></button>
<button id="answer4" class="answer"></button>
</div>
<button id="next" class="btns">Next Question</button>
<button id="end" class="btns">End Quiz</button>
</div>
Related
I have my search box almost finished, I think the logic of the code is correct, but it is throwing me an error on the line that I want to add the "hide" class, if someone would be so kind as to give me a hand I would appreciate it.
Error:
Uncaught TypeError: Cannot read properties of undefined (reading 'classList') at prueba.js:301:19 at Array.forEach (<anonymous>) at HTMLInputElement.<anonymous> (prueba.js:296:13)
Code:
const traigoJson = async()=> {
const respuesta = await fetch ('../stock.json')
const data = await respuesta.json()
let contenedor = document.getElementById("container")
data.forEach((producto, indice) => {
let card = document.createElement("div")
card.classList.add("col-xl-3", "col-lg-3", "col-md-6", "col-sm-6", "hide", `${producto.deporte}`, "products")
card.innerHTML = `<div class="glasses_box">
<figure><img src="${producto.imagen}" alt="esta es una foto de ${producto.nombre}"/></figure>
<h3><span class="blu">$</span>${producto.precio}</h3>
<p class="product-name">${producto.nombre}</p>
<button type="button" class="btn btn-outline-secondary boton-comprar" id="asd" onClick = "agregarAlCarrito (${indice})">COMPRAR</button>
</div>`
contenedor.appendChild(card)
producto.card = card;
})
}
traigoJson()
const searchInput = document.getElementById("search-input")
const card = document.querySelectorAll(".products")
searchInput.addEventListener("input", e => {
const value = e.target.value.toLowerCase()
productos.forEach(producto => {
const isVisible =
producto.nombre.toLowerCase().includes(value) ||
producto.deporte.toLowerCase().includes(value)
producto.card.classList.toggle("hide", !isVisible)
})
})
You create a card element for each product, but then do not add the card to the product item. And so the code errors during the search when trying to get the value of producto.card.
In the code below I've added this line to the forEach loop:
producto.card = card;
This change should be enough to get you started.
let productos = [{
nombre: "1",
deporte: "d",
precio: "100",
imagen: "https://via.placeholder.com/100"
},
{
nombre: "2",
deporte: "d",
precio: "100",
imagen: "https://via.placeholder.com/100"
},
{
nombre: "3",
deporte: "d",
precio: "100",
imagen: "https://via.placeholder.com/100"
},
{
nombre: "4",
deporte: "d",
precio: "100",
imagen: "https://via.placeholder.com/100"
},
{
nombre: "5",
deporte: "d",
precio: "100",
imagen: "https://via.placeholder.com/100"
}
];
const crearCards = () => {
let contenedor = document.getElementById("container")
productos.forEach((producto, indice) => {
let card = document.createElement("div")
card.classList.add("col-xl-3", "col-lg-3", "col-md-6", "col-sm-6", "hide", `${producto.deporte}`, "products")
card.innerHTML = `<div class="glasses_box">
<figure><img src="${producto.imagen}" alt="esta es una foto de ${producto.nombre}"/></figure>
<h3><span class="blu">$</span>${producto.precio}</h3>
<p class="product-name">${producto.nombre}</p>
<button type="button" class="btn btn-outline-secondary boton-comprar" id="asd" onClick = "agregarAlCarrito (${indice})">COMPRAR</button>
</div>`
contenedor.appendChild(card)
producto.card = card;
})
}
const searchInput = document.getElementById("search-input")
const card = document.querySelectorAll(".products")
searchInput.addEventListener("input", e => {
const value = e.target.value.toLowerCase()
productos.forEach(producto => {
const isVisible =
producto.nombre.toLowerCase().includes(value) ||
producto.deporte.toLowerCase().includes(value)
producto.card.classList.toggle("hide", !isVisible)
})
})
crearCards();
.hide {
display: none;
}
<div id="container" class="container">
Type 1, 2, 3, 4, or 5 into the search box to test:
<input id="search-input" class="form-control my-2" placeholder="search">
<div class="products"></div>
</div>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.2.1/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.2.1/dist/js/bootstrap.bundle.min.js"></script>
I have to build a quiz/survey app in vue.js, I'm pretty new to vue and still trying to learn it. I have a quiz/survey that asks different questions depending on what the user answers in the initial question.
so if the user picks yes it will display question 2 if the user picks no it will display question 3 etc.
I'm not sure what the best way of going around it but so far I have this.
Is there anyway i can use the value of my answer as the questionIndex after a person clicks next?
JS file:
"use strict";
let quiz = {
title: "Asbestos Quiz",
questions: [
{
text: 'Do you need help with an Asbestos Survey?',
answers: [
{
text: "Yes",
value: '2'`enter code here`
},
{
text: "No",
value: '3'
},
]
},
{
text: 'Was your property constructed pre 2000',
answers: [
{
text: "Yes",
value: '4'
},
{
text: "No",
value: '5'
},
]
},
{
text: 'Do you need an Asbestos Management plan?',
answers: [
{
text: "Yes",
value: '6'
},
{
text: "No",
value: '7'
},
]
}
]
};
var app = new Vue({
el: "#app",
data: {
quiz: quiz,
questionIndex: 0,
responses: [],
errors: [],
error: ''
},
methods: {
prev: function() {
this.questionIndex--;
},
next: function() {
if (this.responses[this.questionIndex] === undefined) {
this.errors[this.questionIndex] = 1;
this.error = 'Please select your answer';
}
else {
this.errors[this.questionIndex] = 0;
this.questionIndex++;
}
},
score: function() {
},
playAgain: function() {
this.questionIndex = 0;
}
}
});
HTML:
<html lang="en">
<head>
<title>Vue quiz/survey</title>
<meta name="viewport" content="width=device-width"/>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<!-- <link rel="stylesheet" href="index.css"> -->
</head>
<body>
<div id="app">
<div class="container">
<div class="jumbotron mt-3">
<h1 class="mb-5">{{ quiz.title }}</h1>
<hr>
<p v-if="errors[questionIndex]" class="alert alert-danger">
{{ error }}
</p>
<div v-for="(question, index) in quiz.questions">
<div v-show="index === questionIndex">
<h4 class="mt-5 mb-3">{{ question.text }}</h4>
<div v-for="answer in question.answers" class="form-check">
<label class="form-check-label">
<input class="form-check-input" type="radio"
:value="answer.value"
:name="index"
v-model="responses[index]">
{{answer.text}}
</label>
</div>
<div class="mt-5">
<button
class="btn btn-primary"
v-if="questionIndex > 0"
#click="prev">
prev
</button>
<button class="btn btn-secondary" #click="next">
next
</button>
</div>
</div>
</div>
<div v-show="questionIndex === quiz.questions.length">
<h3>Your Results</h3>
<p>
You are: {{ score() }}
</p>
<button class="btn btn-success" #click="playAgain">
Play Again!
</button>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="index.js"></script>
</body>
</html>
I thought that this sounded like a potentially interesting exercise, so I spent some time creating an implementation in a Vue CLI sandbox app that I built and use for trying out various ideas.
I learned a few things, and hopefully you will get something out of it. I left the 'Previous' functionality as a TODO if you decide you like it and want implement that yourself.
QuizQuestions.vue
<template>
<div class="quiz-questions">
<div class="jumbotron mt-3">
<h1 class="mb-5">{{ quiz.title }}</h1>
<hr>
<question v-if="!showResults" :question="currentQuestion" #answer-selected="processAnswer" />
<div class="mt-5">
<button class="btn btn-primary" v-if="currentQuestionId > 1 && !showResults" #click="getPreviousQuestion">
prev
</button>
<button v-if="!showResults" class="btn btn-secondary" #click="getNextQuestion">
{{ nextButtonLabel }}
</button>
</div>
<div v-if="showResults">
<h3>Your Results</h3>
<table class="table table-bordered">
<thead>
<tr>
<th>QUESTION</th>
<th>ANSWER</th>
</tr>
</thead>
<tbody>
<tr v-for="(response, index) in responses" :key="index">
<td>{{ getQuestionText(response.questionId) }}</td>
<td>{{ getAnswerText(response.answerId) }}</td>
</tr>
</tbody>
</table>
<button class="btn btn-success" #click="playAgain">
Play Again!
</button>
</div>
</div>
</div>
</template>
<script>
import quiz from './quiz.js';
import Question from '#/components/stackoverflow/Question'
export default {
components: {
Question
},
data() {
return {
quiz: quiz,
currentQuestionId: 1,
currentAnswerId: 1,
previousQuestionId: 0,
responses: [],
showResults: false,
errors: [],
error: ''
}
},
computed: {
currentQuestion() {
return this.quiz.questions.find( question => {
return question.id === this.currentQuestionId;
})
},
nextQuestionId() {
let retVal = 0;
if (this.currentAnswerId > 0) {
let tempAnswer = this.currentQuestion.answers.find( answer => {
return answer.id === this.currentAnswerId;
});
retVal = tempAnswer.nextQuestionId;
}
return retVal;
},
lastQuestion() {
return this.currentQuestion.answers[0].nextQuestionId === 0;
},
nextButtonLabel() {
return this.lastQuestion ? 'Finish' : 'Next';
}
},
methods: {
getPreviousQuestion() {
this.currentQuestionId = this.previousQuestionId;
},
getNextQuestion() {
// TODO: Look for existing response for this question in case the 'Previous' button was pressed
// If found, update answer
// Store current question id and answer id in responses
let response = { questionId: this.currentQuestionId, answerId: this.currentAnswerId };
this.responses.push(response);
if (this.lastQuestion) {
this.showResults = true;
return;
}
this.previousQuestionId = this.currentQuestionId;
this.currentQuestionId = this.nextQuestionId;
//console.log(this.responses);
},
getQuestionText(id) {
let result = this.quiz.questions.find( question => {
return question.id === id;
});
return result.text;
},
getAnswerText(id) {
// NOTE: Since answers are currently limited to '1 = Yes' and '2 = No',
// this method does not need to involve any look up
return id === 1 ? 'Yes' : 'No';
},
processAnswer(selectedAnswerId) {
this.currentAnswerId = selectedAnswerId;
},
score() {
return 'TODO'
},
playAgain() {
this.currentQuestionId = 1;
this.showResults = false;
this.responses = [];
}
}
}
</script>
Question.vue
<template>
<div class="question">
<h4 class="mt-5 mb-3">{{ question.text }}</h4>
<div class="form-check" v-for="(answer, idx) in question.answers" :key="idx">
<input class="form-check-input" type="radio"
:value="answer.id" v-model="answerId" #change="answerSelected">
<label class="form-check-label">
{{answer.text}}
</label>
</div>
</div>
</template>
<script>
export default {
props: {
question: {
type: Object,
required: true
}
},
data() {
return {
answerId: 1
}
},
watch:{
question() {
// Reset on new question
this.answerId = 1;
}
},
methods: {
answerSelected() {
this.$emit('answer-selected', this.answerId);
}
}
}
</script>
I also modified your test data by adding various ID properties to help with tracking, as well as created a few more placeholder questions.
quiz.js
const quiz = {
title: "Asbestos Quiz",
questions: [
{
id: 1,
text: 'Do you need help with an Asbestos Survey?',
answers: [
{
id: 1,
text: "Yes",
nextQuestionId: 2
},
{
id: 2,
text: "No",
nextQuestionId: 3
},
]
},
{
id: 2,
text: 'Was your property constructed pre 2000',
answers: [
{
id: 1,
text: "Yes",
nextQuestionId: 4
},
{
id: 2,
text: "No",
nextQuestionId: 5
},
]
},
{
id: 3,
text: 'Do you need an Asbestos Management plan?',
answers: [
{
id: 1,
text: "Yes",
nextQuestionId: 6
},
{
id: 2,
text: "No",
nextQuestionId: 7
},
]
},
{
id: 4,
text: 'Question 4',
answers: [
{
id: 1,
text: "Yes",
nextQuestionId: 0
},
{
id: 2,
text: "No",
nextQuestionId: 0
},
]
},
{
id: 5,
text: 'Question 5',
answers: [
{
id: 1,
text: "Yes",
nextQuestionId: 0
},
{
id: 2,
text: "No",
nextQuestionId: 0
},
]
},
{
id: 6,
text: 'Question 6',
answers: [
{
id: 1,
text: "Yes",
nextQuestionId: 0
},
{
id: 2,
text: "No",
nextQuestionId: 0
},
]
},
{
id: 7,
text: 'Question 7',
answers: [
{
id: 1,
text: "Yes",
nextQuestionId: 0
},
{
id: 2,
text: "No",
nextQuestionId: 0
},
]
}
]
};
export default quiz;
How can I let my To-do dynamic, with the possibility to add items trought a form?
This is is my list:
const initialData = {
tasks: {
'task-1': { id: 'task-1', content: 'Org.Internacionais' },
'task-2': { id: 'task-2', content: 'Ind.Farm.LTDA' },
'task-3': { id: 'task-3', content: 'Musc.Sound Live Cmp' },
},
columns: {
'column-1': {
id: 'column-1',
title: 'Cliente em Potencial',
taskIds: ['task-1', 'task-2', 'task-3'],
},
'column-2': {
id: 'column-2',
title: 'Dados Confirmados',
taskIds: [],
},
'column-3': {
id: 'column-3',
title: 'Reunião Agendada',
taskIds: [],
},
},
columnOrder: ['column-1', 'column-2', 'column-3'],
};
This list got fixed tasks, and I wanna let dynamic, adding tasks through a form, but i don't know how to do this!
Code:
import React from 'react';
import ReactDOM from 'react-dom';
import styled from 'styled-components';
import { DragDropContext } from 'react-beautiful-dnd';
import '#atlaskit/css-reset';
import './styles.css';
import Column from './column';
const initialData = {
tasks: {
'task-1': { id: 'task-1', content: 'Org.Internacionais' },
'task-2': { id: 'task-2', content: 'Ind.Farm.LTDA' },
'task-3': { id: 'task-3', content: 'Musc.Sound Live Cmp' },
},
columns: {
'column-1': {
id: 'column-1',
title: 'Cliente em Potencial',
taskIds: ['task-1', 'task-2', 'task-3'],
},
'column-2': {
id: 'column-2',
title: 'Dados Confirmados',
taskIds: [],
},
'column-3': {
id: 'column-3',
title: 'Reunião Agendada',
taskIds: [],
},
},
columnOrder: ['column-1', 'column-2', 'column-3'],
};
const Container = styled.div`
display: flex;
`;
class LeadsComponent extends React.Component {
state = initialData;
onDragEnd = result => {
const { destination, source, draggableId } = result;
if (!destination) {
return;
}
if (
destination.droppableId === source.droppableId &&
destination.index === source.index
) {
return;
}
const start = this.state.columns[source.droppableId];
const finish = this.state.columns[destination.droppableId];
if (start === finish) {
const newTaskIds = Array.from(start.taskIds);
newTaskIds.splice(source.index, 1);
newTaskIds.splice(destination.index, 0, draggableId);
const newColumn = {
...start,
taskIds: newTaskIds,
};
const newState = {
...this.state,
columns: {
...this.state.columns,
[newColumn.id]: newColumn,
},
};
this.setState(newState);
return;
}
const startTaskIds = Array.from(start.taskIds);
startTaskIds.splice(source.index, 1);
const newStart = {
...start,
taskIds: startTaskIds,
};
const finishTaskIds = Array.from(finish.taskIds);
finishTaskIds.splice(destination.index, 0, draggableId);
const newFinish = {
...finish,
taskIds: finishTaskIds,
};
const newState = {
...this.state,
columns: {
...this.state.columns,
[newStart.id]: newStart,
[newFinish.id]: newFinish,
},
};
if (newStart.id == 'column-1' && newFinish.id == 'column-3') {
return;
} else if (newStart.id == 'column-2' && newFinish.id == 'column-1') {
return;
} else if (newStart.id == 'column-3' && newFinish.id == 'column-2') {
return;
} else if (newStart.id == 'column-3' && newFinish.id == 'column-1') {
return;
} else {
this.setState(newState);
}
};
render() {
return (
<>
<DragDropContext onDragEnd={this.onDragEnd}>
<Container>
{this.state.columnOrder.map(columnId => {
const column = this.state.columns[columnId];
const tasks = column.taskIds.map(
taskId => this.state.tasks[taskId],
);
return <Column key={column.id} column={column} tasks={tasks} />;
})}
</Container>
</DragDropContext>
<button
type="button"
class="btn btn-primary"
data-toggle="modal"
data-target="#staticBackdrop"
>
Launch static backdrop modal
</button>
<div
class="modal fade"
id="staticBackdrop"
data-backdrop="static"
data-keyboard="false"
tabindex="-1"
aria-labelledby="staticBackdropLabel"
aria-hidden="true"
>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">
Modal title
</h5>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form>
<input type="text" id="content" className="teste" />
<input type="submit" value="Submit" />
</form>
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-secondary"
data-dismiss="modal"
>
Close
</button>
<button type="button" class="btn btn-primary">
Understood
</button>
</div>
</div>
</div>
</div>
</>
);
}
}
export default LeadsComponent;
On this code already has a form with one input and submit! Can you help me to solve this problem?
First, your state needs a counter field to create an index for new items and a text field to store the text for the new item.
const initialData = {
count: 3,
newTask: '',
tasks: {
'task-1': { id: 'task-1', content: 'Org.Internacionais' },
'task-2': { id: 'task-2', content: 'Ind.Farm.LTDA' },
'task-3': { id: 'task-3', content: 'Musc.Sound Live Cmp' },
},
Then define the handlers as described in the question I commented
inputChangeHandler = ({ target: { value } }) =>
this.setState({
newTask: value,
});
submitHandler = e => {
e.preventDefault();
this.setState(prevState => {
// increment task count
const newCount = prevState.count + 1;
// create new id based on task count
const newId = `task-${newCount}`;
return {
count: newCount,
// clear input
newTask: '',
tasks: {
// add to tasks array
...prevState.tasks,
[newId]: { id: newId, content: prevState.newTask },
},
// add task id at the end of first column
columns: {
...prevState.columns,
'column-1': {
...prevState.columns['column-1'],
taskIds: [...prevState.columns['column-1'].taskIds, newId],
},
},
};
});
};
And pass the handler to the form components
<form onSubmit={this.submitHandler}>
<input
type="text"
id="content"
className="teste"
value={this.state.newTask}
onChange={this.inputChangeHandler}
/>
<input type="submit" value="Submit" />
</form>;
You main concern would be the way you update your state since it's gotten complex
I have created some quizzes. Each box as a total of 5 questions and a complete button. I want to have a progress bar which increases when each section is complete (1-5). Also, I want to have each section greyed out so you can unlock onto the next section. This is access when a using is logged in, I have using PHP and MySQL. I wondered if anyone could suggest a way of achieving this.
HTML
<div class="grid-container">
<div class="grid-item item1">1. Discover HTML Basics and Tags</div>
<div class="grid-item item2">2. Styling HTML with Tags</div>
<div>3. Creating Links and Images</div>
<div class="grid-item item4">4. Building Features</div>
<div class="grid-item item5">5. Building Lists</div>
</div>
JS
$(document).ready(function() {
const startButton = document.getElementById('start-btn')
const nextButton = document.getElementById('next-btn')
const completeButton = document.getElementById('complete-btn')
const questionContainerElement = document.getElementById('question-container')
const questionElement = document.getElementById('question')
const answerButtonsElement = document.getElementById('answer-buttons')
let shuffledQuestions, currentQuestionIndex
startButton.addEventListener('click', startGame)
nextButton.addEventListener('click', () => {
currentQuestionIndex++
setNextQuestion()
})
function startGame() {
startButton.classList.add('hide')
shuffledQuestions = questions.sort(() => Math.random() - .5)
currentQuestionIndex = 0
questionContainerElement.classList.remove('hide')
setNextQuestion()
}
function setNextQuestion() {
resetState()
showQuestion(shuffledQuestions[currentQuestionIndex])
}
function showQuestion(question) {
questionElement.innerText = question.question
question.answers.forEach(answer => {
const button = document.createElement('button')
button.innerText = answer.text
button.classList.add('btn')
if (answer.correct) {
button.dataset.correct = answer.correct
}
button.addEventListener('click', selectAnswer)
answerButtonsElement.appendChild(button)
})
}
function resetState() {
clearStatusClass(document.body)
nextButton.classList.add('hide')
while (answerButtonsElement.firstChild) {
answerButtonsElement.removeChild(answerButtonsElement.firstChild)
}
}
function selectAnswer(e) {
const selectedButton = e.target
const correct = selectedButton.dataset.correct
setStatusClass(document.body, correct)
Array.from(answerButtonsElement.children).forEach(button => {
setStatusClass(button, button.dataset.correct)
})
if (shuffledQuestions.length > currentQuestionIndex + 1) {
nextButton.classList.remove('hide')
} else {
completeButton.innerText = 'Complete'
completeButton.classList.remove('hide')
}
}
function setStatusClass(element, correct) {
clearStatusClass(element)
if (correct) {
element.classList.add('correct')
} else {
element.classList.add('wrong')
}
}
function clearStatusClass(element) {
element.classList.remove('correct')
element.classList.remove('wrong')
}
function completeprogress {
if ()
}
const questions = [
{
question: 'What does HTML stand for?',
answers: [
{ text: 'Hyperlinks and Text Markup Language', correct: true },
{ text: 'Hyper Text Markup Language', correct: false },
{ text: 'Home Tool Markup Language', correct: false }
]
},
{
question: 'Which character is used to indicate an end tag?',
answers: [
{ text: '<', correct: false },
{ text: '*', correct: false },
{ text: '/', correct: true },
{ text: ';', correct: false }
]
},
{
question: 'Who is making the Web standards?',
answers: [
{ text: 'Google', correct: false },
{ text: 'Mozilla', correct: false },
{ text: 'Microsoft', correct: false },
{ text: 'The World Wide Web Consortium', correct: true }
]
},
{
question: 'What is the correct HTML for making a text input field?',
answers: [
{ text: '<input type="textfield">', correct: false },
{ text: '<input type="text">', correct: true },
{ text: '<textfield>', correct: false },
{ text: '<textinput type="text">', correct: false }
]
},
{
question: 'Choose the correct HTML element for the largest heading:',
answers: [
{ text: '<head>', correct: false },
{ text: '<h6>', correct: false },
{ text: '<heading>', correct: false },
{ text: '<h1>', correct: true }
]
}
]
});
The following logic will work. Just got to work out if you want to do it client side or in php.
<?php
// once a quiz is complete, add a status to the session. This could also be stored client side
session_start();
if(!isset($_SESSION['NumberComplete'])){
$_SESSION['NumberComplete'] = 0;
}
// run this when the quiz is completed to increment the counter
$_SESSION['NumberComplete'] = (isset($_SESSION['NumberComplete']) ? $_SESSION['NumberComplete']++ : 1);
// read out the data on page load into a js var
echo '<script>var numberComplete='.$_SESSION['NumberComplete'].';</script>';
?>
<div id="NumberComplete" style="width:100%;height:50px">
<div id="percent" style="background-color: green;width:10%;height: 50px;"></div>
</div>
<script src="https://code.jquery.com/jquery-3.5.0.min.js"></script>
<script>
var totalQuizes = 5;
// run this either on page load, or dynamically when you have a new percentage to update
$(function(){
$("#NumberComplete #percent").css( {"width": (numberComplete / totalQuizes) * 100 + "%" } );
});
</script>
In my vue-app I have an array of job-postings, which have different states, such as "active", "rejected", "draft", "not_active" etc. Now I have a TabMenu: All Jobs, Drafts and To Be Approved. Each of those MenuTabs, have their own Dropdown menu, where you are supposed to filter the jobpostings. I've realized that this feature is more complex than expected, or maybe I have spend too much time with the issues, but for some reason, I cannot manage, to show "all" for the individual MenuTab. For example, when I click on the "To Be Approved" MenuTab, I want to see all the jobpostings, with the status "Not approved" and "Rejected" (See data below in the code).
So my question is, how to solve this properly? Does the job-posting data object need to have a category too?
Any help is most welcome!
So, here is my component:
<template>
<div>
<div class="tabs">
<ul>
<li v-for="(tab, index) in menuTabs” :key="tab.id" :class="{ 'active': activeTab === index }"
#click="toggleList(tab, index)” >
<span>{{tab.label}}</span>
</li>
</ul>
</div>
<div class="dropdown has-prepend col-8" :class="{ active: isOpen }">
<div :class="{ active: isOpen }" class="dropdown-select" #click="toggle">
{{ selectedOption }}
<i class="icon-chevron_down" />
</div>
<div class="dropdown-options" v-show="isOpen">
<div class="option" v-for="tab in dropDownTabs" #click="select(tab)" :key="tab.id">
<span>{{ tab.status }}</span>
</div>
</div>
</div>
<div class="block">
<DataTable :data="filteredData" :columns="tableColumns" :filter="search" />
</div>
</div>
</template>
import DataTable from '../../snippets/DataTable';
export default {
components: { DataTable },
data() {
return {
isOpen: false,
search: "",
tableData: [
{
id: 1,
title: "Salesperson",
publish_date: "2019-07-10",
status: "active",
applicants: 23,
expiration_date: "2020-02-21"
},
{
id: 2,
title: "Developer",
publish_date: "2019-11-12",
status: "not_active",
applicants: 111,
expiration_date: "2020-02-21"
},
{
id: 3,
title: "Freelanceer",
publish_date: "2019-06-10",
status: "need_approval",
applicants: 222,
expiration_date: "2020-01-10"
},
{
id: 4,
title: "Construction worker",
publish_date: "2019-12-06",
status: "active",
applicants: 76,
expiration_date: "2020-03-15"
},
{
id: 5,
title: "IT support”
publish_date: "2019-11-20",
status: "draft",
applicants: 103,
expiration_date: "2020-04-31"
},
],
menuTabs: [
{
label: "All jobs",
options: [
{
status: "all",
},
{
status: "active",
},
{
status: "not_active"
}
]
},
{
label: "Drafts",
options: [
{
status: "all"
},
{
status: "draft"
}
]
},
{
label: "To Be Approved",
options: [
{
status: "all",
},
{
status: "need_approval",
},
{
status: "rejected"
}
]
},
],
dropDownTabs: [],
selectedOption: "",
selectedTabCategory: "",
category: "",
activeTab: "",
tableColumns: [
"id",
"title",
"publish_date",
"status",
"applicants",
"expiration_date"
]
}
},
computed: {
filteredData() {
let status = this.selectedOption;
let category = this.category;
let filtered = this.tableData.filter(data => {
if (status == "all") {
return data;
}
return data.status === status;
});
return filtered;
}
},
methods: {
toggleList(tab, index) {
this.category = tab.options[0].category;
this.selectedTabCategory = tab;
let currentTabOptions = this.selectedTabCategory.options;
this.clearDropDown();
this.selectedOption = currentTabOptions[0].status;
currentTabOptions.forEach(option => {
this.dropDownTabs.push(option);
});
this.activeTab = index;
},
toggle() {
this.isOpen = !this.isOpen;
},
select(tab) {
this.selectedOption = tab.status;
let category = tab.category;
let filtered = this.tableData.filter(data => {
return data.status === this.selectedOption;
});
this.isOpen = false;
return filtered;
},
clearDropDown() {
this.dropDownTabs = [];
}
},
created() {},
mounted() {
this.selectedOption = this.menuTabs[0].options[0].status;
this.selectedTabCategory = this.menuTabs[0].label;
this.category = this.menuTabs[0].options[0].category;
let defaultOptions = this.menuTabs[0].options;
defaultOptions.forEach(option => {
this.dropDownTabs.push(option);
});
this.activeTab = 0;
}
};
I am not sure if it will help you at all. However I will try anyway.
You should store the selected tab when you click on it. Then filter the this.tableData based on the selected tab options. Also you will need map the tab option options to array of strings, so you can check if the posting status is in there.
methods: {
toggleList (tab, index) {
this.selectedTabObject = tab
// rest of your code...
}
},
computed: {
filteredData () {
return this.tableData.filter(data => {
const states = this.selectedTabObject.options.map(opt => opt.status)
return states.includes(data.status)
})
}
}
Also I have created simple fiddle to mimic your problem.
https://jsfiddle.net/3hqnp7u2/7/