五个可在浏览器直接运行的 HTML 互动小代码

五个可在浏览器直接运行的 HTML 互动小代码

互动代码是把”读”变成”玩”的最直接方式。下面的五段 HTML 都可以直接复制到本地 .html 文件中双击打开运行,也可以粘贴到任意支持 iframe 的网页里。每一段都只使用原生 HTML/CSS/JavaScript,不依赖任何外部库,复制即用。

一、可拖拽的方块

把鼠标按在小方块上,可以把它拖到页面任意位置;松开鼠标后,方块就会停在那里。


<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>可拖拽的方块</title>
<style>
  body { margin: 0; height: 100vh; background: #f3f4f6; }
  .draggable {
    position: absolute; width: 120px; height: 120px;
    background: linear-gradient(135deg, #6366f1, #8b5cf6);
    color: #fff; display: flex; align-items: center; justify-content: center;
    border-radius: 12px; cursor: move; user-select: none;
    box-shadow: 0 8px 20px rgba(99, 102, 241, 0.3);
    font: 16px/1.4 system-ui, sans-serif;
  }
</style>
</head>
<body>
  <div class="draggable" id="box">拖动我</div>
  <script>
    const box = document.getElementById('box');
    let offsetX = 0, offsetY = 0, dragging = false;
    box.addEventListener('mousedown', (e) => {
      dragging = true;
      offsetX = e.clientX - box.offsetLeft;
      offsetY = e.clientY - box.offsetTop;
    });
    document.addEventListener('mousemove', (e) => {
      if (!dragging) return;
      box.style.left = (e.clientX - offsetX) + 'px';
      box.style.top  = (e.clientY - offsetY) + 'px';
    });
    document.addEventListener('mouseup', () => { dragging = false; });
  </script>
</body>
</html>

要点mousedown 记录指针相对元素的位置,mousemove 用该偏移量更新元素位置。mouseup 释放拖拽状态。这套模板可以扩展到多个元素。

二、点击切换主题的按钮

页面右上角有一个按钮,点击后会在浅色与深色主题之间切换,颜色全部通过 CSS 变量驱动。


<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>主题切换</title>
<style>
  :root {
    --bg: #ffffff; --fg: #1f2937; --card: #f9fafb; --accent: #2563eb;
  }
  body.dark {
    --bg: #111827; --fg: #f9fafb; --card: #1f2937; --accent: #f59e0b;
  }
  body { margin: 0; background: var(--bg); color: var(--fg);
         font: 16px/1.6 system-ui, sans-serif; transition: all .3s; }
  .toggle { position: fixed; top: 16px; right: 16px; padding: 8px 14px;
            background: var(--accent); color: #fff; border: 0; border-radius: 8px;
            cursor: pointer; }
  .card { max-width: 520px; margin: 80px auto; padding: 24px;
          background: var(--card); border-radius: 12px; }
</style>
</head>
<body>
  <button class="toggle" id="btn">切换主题</button>
  <div class="card">
    <h2>这是一个示例卡片</h2>
    <p>点击右上角按钮切换浅色 / 深色主题。所有颜色由 CSS 变量驱动。</p>
  </div>
  <script>
    document.getElementById('btn').addEventListener('click', () => {
      document.body.classList.toggle('dark');
    });
  </script>
</body>
</html>

要点:用 :root 定义变量、用 body.dark 覆盖变量,整套主题切换只需一行 classList.toggle。把这种模式用到自己的站点上,配合 localStorage 即可记住用户偏好。

三、待办清单(带本地存储)

输入框 + 复选框 + 删除按钮。数据保存在 localStorage,刷新页面不会丢。


<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>待办清单</title>
<style>
  body { font: 16px/1.6 system-ui, sans-serif; max-width: 480px;
         margin: 40px auto; padding: 0 16px; }
  .row { display: flex; gap: 8px; }
  input[type=text] { flex: 1; padding: 8px 10px; border: 1px solid #d1d5db;
                     border-radius: 6px; }
  button { padding: 8px 14px; background: #2563eb; color: #fff; border: 0;
           border-radius: 6px; cursor: pointer; }
  ul { list-style: none; padding: 0; margin: 20px 0; }
  li { display: flex; align-items: center; gap: 8px; padding: 8px 0;
       border-bottom: 1px solid #f3f4f6; }
  li.done span { text-decoration: line-through; color: #9ca3af; }
  li button { background: #ef4444; }
</style>
</head>
<body>
  <h2>待办清单</h2>
  <div class="row">
    <input type="text" id="input" placeholder="要做什么…">
    <button id="add">添加</button>
  </div>
  <ul id="list"></ul>
  <script>
    const STORAGE_KEY = 'todo-items';
    const input = document.getElementById('input');
    const list  = document.getElementById('list');
    const add   = document.getElementById('add');
    const load = () => JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
    const save = (items) => localStorage.setItem(STORAGE_KEY, JSON.stringify(items));
    const render = () => {
      list.innerHTML = '';
      load().forEach((it, idx) => {
        const li = document.createElement('li');
        if (it.done) li.classList.add('done');
        li.innerHTML = `
          <input type="checkbox" ${it.done ? 'checked' : ''}>
          <span>${it.text}</span>
          <button>删除</button>`;
        li.querySelector('input').addEventListener('change', (e) => {
          const items = load(); items[idx].done = e.target.checked;
          save(items); render();
        });
        li.querySelector('button').addEventListener('click', () => {
          const items = load(); items.splice(idx, 1);
          save(items); render();
        });
        list.appendChild(li);
      });
    };
    add.addEventListener('click', () => {
      const text = input.value.trim(); if (!text) return;
      const items = load(); items.push({ text, done: false });
      save(items); input.value = ''; render();
    });
    input.addEventListener('keydown', (e) => { if (e.key === 'Enter') add.click(); });
    render();
  </script>
</body>
</html>

要点:所有状态序列化到 localStorage 一个 JSON 字符串中,渲染逻辑统一从 load() 读取。这样写出来的组件加新字段、迁移数据都很方便。

四、随机点名器

在文本框里一行放一个人名,点击按钮随机抽一个。可用作课堂、活动、抽奖的小工具。


<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>随机点名器</title>
<style>
  body { font: 16px/1.6 system-ui, sans-serif; max-width: 480px;
         margin: 40px auto; padding: 0 16px; text-align: center; }
  textarea { width: 100%; height: 160px; padding: 10px; border: 1px solid #d1d5db;
             border-radius: 6px; box-sizing: border-box; }
  button { margin-top: 12px; padding: 10px 20px; background: #10b981; color: #fff;
           border: 0; border-radius: 6px; cursor: pointer; font-size: 16px; }
  .result { margin-top: 24px; font-size: 32px; font-weight: bold; color: #10b981;
            min-height: 48px; }
</style>
</head>
<body>
  <h2>随机点名器</h2>
  <textarea id="pool" placeholder="一行一个人名"></textarea>
  <button id="pick">开始抽</button>
  <div class="result" id="result"></div>
  <script>
    const pool   = document.getElementById('pool');
    const pick   = document.getElementById('pick');
    const result = document.getElementById('result');
    pool.value = '张三\n李四\n王五\n赵六\n钱七';
    pick.addEventListener('click', () => {
      const names = pool.value.split('\n').map(s => s.trim()).filter(Boolean);
      if (!names.length) { result.textContent = '请先输入人名'; return; }
      const idx = Math.floor(Math.random() * names.length);
      result.textContent = names[idx];
    });
  </script>
</body>
</html>

要点:输入处理用 split + filter(Boolean) 跳过空行;Math.random() * length 取下标即可。如果要做到”不重复抽”,可以额外维护一个已抽名单并随抽取移除。

五、键盘钢琴(用键盘敲出旋律)

A S D F G H J 七个键,会发出不同音高的”叮”声。无需任何音频文件,音效用 Web Audio API 实时合成。


<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>键盘钢琴</title>
<style>
  body { font: 16px/1.6 system-ui, sans-serif; text-align: center;
         margin: 40px; background: #1e1b4b; color: #fff; }
  .keys { display: flex; gap: 6px; justify-content: center; margin-top: 30px; }
  .key { width: 60px; height: 200px; background: #fff; color: #1e1b4b;
         border-radius: 8px; display: flex; align-items: flex-end;
         justify-content: center; padding-bottom: 12px; font-weight: bold;
         cursor: pointer; user-select: none; transition: transform .05s; }
  .key.active { background: #facc15; transform: translateY(4px); }
  p { color: #c7d2fe; }
</style>
</head>
<body>
  <h2>键盘钢琴</h2>
  <p>按下 A S D F G H J 键弹奏</p>
  <div class="keys" id="keys"></div>
  <script>
    const NOTES = [261.63, 293.66, 329.63, 349.23, 392.00, 440.00, 493.88];
    const KEYS  = ['A','S','D','F','G','H','J'];
    const audio = new (window.AudioContext || window.webkitAudioContext)();
    const keysEl = document.getElementById('keys');
    KEYS.forEach((k, i) => {
      const div = document.createElement('div');
      div.className = 'key'; div.textContent = k;
      div.dataset.idx = i;
      div.addEventListener('mousedown', () => play(i, div));
      keysEl.appendChild(div);
    });
    document.addEventListener('keydown', (e) => {
      const i = KEYS.indexOf(e.key.toUpperCase());
      if (i >= 0) play(i, keysEl.children[i]);
    });
    function play(i, el) {
      el.classList.add('active');
      setTimeout(() => el.classList.remove('active'), 150);
      const osc = audio.createOscillator();
      const gain = audio.createGain();
      osc.frequency.value = NOTES[i];
      osc.type = 'sine';
      osc.connect(gain).connect(audio.destination);
      gain.gain.setValueAtTime(0.2, audio.currentTime);
      gain.gain.exponentialRampToValueAtTime(0.001, audio.currentTime + 0.4);
      osc.start();
      osc.stop(audio.currentTime + 0.4);
    }
  </script>
</body>
</html>

要点AudioContext 创建 OscillatorNode + GainNode 链,exponentialRampToValueAtTime 让声音自然衰减。键盘事件比鼠标事件更连贯,两者都监听即可。

六、把互动代码嵌进自己的页面

如果想在文章里嵌入这些代码(而不是让读者复制保存),可以用