Esercizio finale: Quiz interattivo

Niccolò Maltoni

Struttura del materiale

L’obiettivo dell’esercizio è realizzare un quiz interattivo.

Nella cartella starter troviamo il materiale di partenza, con la pagina HTML, il CSS e un file JavaScript con alcune funzioni vuote.

Nel file script.js è già presente un array di domande con questa struttura:

{
    category: 'JavaScript',
    question: 'Quale metodo aggiunge un elemento in fondo a un array?',
    correct_answer: 'push',
    incorrect_answers: ['pop', 'slice', 'shift']
}

Il lavoro è suddiviso in 4 esercizi base, ognuno con i propri requisiti.

Alla fine troviamo anche un esercizio avanzato opzionale E5 con caricamento domande da API.

Cosa dobbiamo implementare

In script.js troviamo 5 funzioni vuote e nessun listener.

Dobbiamo scrivere noi tutto il codice, usando i riferimenti già presenti:

Funzione Esercizio
startQuiz() E1
renderQuestion() E1
handleAnswerClick(event) E2
goToNextQuestion() E3
finishQuiz() E4

Dobbiamo anche aggiungere 3 addEventListener:

  • startButtonstartQuiz
  • answersContainerhandleAnswerClick
  • nextButtongoToNextQuestion

E1 - Avvio quiz e render prima domanda

Obiettivo

Osserva la struttura HTML fornita.

Al click su Avvia quiz, dobbiamo mostrare una card con la prima domanda e le 4 risposte (1 corretta e 3 errate).

Ogni risposta deve essere un bottone cliccabile.

Il progresso deve mostrare 1 / 5 (domanda 1 su 5).

Suggerimento

startQuiz deve preparare l’interfaccia prima di chiamare renderQuestion: pensa a cosa va azzerato nello stato, cosa va nascosto e cosa va disabilitato.

renderQuestion deve costruire i bottoni dinamicamente: ogni bottone ha un testo visibile e un valore da confrontare in seguito. Dove conviene salvare quel valore?

E2 - Risposta, feedback e punteggio

Obiettivo

Al click su una risposta dobbiamo:

  • capire se la risposta è corretta
  • aggiornare il punteggio
  • mostrare feedback corretto o errato
  • considerare definitiva la scelta per quella domanda disabilitando i bottoni delle risposte

Suggerimento

Il listener è sull’answersContainer, non sui singoli bottoni: come risaliamo al bottone che ha ricevuto il click?

Come evitiamo che l’utente risponda una seconda volta alla stessa domanda?

Oltre al bottone cliccato, anche gli altri bottoni vanno aggiornati: come selezionarli tutti?

E3 - Navigazione tra domande

Obiettivo

Con il pulsante Prossima domanda dobbiamo avanzare nel quiz.

Durante il flusso dobbiamo anche mantenere uno stato UI coerente:

  • nuova domanda renderizzata correttamente
  • feedback precedente pulito
  • pulsante Prossima domanda disabilitato finché non rispondiamo

Suggerimento

Quando l’utente clicca “Prossima domanda” ci sono due scenari possibili. Come li distinguiamo? Cosa va reimpostato prima di mostrare la nuova domanda?

E4 - Fine quiz

Obiettivo

Quando arriviamo all’ultima domanda, dobbiamo terminare il quiz.

Terminare il quiz significa:

  • nascondere la card con domanda e risposte
  • mostrare il pannello finale
  • visualizzare il punteggio finale x / 5

Suggerimento

questionCard e resultPanel esistono già nella pagina: uno va nascosto, l’altro mostrato. Come costruiamo la stringa del punteggio finale con i valori che abbiamo?

E5 - Avanzato: domande da Open Trivia DB

Obiettivo

In questa estensione avanzata sostituiamo il dataset locale con un caricamento remoto da Open Trivia DB.

Requisiti:

  • usare fetch verso https://opentdb.com/api.php?amount=5&type=multiple
  • normalizzare i risultati nel formato già usato dal quiz (question, correct_answer, incorrect_answers)
  • in caso di errore API/rete: mostrare un messaggio e non avviare il quiz

Suggerimento

Il risultato di Open Trivia DB contiene stringhe con HTML entities (es. " per "). Per visualizzarle correttamente, dobbiamo decodificarle prima di assegnarle a textContent.

Possiamo usare questo trucchetto:

function decodeHtmlEntities(text) {
    const textarea = document.createElement('textarea');
    textarea.innerHTML = text;
    return textarea.value;
}

costruiamo una textarea in memoria, assegniamo il testo con entities a innerHTML e poi leggiamo il risultato decodificato da value, senza mai aggiungerlo al DOM.