¿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 y 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:
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:
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 o form.(name).value. Esto 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:
Vamos a hacer un ejemplo para entenderlo bien. ?
1. Creamos un método setInterval y hacemos 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 i y le damos un valor de 0. Dentro de la callback function incrementamos i 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 i 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 page. Así que cogemos esta línea de código:
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