五个可在浏览器直接运行的 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 让声音自然衰减。键盘事件比鼠标事件更连贯,两者都监听即可。
六、把互动代码嵌进自己的页面
如果想在文章里嵌入这些代码(而不是让读者复制保存),可以用 :
<iframe src="todo.html" width="100%" height="400"
style="border:1px solid #e5e7eb;border-radius:8px"></iframe>
把代码保存为同目录的 todo.html,再在文章里用上面的 iframe 引用即可。如果主题色与站点不一致,可以把代码里的 #2563eb、#1e1b4b 等主色替换成自己的设计变量。
七、小结
- 全部代码不依赖任何外部库,单文件双击即可运行
- 五段代码覆盖了拖拽、主题切换、本地存储、随机抽样、Web Audio 五个常见场景
- 嵌入到文章里用
最稳妥 - 想二次开发时,先把
localStorage的 key、CSS 变量、事件名固定下来,再做扩展 - 主题、动效、布局都可以用 CSS 变量驱动,结构与样式解耦
互动代码是练习”动手写前端”最直接的方式。每段代码控制在 100 行左右,先跑通再改样式,是稳妥的迭代节奏。