Volver al curso

JavaScript Desde Cero: Tu Primer Lenguaje de Programación

leccion
5 / 22
beginner
8 horas
Fundamentos de JavaScript

Tu Primer Script Completo en JavaScript

Lectura
40 min~11 min lectura

Tu Primer Script Completo en JavaScript

Objetivos de aprendizaje

Al finalizar esta lección serás capaz de:

  • Crear un archivo HTML que cargue y ejecute JavaScript correctamente
  • Entender la diferencia entre JavaScript inline, interno y externo
  • Construir un programa interactivo que combine todos los conceptos del módulo
  • Estructurar tu código de forma legible y mantenible
  • Desplegar tu primer mini-proyecto web funcional

1. Conectando JavaScript con HTML

Hasta ahora escribimos JavaScript en la consola del navegador o en archivos .js ejecutados con Node.js. Pero el uso más común de JavaScript es en páginas web, conectándolo con HTML.

Las 3 formas de incluir JavaScript en HTML

Forma 1: JavaScript inline (NO recomendado)

<!-- Directamente en atributos de HTML -->
<button onclick="alert('¡Hola!');">Hacé click</button>
<a href="javascript:void(0)" onclick="console.log('click');">Link</a>

¿Por qué NO se recomienda? Mezcla la estructura (HTML) con el comportamiento (JavaScript), haciendo el código difícil de mantener. Además, tiene limitaciones de seguridad.

Forma 2: JavaScript interno (script tag en HTML)

<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="UTF-8">
  <title>Mi Página</title>
</head>
<body>
  <h1 id="titulo">Hola Mundo</h1>
  
  <script>
    // JavaScript directamente en el HTML
    const titulo = document.getElementById("titulo");
    titulo.style.color = "blue";
    console.log("JavaScript interno ejecutado");
  </script>
</body>
</html>

Aceptable para scripts pequeños y demos rápidas, pero para proyectos reales preferimos archivos externos.

Forma 3: JavaScript externo (RECOMENDADO)

<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="UTF-8">
  <title>Mi Página</title>
</head>
<body>
  <h1 id="titulo">Hola Mundo</h1>
  <p id="parrafo">Este texto va a cambiar.</p>
  <button id="boton">Hacé click</button>
  
  <!-- Script externo al final del body -->
  <script src="app.js"></script>
</body>
</html>
// app.js (archivo separado)
const titulo = document.getElementById("titulo");
const parrafo = document.getElementById("parrafo");
const boton = document.getElementById("boton");

titulo.style.color = "#6366f1";

boton.addEventListener("click", () => {
  parrafo.textContent = "¡El texto cambió!";
});

¿Por qué al final del <body>?

Cuando el navegador lee un HTML, lo procesa de arriba a abajo. Si ponés el <script> en el <head>, el JavaScript se ejecuta antes de que existan los elementos HTML, y tu código no encontrará los elementos que necesita.

<!-- ❌ PROBLEMA: el script se ejecuta antes de que exista #titulo -->
<head>
  <script>
    document.getElementById("titulo").textContent = "Nuevo título";
    // TypeError: Cannot set properties of null
  </script>
</head>
<body>
  <h1 id="titulo">Hola</h1>
</body>

Soluciones:

<!-- ✅ Solución 1: Script al final del body -->
<body>
  <h1 id="titulo">Hola</h1>
  <script src="app.js"></script>
</body>

<!-- ✅ Solución 2: Atributo defer (carga en paralelo, ejecuta después) -->
<head>
  <script src="app.js" defer></script>
</head>
<body>
  <h1 id="titulo">Hola</h1>
</body>

<!-- ✅ Solución 3: DOMContentLoaded event -->
<head>
  <script>
    document.addEventListener("DOMContentLoaded", () => {
      document.getElementById("titulo").textContent = "Nuevo título";
    });
  </script>
</head>

La opción más moderna y recomendada es usar defer o poner los scripts al final del body.


2. Interactuando con la página: document object model (DOM) básico

El DOM (Document Object Model) es la representación de tu HTML como un árbol de objetos que JavaScript puede manipular. Cada etiqueta HTML se convierte en un "nodo" del DOM.

Seleccionando elementos

// Por ID (devuelve 1 elemento)
const titulo = document.getElementById("titulo");

// Por clase (devuelve una colección)
const items = document.getElementsByClassName("item");

// Por selector CSS (devuelve el primero que encuentre)
const primer = document.querySelector(".item");
const boton = document.querySelector("#boton-enviar");
const link = document.querySelector("a[href='#contacto']");

// Todos los que coincidan con el selector
const todosLosItems = document.querySelectorAll(".item");
// Devuelve un NodeList (similar a un array)

todosLosItems.forEach(item => {
  console.log(item.textContent);
});

