portada javascript ninja quiz

JavaScript Ninja Quiz

Última actualización:

¬ŅQu√© vamos a construir?

Vamos a construir un cuestionario para ver cu√°l es tu nivel de JavaScript, de 0 a 100 (o ninja ūüėČ). Es un proyecto inspirado en cursos como el de JavaScript from novice to ninja. Aqu√≠ puedes ver la versi√≥n final.

¬ŅY esto a qu√© viene?‚Äč‚Äč

Despu√©s de aprender sobre temas como las callback functions o el DOM, es momento de practicar a fondo. Porque s√≥lo haciendo proyectos es cuando te encuentras con problemas enfocados a la vida real de un programador. ¬°Vamos all√°! ūüí™

Pre-requisitos y consejos

ūüĎČ Debes tener unas nociones b√°sicas de c√≥mo funciona JavaScript. Pero don't worry, aqu√≠ tienes una colecci√≥n de art√≠culos que tratan temas b√°sicos sobre JavaScript.

ūüíĀ Yo utilizo vsCode como IDE y una extensi√≥n llamada live-server para ver los cambios en mi navegador sin necesidad de recargar la p√°gina.

Estructura de archivos

Vamos a trabajar con un archivo HTML vinculado a un archivo JS (JavaScript). Los llamaremos ‚Äčindex.html ‚Äčapp.js respectivamente. Para darle estilazo, vamos a trabajar con Bootstrap 4. Me encanta Bootstrap. Y eso que en su d√≠a aprend√≠ muy bien CSS3. Pero Bootstrap me permite construir apps a la velocidad del rayo. ‚ö°

Bootstrap es una librer√≠a CSS y de JS, pero nosotros s√≥lo vamos a utilizar la parte del CSS, porque la parte del JS la vamos a construir desde cero. ūüôą

Para incluir Bootstrap en nuestro proyecto, no tenemos m√°s que incluir su CDN en nuestro ‚Äčindex.html.

‚Äč‚ÄčEste es el esqueleto inicial del ‚Äčindex.html con el que arrancamos nuestro proyecto:‚Äč‚Äč

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
        integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
    <title>Ninja Quiz</title>
</head>
<body>
    <script src="app.js"></script>
</body>
</html>

Subrayado puedes ver el CDN de Bootstrap. 

Definiendo el contenido HTML

Vamos a crear todo el contenido de nuestro ‚Äčindex.html ‚Äčcon la ayuda de Bootstrap. ‚Äč‚Äč

1. Construimos una secci√≥n superior. Vamos a ir comentando el c√≥digo para que nos resulte m√°s f√°cil de reconocer. De hecho, comentar tu c√≥digo es una buena pr√°ctica. ‚úĆ

Vamos a usar también nuestras propias clases de CSS.

2. Construimos la sección que abarca todo el cuestionario. Aquí es donde incluiremos un formulario (o form).

Así nos queda el <body> tras aplicarle los pasos 1 y 2:

<!--************************* top section **********************************-->
<div class="intro py-3 bg-white text-center">
    <div class="container">
        <h2 class="text-primary display-4 my-4" id="tab-con-5">JavaScript Ninja Quiz</h2>
    </div>
</div>
<!--************************* quiz section **********************************-->
<div class="quiz py-4 bg-primary">
    <div class="container">
        <h2 class="my-5 text-white" id="tab-con-10">Answer before this page autodestructs! 3, 2, 1... ūüí£</h2>
        <form class="quiz-form text-light">
            <div class="my-5">
                <p class="lead font-weight-normal">
                    1. What's the difference between JavaScript and Java?
                </p>
                <div class="form-check my-2 text-white-50">
                    <input type="radio" name="q1" value="A" checked="">
                    <label class="form-check-label">
                        "Java" is just short for "JavaScript"
                    </label>
                </div>
                <div class="form-check my-2 text-white-50">
                    <input type="radio" name="q1" value="B">
                    <label class="form-check-label">
                        Java is a backend language whereas JavaScript is a frontend language
                    </label>
                </div>
            </div>
            <div class="my-5">
                <p class="lead font-weight-normal">
                    2. How do you solve a coding problem when you are stuck?
                </p>
                <div class="form-check my-2 text-white-50">
                    <input type="radio" name="q2" value="A" checked="">
                    <label class="form-check-label">
                        Stare at the computer during hours and start to think about punching the screen
                    </label>
                </div>
                <div class="form-check my-2 text-white-50">
                    <input type="radio" name="q2" value="B">
                    <label class="form-check-label">
                        Ask your dear friend Google
                    </label>
                </div>
            </div>
            <div class="my-5">
                <p class="lead font-weight-normal">
                    3. What does BOM mean?
                </p>
                <div class="form-check my-2 text-white-50">
                    <input type="radio" name="q3" value="A" checked="">
                    <label class="form-check-label">
                        Browser Object Model
                    </label>
                </div>
                <div class="form-check my-2 text-white-50">
                    <input type="radio" name="q3" value="B">
                    <label class="form-check-label">
                        Board of Managers
                    </label>
                </div>
            </div>
            <div class="my-5">
                <p class="lead font-weight-normal">
                    4. What is FreeCodeCamp?
                </p>
                <div class="form-check my-2 text-white-50">
                    <input type="radio" name="q4" value="A" checked="">
                    <label class="form-check-label">
                        A Scout group of the Rocky Mountains
                    </label>
                </div>
                <div class="form-check my-2 text-white-50">
                    <input type="radio" name="q4" value="B">
                    <label class="form-check-label">
                        An online platform to learn web development
                    </label>
                </div>
            </div>
            <div class="text-center">
                <input type="submit" class="btn btn-light">
            </div>
        </form>
    </div>
