Aprendamos a crear pruebas y cuestionarios para un sitio web usando JavaScript «vanilla».

Las pruebas y los cuestionarios no solo son útiles para evaluar el conocimiento, sino también como contenido entretenido que mantiene a los usuarios en el sitio web por más tiempo.

Puedes usar servicios externos para crearlos, ¡pero ¿a qué desarrollador eso lo ha detenido alguna vez? Con cariño, inventaremos nuestra propia rueda y crearemos nuestro propio código para integrar pruebas en las páginas.

Estructura de la página

Colocaremos la prueba en el archivo quiz.html para poder insertarlo usando un iframe en otras páginas. Vamos a estructurar la prueba:

<div class="wrapper">
   <main class="main">
       <div class="quiz__head">
           <div class="head__content" id="head">Lorem ipsum dolor sit amet consectetur adipisicing elit. Ut ducimus odit accusamus, illum quas magni provident odio praesentium commodi sint, porro harum, minus cupiditate architecto culpa aut ex dolore officia.</div>
       </div>
       <div class="quiz__body">
           <div class="buttons">
               <div class="buttons__content" id="buttons">
                   <button class="button">Botón predeterminado</button><br>
                   <button class="button button_wrong">Respuesta incorrecta</button><br>
                   <button class="button button_correct">Respuesta correcta</button><br>
                   <button class="button button_passive">Botón sin clic</button><br>
               </div>
           </div>
 
           <div class="quiz__footer">
               <div class="footer__content" id="pages">0 / 0</div>
           </div>
       </div>
      
   </main>
</div>

Ahora, agreguemos los estilos:

body, html {
   width: 100%;
   height: 100%;
   margin: 0;
   padding: 0;
   overflow: hidden;
   font-size: 16px;
   font-family: helvetica, arial;
   background: #f9f9f9;
   color: #111;
}

.wrapper {
   width: 100%;
   height: 100%;
   display: table;
}

.main {
   display: table-cell;
   vertical-align: middle;
   text-align: center;
}

.quiz-frame {
   border: 0;
   box-shadow: 0 0 10px rgba(0,0,0,0.5);
}

.quiz__head {
   font-size: 20pt;
   margin: 10px;
   margin-bottom: 50px;
}

.head__content {
   padding: 5px;
}

.quiz__body {
   margin: 10px;
}

.quiz__footer {
   position: absolute;
   bottom: 0;
   display: block;
   width: 100%;
}

.footer__content {
   padding: 5px;
}

.button {
   border: 0;
   border-radius: 10px;
   background: #6477EB;
   color: #fff;
   padding: 10px 25px;
   width: 70%;
   font-size: 15pt;
   display: block;
   margin: 2px auto;
   cursor: pointer;
}

.button_wrong {
   background: #EB6465;
}

.button_correct {
   background: #5EB97D;
}

.button_passive {
   background: #B3B3B3;
}

Al archivo index.html añadiremos un iframe para conectar la prueba:

<iframe src="quiz.html" width="480" height="720" class="quiz-frame"></iframe>

Así se ve:

Maqueta de la interfaz de usuario de un cuestionario con diferentes opciones de respuesta.
Interfaz de usuario para un quiz o test interactivo.

Arriba está la pregunta, debajo las opciones de respuesta, y al final el progreso del cuestionario.

Creamos las clases

La prueba funcionará con las siguientes clases:

  • Quiz: La prueba en sí. Contiene todos los datos, se encarga de pasar a la siguiente pregunta y de finalizar la prueba.
  • Question: La pregunta. Contiene el texto de la pregunta y las opciones de respuesta.
  • Answer: La respuesta. Contiene el texto de la respuesta y la puntuación.
  • Result: El resultado. Contiene el texto final y la puntuación necesaria para alcanzar ese resultado.

Aquí están las clases:

// Clase que representa la prueba
class Quiz {
   constructor(type, questions, results) {
       // Tipo de prueba: 1 - prueba clásica con respuestas correctas, 2 - prueba sin respuestas correctas
       this.type = type;

       // Arreglo con las preguntas
       this.questions = questions;

       // Arreglo con los posibles resultados
       this.results = results;

       // Puntuación obtenida
       this.score = 0;

       // Número del resultado en el arreglo
       this.result = 0;

       // Número de la pregunta actual
       this.current = 0;
   }

   Click(index) {
       // Sumamos puntos
       let value = this.questions[this.current].Click(index);
       this.score += value;

       let correct = -1;

       // Si se añadió al menos un punto, consideramos que la respuesta es correcta
       if(value >= 1) {
           correct = index;
       } else {
           // De lo contrario, buscamos qué respuesta podría ser correcta
           for(let i = 0; i < this.questions[this.current].answers.length; i++) {
               if(this.questions[this.current].answers[i].value >= 1) {
                   correct = i;
                   break;
               }
           }
       }

       this.Next();

       return correct;
   }

   // Pasar a la siguiente pregunta
   Next() {
       this.current++;

       if(this.current >= this.questions.length) {
           this.End();
       }
   }

   // Si se acabaron las preguntas, este método comprobará qué resultado obtuvo el usuario
   End() {
       for(let i = 0; i < this.results.length; i++) {
           if(this.results[i].Check(this.score)) {
               this.result = i;
           }
       }
   }
}

// Clase que representa una pregunta
class Question {
   constructor(text, answers) {
       this.text = text;
       this.answers = answers;
   }

   Click(index) {
       return this.answers[index].value;
   }
}