Modificando elementos

const elemento = document.querySelector("#mi-elemento");

// Cambiar texto
elemento.textContent = "Nuevo texto";
elemento.innerHTML = "<strong>Texto en negrita</strong>";

// Cambiar estilos
elemento.style.color = "red";
elemento.style.backgroundColor = "#f0f0f0";
elemento.style.padding = "10px";
elemento.style.borderRadius = "8px";

// Agregar/quitar clases CSS
elemento.classList.add("activo");
elemento.classList.remove("oculto");
elemento.classList.toggle("seleccionado"); // Agrega si no tiene, quita si tiene

// Cambiar atributos
elemento.setAttribute("data-id", "123");
const valor = elemento.getAttribute("data-id"); // "123"

Creando elementos nuevos

// Crear un nuevo párrafo
const nuevoParrafo = document.createElement("p");
nuevoParrafo.textContent = "Este párrafo fue creado con JavaScript";
nuevoParrafo.classList.add("destacado");

// Agregarlo al documento
const contenedor = document.querySelector("#contenedor");
contenedor.appendChild(nuevoParrafo);

// Crear una lista completa
const lista = document.createElement("ul");
const frutas = ["Manzana", "Banana", "Cereza"];

frutas.forEach(fruta => {
  const li = document.createElement("li");
  li.textContent = fruta;
  lista.appendChild(li);
});

contenedor.appendChild(lista);

Escuchando eventos

const boton = document.querySelector("#mi-boton");
const input = document.querySelector("#mi-input");

// Click
boton.addEventListener("click", () => {
  console.log("¡Botón clickeado!");
});

// Input (cada vez que se escribe)
input.addEventListener("input", (evento) => {
  console.log("Escribiendo:", evento.target.value);
});

// Submit de formulario
const formulario = document.querySelector("#mi-form");
formulario.addEventListener("submit", (evento) => {
  evento.preventDefault(); // Prevenir recarga de página
  console.log("Formulario enviado");
});

// Teclado
document.addEventListener("keydown", (evento) => {
  console.log("Tecla presionada:", evento.key);
  if (evento.key === "Escape") {
    console.log("¡Escape presionado!");
  }
});

3. Proyecto completo: Generador de contraseñas

Vamos a crear un proyecto completo que combina todo lo que aprendimos en este módulo: variables, operadores, funciones, DOM y eventos.

El HTML (index.html)