</div>
<script src="app.js"></script>

Reaccionando a las respuestas del usuario

Con nuestra HTML ‚Äčtemplate ‚Äčterminada, ahora vamos a capturar las respuestas que el usuario marca en el cuestionario. Por cada respuesta positiva sumaremos 25 puntos, lo que significa que si las acierta todas, obtiene un 100.

Para llevar a cabo esta tarea, nos vamos al archivo ‚Äčapp.js.

1. ‚Äč‚ÄčCreamos un ‚Äčarray ‚Äčcon las respuestas correctas. El valor de la respuesta lo encontramos en el atributo value de cada <input type="radio">. En mi caso, las respuestas correctas para cada una de las cuatro preguntas ser√≠an: B, B, A, B. Aseg√ļrate de escribirlas en may√ļsculas, porque JS es case sensitive. 

2. Creamos una referencia al formulario y le a√Īadimos un event listener para capturar cu√°ndo un usuario ha enviado el formulario.

3. Evitamos que la p√°gina se recargue al pulsar "submit", usando el m√©todo ‚ÄčpreventDefault().

4. Creamos una variable para almacenar la puntuaci√≥n del usuario y un array para almacenar las respuestas del usuario. Podemos capturar las respuestas a trav√©s del ‚Äčform.(id).value ‚Äčform.(name).valueEsto lo explicamos en detalle en la DOM Saga. Ya que en nuestro caso el atributo que tenemos en nuestro ‚Äčindex.html ‚Äčes el ‚Äčname, ‚Äč√©se es el que usamos para acceder al ‚Äč‚Äč‚Äčvalor de cada <input> del <form>.

En el caso de los ‚Äčradio buttons, ‚Äčlo que hace JS cuando le pedimos el valor de un <input> de tipo radio, es darnos el valor del ‚Äčradio button ‚Äčque est√© seleccionado. Es decir, como hemos marcado que por cada pregunta, la primera opci√≥n est√© seleccionada por defecto (usando el atributo ‚Äčchecked), eso le indica a JS que el valor de todas las preguntas es "A".

5. Comparamos las respuestas del usuario con las respuestas correctas. Para eso, barremos con el ‚ÄčforEach loop el array de respuestas del usuario y extraemos una respuesta individual y un √≠ndice.‚Äč‚Äč‚Äč‚Äč Recuerda que el √≠ndice es la posici√≥n de cada elemento en el ‚Äčarray, ‚Äčempezando por cero.

La comparaci√≥n la hacemos comprobando si cada respuesta del usuario coincide con alg√ļn elemento del ‚Äčarray ‚Äčde respuestas correctas. A los elementos del array de respuestas correctas podemos acceder a trav√©s del par√°metro ‚Äčindex.

Por cada respuesta acertada, le daremos 25 puntos. 

Hacemos un ‚Äčconsole.log ‚Äčde la puntuaci√≥n para comprobar que nuestro c√≥digo funcione.

Aqu√≠ el bloque de c√≥digo que hemos a√Īadido:‚Äč‚Äč

const correctAnswers = ["B", "B", "A", "B"];
const form = document.querySelector(".quiz-form");

form.addEventListener("submit", e => {
  e.preventDefault();
  let score = 0;
  const userAnswers = [
    form.q1.value,
    form.q2.value,
    form.q3.value,
    form.q4.value
  ];

  // check answers
  userAnswers.forEach((answer, index) => {
    if (answer === correctAnswers[index]) {
      score += 25;
    }
  });

  console.log(score);
});

Guarda los cambios, ve a tu navegador y prueba a seleccionar alguna respuesta y enviar el formulario. Ver√°s como en tu consola se va actualizando el resultado a medida que marcas diferentes opciones y le das a "enviar".

Mostrando la puntuación en la página

Ahora que ya sabemos c√≥mo mostrar la puntuaci√≥n que ha obtenido que usuario, vamos a mostrarla en la pantalla para que el usuario pueda verla. 

