WhatsApp

  

Random Choice Picker

Escribe opciones y deja que el azar elija

¿De qué va?

Es un mini-proyecto donde escribes opciones separadas por comas dentro de un <textarea>. Al presionar Enter, el script hace un “shuffle” visual (resalta opciones al azar por unas décimas) y se detiene en la elegida. Sirve para encuestas rápidas, dinámicas en clase, o decidir el almuerzo sin pelear. Es uno de los proyectos del curso 50 Projects 50 Days de Traversy Media. 

Qué practicas

  • Eventos de teclado (keydown) y lectura de event.key

  • Manejo de strings: split, trim, filtros vacíos.

  • Micro-interacciones: resaltar con clases CSS y temporizadores.

Código (3 archivos)

1) index.html

<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <title>Random Choice Picker</title>
  <link rel="stylesheet" href="style.css" />
</head>
<body>
  <main class="wrap">
    <h1>Random Choice Picker</h1>
    <p class="lead">
      Escribe opciones separadas por comas (ej.: <em>tacos, pizza, sushi</em>).
      Presiona <kbd>Enter</kbd> para elegir una al azar.
    </p>
    <label for="ta" class="sr-only">Opciones separadas por comas</label>
    <textarea id="ta" rows="4" placeholder="Escribe aquí…"></textarea>
    <section id="tags" class="tags" aria-live="polite" aria-atomic="true"></section>
  </main>
  <script src="script.js"></script>
</body>
</html>


2) style.css 

* { box-sizing: border-box; }
:root {
  --bg: #0f1115; --txt: #f5f7fa; --muted: #a0a8b8;
  --chip: #151a23; --accent: #7c5cff; --accent-2: #22d3ee;
  --radius: 14px; --gap: 10px;
}
html, body { height: 100%; }
body {
  margin: 0;
  font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
  background: radial-gradient(1200px 600px at 20% -10%, #1b2030 0, #0f1115 45%, #0b0e13 100%);
  color: var(--txt);
}
.wrap {
  max-width: 860px;
  margin: 48px auto;
  padding: 0 16px 32px;
}
h1 { margin: 0 0 6px; font-size: clamp(28px, 4vw, 44px); }
.lead { margin: 0 0 16px; color: var(--muted); }
textarea {
  width: 100%;
  border: 1px solid rgba(255,255,255,.12);
  background: #0d1422;
  color: var(--txt);
  border-radius: 12px;
  padding: 14px;
  resize: vertical;
  font-size: 16px;
  outline: none;
  box-shadow: 0 8px 26px rgba(0,0,0,.35) inset;
}
.tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--gap);
  margin-top: 14px;
  min-height: 42px;
}
.tag {
  padding: 8px 12px;
  border-radius: var(--radius);
  background: linear-gradient(180deg, #1b2130, var(--chip));
  border: 1px solid rgba(255,255,255,.06);
  font-weight: 600;
  letter-spacing: .2px;
  transition: transform .12s ease, box-shadow .18s ease;
}
.tag.highlight {
  transform: translateY(-1px) scale(1.05);
  box-shadow: 0 0 0 2px #000, 0 0 0 4px var(--accent);
}
.tag.final {
  transform: translateY(0) scale(1.07);
  box-shadow: 0 0 0 2px #000, 0 0 0 4px var(--accent-2);
}
kbd {
  background: #141b2a;
  border: 1px solid rgba(255,255,255,.12);
  border-bottom-color: rgba(255,255,255,.2);
  border-radius: 6px;
  padding: 2px 6px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .95em;
}
.sr-only {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip: rect(0 0 0 0); overflow: hidden;
  white-space: nowrap;
}


3) script.js 

const ta   = document.getElementById('ta');
const tags = document.getElementById('tags');
function parseChoices(value) {
  return value
    .split(',')
    .map(t => t.trim())
    .filter(Boolean);
}
function renderTags(list) {
  tags.innerHTML = list.map(txt => `${txt}`).join('');
}
function pickRandomTag() {
  const all = [...document.querySelectorAll('.tag')];
  if (!all.length) return null;
  const i = Math.floor(Math.random() * all.length);
  return all[i];
}
function flashPick(times = 24, interval = 70) {
  return new Promise(resolve => {
    let count = 0, last;
    const timer = setInterval(() => {
      if (last) last.classList.remove('highlight');
      const t = pickRandomTag();
      if (t) {
        t.classList.add('highlight');
        last = t;
      }
      if (++count >= times) {
        clearInterval(timer);
        resolve(last || null);
      }
    }, interval);
  });
}
ta.addEventListener('input', (e) => {
  renderTags(parseChoices(e.target.value));
});
// Al presionar Enter, ejecuta el “shuffle” y resalta la elección final
window.addEventListener('keydown', async (e) => {
  if (e.key === 'Enter') {        // recomendado frente a keyCode (deprecado)
    e.preventDefault();           // evita salto de línea en textarea
    const choices = parseChoices(ta.value);
    renderTags(choices);
    if (!choices.length) return;
    // Animación de resaltado aleatorio
    document.querySelectorAll('.tag').forEach(t => t.classList.remove('final', 'highlight'));
    const finalTag = await flashPick(24, 70); // ~1.6s “shuffle”
    if (finalTag) {
      document.querySelectorAll('.tag').forEach(t => t.classList.remove('highlight'));
      finalTag.classList.add('final');
    }
  }
});


Ejemplo

Aquí hay un pequeño ejemplo de implementación


Extra:  consejos rápidos

  • Entrada limpia: elimina espacios extra y evita tags vacíos con filter(Boolean).

  • Accesibilidad: anuncia cambios con aria-live y bloquea el salto de línea al detectar Enter (en textarea) con preventDefault().

  • Tecla correcta: usa e.key === 'Enter' (moderno) en lugar de keyCode.






Random Choice Picker
Paris Minero 21 octubre, 2025
Compartir
Iniciar sesión dejar un comentario

  
Event KeyCodes
Detecta teclas y muestra sus propiedades