<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Generador de Contraseñas</title>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body {
      font-family: 'Segoe UI', sans-serif;
      background: #1a1a2e;
      color: #e0e0e0;
      display: flex;
      justify-content: center;
      align-items: center;
      min-height: 100vh;
    }
    .container {
      background: #16213e;
      padding: 2rem;
      border-radius: 16px;
      width: 400px;
      box-shadow: 0 20px 60px rgba(0,0,0,0.3);
    }
    h1 { text-align: center; margin-bottom: 1.5rem; color: #6366f1; }
    .password-display {
      background: #0f3460;
      padding: 1rem;
      border-radius: 8px;
      font-family: 'Courier New', monospace;
      font-size: 1.3rem;
      text-align: center;
      letter-spacing: 2px;
      margin-bottom: 1rem;
      word-break: break-all;
      min-height: 50px;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    .option { display: flex; justify-content: space-between; align-items: center; padding: 0.5rem 0; }
    .option label { cursor: pointer; }
    input[type="range"] { width: 60%; accent-color: #6366f1; }
    input[type="checkbox"] { accent-color: #6366f1; width: 20px; height: 20px; }
    .btn {
      width: 100%;
      padding: 0.8rem;
      border: none;
      border-radius: 8px;
      font-size: 1rem;
      cursor: pointer;
      margin-top: 1rem;
      font-weight: bold;
      transition: transform 0.1s;
    }
    .btn:active { transform: scale(0.98); }
    .btn-generate { background: #6366f1; color: white; }
    .btn-copy { background: #10b981; color: white; }
    .strength { text-align: center; margin-top: 0.5rem; font-size: 0.9rem; }
    .strength.weak { color: #ef4444; }
    .strength.medium { color: #f59e0b; }
    .strength.strong { color: #10b981; }
  </style>
</head>
<body>
  <div class="container">
    <h1>Generador de Contraseñas</h1>
    
    <div class="password-display" id="passwordDisplay">
      Hacé click en Generar
    </div>
    
    <div class="option">
      <label for="length">Longitud: <span id="lengthValue">12</span></label>
      <input type="range" id="length" min="4" max="32" value="12">
    </div>
    
    <div class="option">
      <label for="uppercase">Mayúsculas (A-Z)</label>
      <input type="checkbox" id="uppercase" checked>
    </div>
    
    <div class="option">
      <label for="lowercase">Minúsculas (a-z)</label>
      <input type="checkbox" id="lowercase" checked>
    </div>
    
    <div class="option">
      <label for="numbers">Números (0-9)</label>
      <input type="checkbox" id="numbers" checked>
    </div>
    
    <div class="option">
      <label for="symbols">Símbolos (!@#$...)</label>
      <input type="checkbox" id="symbols">
    </div>
    
    <button class="btn btn-generate" id="generateBtn">Generar Contraseña</button>
    <button class="btn btn-copy" id="copyBtn">Copiar al Portapapeles</button>
    
    <div class="strength" id="strength"></div>
  </div>
  
  <script src="app.js"></script>
</body>
</html>

El JavaScript (app.js)

// ============================================
// GENERADOR DE CONTRASEÑAS
// Proyecto del Módulo 1: Fundamentos de JS
// ============================================

// 1. SELECCIONAR ELEMENTOS DEL DOM
const passwordDisplay = document.getElementById("passwordDisplay");
const lengthSlider = document.getElementById("length");
const lengthValue = document.getElementById("lengthValue");
const uppercaseCheck = document.getElementById("uppercase");
const lowercaseCheck = document.getElementById("lowercase");
const numbersCheck = document.getElementById("numbers");
const symbolsCheck = document.getElementById("symbols");
const generateBtn = document.getElementById("generateBtn");
const copyBtn = document.getElementById("copyBtn");
const strengthDisplay = document.getElementById("strength");

// 2. DEFINIR LOS CONJUNTOS DE CARACTERES
const CHARS_UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const CHARS_LOWER = "abcdefghijklmnopqrstuvwxyz";
const CHARS_NUMBERS = "0123456789";
const CHARS_SYMBOLS = "!@#$%^&*()_+-=[]{}|;:,.<>?";

// 3. FUNCIÓN PRINCIPAL: GENERAR CONTRASEÑA
function generarPassword() {
  const longitud = parseInt(lengthSlider.value);
  let caracteres = "";
  
  // Construir el conjunto de caracteres según opciones seleccionadas
  if (uppercaseCheck.checked) caracteres += CHARS_UPPER;
  if (lowercaseCheck.checked) caracteres += CHARS_LOWER;
  if (numbersCheck.checked) caracteres += CHARS_NUMBERS;
  if (symbolsCheck.checked) caracteres += CHARS_SYMBOLS;
  
  // Validar que al menos una opción esté seleccionada
  if (caracteres === "") {
    passwordDisplay.textContent = "Seleccioná al menos una opción";
    strengthDisplay.textContent = "";
    return;
  }
  
  // Generar la contraseña caracter por caracter
  let password = "";
  for (let i = 0; i < longitud; i++) {
    const indiceAleatorio = Math.floor(Math.random() * caracteres.length);
    password += caracteres[indiceAleatorio];
  }
  
  // Mostrar la contraseña
  passwordDisplay.textContent = password;
  
  // Evaluar y mostrar la fortaleza
  evaluarFortaleza(password);
  
  console.log(`Contraseña generada: ${longitud} caracteres, ${caracteres.length} posibles`);
}

// 4. FUNCIÓN: EVALUAR FORTALEZA
function evaluarFortaleza(password) {
  let puntaje = 0;
  
  // Longitud
  if (password.length >= 8) puntaje++;
  if (password.length >= 12) puntaje++;
  if (password.length >= 16) puntaje++;
  
  // Variedad de caracteres
  if (/[A-Z]/.test(password)) puntaje++;
  if (/[a-z]/.test(password)) puntaje++;
  if (/[0-9]/.test(password)) puntaje++;
  if (/[^A-Za-z0-9]/.test(password)) puntaje++;
  
  // Mostrar resultado
  if (puntaje <= 3) {
    strengthDisplay.textContent = "Fortaleza: Débil";
    strengthDisplay.className = "strength weak";
  } else if (puntaje <= 5) {
    strengthDisplay.textContent = "Fortaleza: Media";
    strengthDisplay.className = "strength medium";
  } else {
    strengthDisplay.textContent = "Fortaleza: Fuerte";
    strengthDisplay.className = "strength strong";
  }
}

// 5. FUNCIÓN: COPIAR AL PORTAPAPELES
async function copiarPassword() {
  const password = passwordDisplay.textContent;
  
  if (password === "Hacé click en Generar" || password === "Seleccioná al menos una opción") {
    return;
  }
  
  try {
    await navigator.clipboard.writeText(password);
    copyBtn.textContent = "¡Copiado!";
    setTimeout(() => {
      copyBtn.textContent = "Copiar al Portapapeles";
    }, 2000);
  } catch (err) {
    console.error("Error al copiar:", err);
  }
}

// 6. CONECTAR EVENTOS
lengthSlider.addEventListener("input", () => {
  lengthValue.textContent = lengthSlider.value;
});

generateBtn.addEventListener("click", generarPassword);
copyBtn.addEventListener("click", copiarPassword);

// Generar una contraseña inicial
generarPassword();

console.log("Generador de contraseñas cargado correctamente");

Este proyecto demuestra:

  • Variables y constantes (const, let)
  • Tipos de datos (strings, numbers, booleans)
  • Operadores (aritméticos, comparación, lógicos, concatenación)
  • console.log() para debugging
  • Funciones (declaración, parámetros, retorno)
  • DOM manipulation (seleccionar, modificar, crear elementos)
  • Eventos (click, input)
  • Condicionales (if/else)
  • Loops (for)

Errores comunes de principiantes
  1. Poner el <script> en el <head> sin defer: El JavaScript se ejecuta antes de que el HTML esté listo, causando errores de null.

  2. Usar innerHTML cuando textContent alcanza: innerHTML puede ser un riesgo de seguridad (XSS). Usá textContent para texto plano.

  3. No usar preventDefault() en formularios: Sin esto, el formulario recarga la página y tu JavaScript se pierde.

  4. Olvidar que querySelectorAll devuelve un NodeList, no un Array: Podés usar forEach pero no map o filter directamente. Convertilo con Array.from().

  5. No separar HTML, CSS y JavaScript: Mantené cada lenguaje en su propio archivo para código más limpio y mantenible.


Puntos clave de esta lección
  1. Siempre usá archivos JavaScript externos (<script src="app.js">) para proyectos reales.
  2. Poné el <script> al final del <body> o usá el atributo defer en el <head>.
  3. El DOM es la representación de tu HTML que JavaScript puede manipular.
  4. querySelector() y querySelectorAll() son los métodos modernos para seleccionar elementos.
  5. addEventListener() es la forma correcta de manejar eventos (no usar atributos HTML inline).
  6. textContent para texto plano, innerHTML solo cuando necesitás insertar HTML.
  7. Un proyecto completo combina todos los fundamentos: variables, operadores, funciones, DOM y eventos.

Quiz de autoevaluación

1. ¿Cuál es la forma recomendada de incluir JavaScript en HTML?
a) JavaScript inline en atributos onclick
b) Script tag en el head sin defer
c) Archivo externo con script src al final del body o con defer
d) Escribirlo directamente en el CSS

2. ¿Qué hace document.querySelector('.item')?
a) Selecciona todos los elementos con clase 'item'
b) Selecciona el primer elemento que coincide con el selector CSS
c) Crea un nuevo elemento con clase 'item'
d) Elimina todos los elementos con clase 'item'

3. ¿Qué hace evento.preventDefault() en un formulario?
a) Previene que se ejecute JavaScript
b) Previene que la página se recargue al enviar el formulario
c) Elimina el evento
d) Previene errores de JavaScript