// Clase que representa una respuesta
class Answer {
   constructor(text, value) {
       this.text = text;
       this.value = value;
   }
}

// Clase que representa un resultado
class Result {
   constructor(text, value) {
       this.text = text;
       this.value = value;
   }

   // Este método comprueba si el usuario obtuvo suficientes puntos
   Check(value) {
       if(this.value <= value) {
           return true;
       } else {
           return false;
       }
   }
}

Una vez que las clases están listas, podemos crear instancias de los objetos:

// Arreglo con los resultados
const results =
[
   new Result("Te falta mucho por aprender", 0),
   new Result("Ya tienes un buen nivel", 2),
   new Result("Tu nivel está por encima de la media", 4),
   new Result("Dominas el tema a la perfección", 6)
];

// Arreglo con las preguntas
const questions =
[
   new Question("2 + 2 = ",
   [
       new Answer("2", 0),
       new Answer("3", 0),
       new Answer("4", 1),
       new Answer("0", 0)
   ])
];

// La prueba
const quiz = new Quiz(1, questions, results);

Solo se creó una pregunta para no distraer con código repetitivo. Puedes agregar tantas como necesites.

Solo queda escribir la lógica de interacción con el usuario:

Update();

// Actualizar la prueba
function Update() {
   // Comprobamos si hay más preguntas
   if(quiz.current < quiz.questions.length) {
       // Si las hay, cambiamos la pregunta en el encabezado
       headElem.innerHTML = quiz.questions[quiz.current].text;

       // Eliminamos las opciones de respuesta anteriores
       buttonsElem.innerHTML = "";

       // Creamos botones para las nuevas opciones de respuesta
       for(let i = 0; i < quiz.questions[quiz.current].answers.length; i++) {
           let btn = document.createElement("button");
           btn.className = "button";

           btn.innerHTML = quiz.questions[quiz.current].answers[i].text;

           btn.setAttribute("index", i);

           buttonsElem.appendChild(btn);
       }

       // Mostramos el número de la pregunta actual
       pagesElem.innerHTML = (quiz.current + 1) + " / " + quiz.questions.length;

       // Llamamos a la función que adjunta los eventos a los nuevos botones
       Init();
   } else {
       // Si es el final, mostramos el resultado
       buttonsElem.innerHTML = "";
       headElem.innerHTML = quiz.results[quiz.result].text;
       pagesElem.innerHTML = "Puntuación: " + quiz.score;
   }
}

function Init() {
   // Buscamos todos los botones
   let btns = document.getElementsByClassName("button");

   for(let i = 0; i < btns.length; i++) {
       // Adjuntamos el evento a cada botón individual
       // Al hacer clic en un botón, se llamará a la función Click()
       btns[i].addEventListener("click", function (e) { Click(e.target.getAttribute("index")); });
   }
}

function Click(index) {
   // Obtenemos el número de la respuesta correcta
   let correct = quiz.Click(index);

   // Buscamos todos los botones
   let btns = document.getElementsByClassName("button");

   // Ponemos los botones en gris
   for(let i = 0; i < btns.length; i++) {
       btns[i].className = "button button_passive";
   }

   // Si es una prueba con respuestas correctas, resaltamos la respuesta correcta en verde y la incorrecta en rojo
   if(quiz.type == 1) {
       if(correct >= 0) {
           btns[correct].className = "button button_correct";
       }

       if(index != correct) {
           btns[index].className = "button button_wrong";
       }
   } else {
       // De lo contrario, simplemente resaltamos la respuesta del usuario en verde
       btns[index].className = "button button_correct";
   }

   // Esperamos un segundo y actualizamos la prueba
   setTimeout(Update, 1000);
}

Así se ve:

Captura de pantalla de un quiz animado en JavaScript mostrando una pregunta de matemáticas y opciones de respuesta.
Ejemplo de quiz con animación en JS.

Cuando el usuario finaliza la prueba, verá su resultado:

Pantalla mostrando el resultado de un quiz con una puntuación de 5 y un mensaje indicando que el nivel está por encima de la media.
Resultado de un cuestionario en JavaScript.

Características de la creación de diferentes pruebas

Como puedes observar, esta es una prueba muy sencilla. Será útil para que los usuarios puedan comprobar lo bien que han asimilado el material. O simplemente para divertirse. Aquí hay algunos temas para pruebas divertidas:

  • ¿Quién eres de las «W. I. T. C. H.»?
  • Con quién de «Sobrenatural» te casarías.
  • Tu horóscopo de hoy.
  • En qué casa de Hogwarts estarías.

Todo esto puede ser muy divertido y atractivo si se tienen en cuenta las características de la audiencia. Por ejemplo, para programadores hay pruebas interesantes en el sitio web binar10s.com.

Otra cosa es si tienes una plataforma educativa y los resultados de la prueba influyen en la calificación final. En este caso, los datos de las respuestas correctas deben almacenarse en el servidor. De lo contrario, se pueden ver a través de la consola del desarrollador.

Lo mismo ocurre con los temporizadores: si das un tiempo limitado para realizar la prueba, la hora de inicio del temporizador debe almacenarse en el servidor, no en el código JS.

Conclusión

Con el código de este artículo puedes crear tantas pruebas como quieras. Lo único es que para cada una tendrás que duplicar el archivo app.js para especificar nuevas preguntas.

Esto se puede solucionar usando peticiones HTTP: las preguntas se almacenarán en el servidor y se enviarán al usuario en formato JSON. Esta es una práctica muy común en el desarrollo web que todo desarrollador debe conocer.

Categorizado en:

Javascript,