1. Definimos una estructura HTML en nuestro ‚Äčindex.html. ‚ÄčF√≠jate que por defecto, este bloque de c√≥digo est√° oculto. Nos encargaremos de hacerlo visible con JS.

Aqu√≠ el bloque de c√≥digo que hemos a√Īadido, a continuaci√≥n de la ‚Äčtop section:‚Äč‚Äč

    <!--************************* result section **********************************-->
    <div class="result d-none py-4 bg-light text-center">
        <div class="container lead">
            <p>You are <span class="text-primary display-4 p-3">0%</span> JavaScript ninja</p>
        </div>
    </div>

2. Vamos a nuestro ‚Äčapp.js ‚Äčy creamos una referencia tanto para el <div> que engloba el resultado final como para el <span> que contiene el resultado concreto.

3. Le eliminamos la clase d-none de Bootstrap al <div> del resultado. Esta era la clase que lo manten√≠a oculto.

4. Mostramos el resultado del usuario usando textContent.

ūüßź Los pasos 3 y 4 los hacemos dentro del scope del event listener del form.

Aqu√≠ el bloque de c√≥digo que hemos a√Īadido:

const resultContainer = document.querySelector('.result');
let finalScore = document.querySelector('.result span');

// show score on page
resultContainer.classList.remove('d-none');
finalScore.textContent = `${score}%`;

Haz el cuestionario de nuevo, env√≠alo y ¬°voil√†! Sube con el rat√≥n hacia arriba para ver tu resultado. ūü•≥

La verdad que es un poco molesto eso de tener que subir con el rat√≥n para ver tu puntuaci√≥n, no es demasiado ‚Äčuser-friendly. ‚ÄčPor suerte, esto tiene f√°cil arreglo. ūüĒ®

5. Utilizamos el m√©todo scrollTo(), proporcionado directamente desde el window object. ‚ÄčPor tanto, no hace falta que escribamos ‚Äčwindow.scrollTo()sino simplemente ‚ÄčscrollTo(). ‚ÄčEste m√©todo acepta dos par√°metros, que corresponden a coordenadas: la posici√≥n en p√≠xeles en el eje X y la posici√≥n en el eje Y. 

Como queremos posicionarnos en la parte más alta de la pantalla para que nos lleve ahí, le damos unas coordenadas de 0,0.

Aqu√≠ la l√≠nea de c√≥digo que hemos a√Īadido, justo arriba del c√≥digo que muestra la puntuaci√≥n en la pantalla:

  // show score on page
  scrollTo(0, 0);

Animaciones con el método setInterval()

Hemos conseguido mostrar la puntuaci√≥n del usuario y llevarlo al principio de la pantalla sin que tenga que subir con el rat√≥n. ¬°Chachi! ūüĎŹ Pero lo cierto es que la puntuaci√≥n se muestra de repente, de manera un poco brusca

Vamos a arreglar eso a√Īadiendo un efecto de transici√≥n, como un marcador que va de 0 a un n√ļmero X pasando por todos los n√ļmeros anteriores muy r√°pido. Esto lo vamos a conseguir usando el m√©todo proporcionado directamente por el ‚Äčwindow object, ‚Äčel ‚ÄčsetInterval.

‚Äč‚ÄčEste m√©todo acepta una ‚Äčcallback function ‚Äčy un intervalo como argumentos, en ese orden. La ‚Äčcallback function ‚Äčse repetir√° cada X mili-segundos, seg√ļn le indiquemos en el intervalo.

Sintaxis:

setInterval(() => {
  // c√≥digo aqu√≠
}, intervalo en mili-segundos);

‚Äč

Vamos a hacer un ejemplo para entenderlo bien. ūüėä

1. Creamos un m√©todo ‚ÄčsetInterval ‚Äčy hac‚Äč‚Äčemos un console.log dentro de la callback function.  Le decimos que se ejecute cada 1000 mili-segundos (un segundillo). As√≠ lo har√≠amos:

setInterval(() => {
  console.log('tomorrow is friday');
}, 1000);

Si guardas y vas a tu consola, ver√°s que la frase se imprime cada 1 segundo. ¬°Chachi! Pero tan importante es saber crear una funci√≥n setInterval como saber pararlaūüõĎ

2. Para aprender a pararla, declaramos una variable global a la que llamamos y le damos un valor de 0. Dentro de la callback function incrementamos en +1, y le pedimos que se repita cada 1 segundo (como antes).

3. Guardamos la funci√≥n setInterval en una variable llamada tomorrowsDate, por ejemplo.

4. Comprobamos si es igual a un n√ļmero, 5, por ejemplo. Cosa que ocurrir√° a la quinta vez que se dispare la ‚Äčcallback function.