4. ¿Cuál es la diferencia entre textContent e innerHTML?
a) No hay diferencia
b) textContent inserta HTML, innerHTML inserta texto
c) textContent inserta texto plano, innerHTML puede insertar HTML
d) innerHTML es más seguro

5. ¿Qué atributo permite cargar un script en el head sin bloquear el HTML?
a) async
b) defer
c) lazy
d) preload

Respuestas: 1-c, 2-b, 3-b, 4-c, 5-b


💡 Concepto Clave

Revisemos los puntos más importantes de esta lección antes de continuar.

Ejercicio práctico

Misión: Conversor de unidades

Creá una página web completa (HTML + CSS + JavaScript) que sea un conversor de unidades con las siguientes características:

  1. Un campo de input numérico para ingresar el valor
  2. Un selector (<select>) para elegir la conversión: km→millas, °C→°F, kg→libras
  3. Un botón para convertir
  4. Un área donde se muestra el resultado
  5. El resultado debe mostrarse con 2 decimales
  6. Si el input está vacío o no es un número, mostrar un mensaje de error
  7. Bonus: agregar un botón para invertir la conversión (millas→km, °F→°C, libras→kg)

Fórmulas:

  • km a millas: km * 0.621371
  • °C a °F: (celsius * 9/5) + 32
  • kg a libras: kg * 2.20462

Este ejercicio combina TODO lo que aprendiste en el Módulo 1. ¡Éxitos!

🧠 Pon a prueba tu conocimiento
¿Cuál es el aspecto más importante que aprendiste en esta lección?
  • Comprendo el concepto principal y puedo explicarlo con mis palabras
  • Entiendo cómo aplicarlo en mi situación específica
  • Necesito repasar algunas partes antes de continuar
  • Quiero ver más ejemplos prácticos del tema
✅ ¡Excelente! Continúa con la siguiente lección para profundizar más.