5. Cuando se cumpla esa condici√≥n, paramos el ‚ÄčsetInterval ‚Äčusando el m√©todo ‚ÄčclearInternval ‚Äčsobre la variable que recoge el ‚ÄčsetInterval (tomorrowsDate).‚Äč‚Äč‚Äč‚Äč

Aquí el bloque de código que hemos editado:

let i = 0;
const tomorrowsDate = setInterval(() => {
  i++;
  console.log("tomorrow is friday");
  if (i === 5) {
    clearInterval(tomorrowsDate);
  }
}, 1000);

‚ÄčSi guardas y vas a tu consola, ver√°s que la frase "tomorrow is friday" se repite 5 veces y luego se para‚úč ¬°Genial!

Vamos a aplicar estos conocimientos para crear nuestro marcador. Comenta el código que acabamos de hacer para que no te moleste.

ūüôč Recuerda que estamos trabajando sobre lo que ocurre cuando hacemos click en el bot√≥n "submit", por tanto, toda nuestra l√≥gica de programaci√≥n sigue estando dentro del event listener aplicado al form.

6. Creamos una variable que ser√° la encargada de llevar la cuenta de cu√°ntas veces se dispara nuestra callback function. La llamamos points y le damos el valor de 0. √Čsta ser√° la variable que vaya desde 0 hasta el n√ļmero de puntos que haya conseguido el usuario, que recordemos, pueden ser 0, 25, 75 o 100.

7. Creamos un m√©todo ‚ÄčsetInterval ‚Äčy lo guardamos en una variable a la que llamamos ‚Äčtimer. ‚ÄčDentro de la ‚Äčcallback function ‚Äčvamos a necesitar mostrar el resultado en la pantalla, cosa que ya hemos hecho en el apartado de ‚Äč//show score on pageAs√≠ que cogemos esta l√≠nea de c√≥digo:

  finalScore.textContent = `${score}%`;

 y la pegamos dentro de la callback function. 

ūüĒé Aqu√≠ viene el quid de la cuesti√≥n. Ya no queremos mostrar el ‚Äčscore ‚Äčen la pantalla, porque ser√≠a un valor est√°tico. Lo que queremos mostrar es el valor de la variable points, que vamos a hacerla coincidir con la puntuaci√≥n que el usuario consiga. Despu√©s, la haremos subir de 0 a la puntuaci√≥n conseguida.

8. Para eso, hacemos una comprobaci√≥n igual que en el ejemplo de arriba. Comprobamos si la variable ‚Äčpoints ‚Äčes igual que la variable score, y si a√ļn no lo es, le sumamos +1 a la variable points. Pero si lo es (else), paramos la funci√≥n timer usando el clearInterval. ‚úč

9. Como toque final, le a√Īadimos un intervalo de tiempo al setInterval (recordemos que espera dos par√°metros). Le damos unos 10 mili-segundos para que la animaci√≥n sea muy r√°pida y din√°mica.

Aqu√≠ el bloque de c√≥digo que hemos editado/a√Īadido:

  // show score on page
  scrollTo(0, 0);
  resultContainer.classList.remove("d-none");
  
  // animate score
  let points = 0;
  
  const timer = setInterval(() => {
    finalScore.textContent = `${points}%`;
    if(points === score) {
      clearInterval(timer);
    } else {
      points++;
    }
  }, 10);

THE END!

¬°Fin del proyecto! Espero que hayas aprendido algo nuevo ūüėä.  Si te queda alguna duda, ¬°nos vemos en los comentarios!

Y si crees que este post puede serle √ļtil a alguien, ¬°comp√°rtelo!

Otros artículos que pueden interesarte

Días del 353 al 386
Objetivos versus realidad Y nuevamente, llegó otro día clave. Llegó…y pasó. El pasado 4 de marzo este Reto Computer Geek[...]
Construye Minioland: Tu primera aplicación con Angular | Parte #1
¬ŅQu√© vamos a construir?Minioland, o as√≠ he decidido llamar a esta sencilla app, totalmente responsive y de tipo Single Page[...]
Angular: Entendiendo la Directiva ngModel
Angular es un framework que nos permite, entre otras cosas, a√Īadir contenido din√°mico a nuestros archivos HTML. Una de las formas[...]
Si crees que este post puede serle √ļtil a alguien, por favor, ¬°comp√°rtelo!:

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Como toda web legal que se precie, utilizamos cookies para asegurar que damos la mejor experiencia al usuario en nuestro sitio web. Si contin√ļas utilizando este sitio asumiremos que est√°s de acuerdo. m√°s informaci√≥n

Los ajustes de cookies de esta web están configurados para "permitir cookies" y así ofrecerte la mejor experiencia de navegación posible. Si sigues utilizando esta web sin cambiar tus ajustes de cookies o haces clic en "Aceptar" estarás dando tu consentimiento a esto.

Cerrar