🎧 第十二课:音乐播放器 — 你的第一个"小软件"

前十一课你一直在练单招——HTML 是骨架拳、CSS 是化妆术、JS 是内功、对象数组是兵器、fetch 是千里传音。今天不打练习木桩了,打一套组合拳

零件(你都学过了)来自哪一课在播放器里干什么
HTML 标签第零课骨架:搜索框、按钮、歌单列表
CSS 布局第二、五课把播放器打扮好看(flex 上场)
函数 + 事件第三课点按钮有反应
数组 + 对象第四、十课歌单 = 档案袋数组(压轴用上!)
fetch 调 API第十一课上网点歌(iTunes 免费 30 秒试听)
localStorage第七课挑战③:收藏歌曲

🎭 总比喻:每一课发你一种积木,今天是照图纸拼乐高。拼出来的东西符合"软件四要素":
① 有输入(搜歌)② 有输出(声音)③ 会联网(点歌 API)④ 有界面(按钮歌单)。
——这就是"做网页"和"做软件"的分界线,今天你跨过去!

🎮 2. 先玩后学:成品操作台

下面就是今天要"解剖"的完整播放器。先玩爽了,再拆开看零件。搜一个你喜欢的歌手试试(用的是苹果 iTunes 免费 API,每首歌有 30 秒试听):

🔍 想听什么? 输入歌手或歌名,比如:周杰伦

封面
👆 先在上面点一首歌

📌 玩法提示:点歌 → 点歌单里任意一首开播 → ▶️/⏸ 暂停继续 → ⏭ 切下一首(播完最后一首会回到第一首)→ 底部蓝条是进度(30 秒倒计时)→ 播完自动切下一首。按 F12 → Console 还能看到每次点歌的原始 JSON。

📡 3. 第一步:上网点歌(老套路 + 两个新零件)

这一步是第十一课 fetch 三步曲的复习,只是换了个餐厅(iTunes 点歌 API),另外加了两个新零件(标序号):

// ① 搜索框:拿用户打的字 ② 编码:中文翻成网址密码 ③ fetch 三步曲
let kw = document.getElementById('kw').value; /* ① 用户打的字(空值→提示) */
fetch('https://itunes.apple.com/search?term=' + encodeURIComponent(kw) + '&media=music&country=cn&limit=8') /* ②③ */
  .then(r => r.json())
  .then(data => {
      songs = data.results; /* ④ 拆盒:results 数组里全是歌 */
  });
序号新零件说明
input 搜索框HTML 自带的"信箱口",用户打的字用 .value 拿出来——跟第七课访问计数器一样"先取再说"
encodeURIComponent()把中文/空格翻译成网址密码。比喻:门牌号不能写汉字,邮递员只认 %E5%91%A8 这种代号。不翻译,搜中文可能报错
country=cn让餐厅上"中国菜单"——返回中文歌名(不加它搜"周杰伦"会出来 "Jay Chou");limit=8 = 最多 8 首
data.results拆盒后,所有歌装在一个叫 results数组里,每首歌是一个档案袋——第十课的"档案袋数组",今天实战

拆开档案袋看看,每个标签你都能看懂:
{"trackName": "晴天", "artistName": "周杰伦", "artworkUrl100": "封面图网址", "previewUrl": "30秒试听网址", ...}

📌 复习:这就是第十一课三步曲——fetch('网址') 点餐 → .then(r => r.json()) 拆盒 → .then(data => ...) 开吃。换餐厅不换姿势。

🖨️ 4. 第二步:把歌单画到页面上(数组 → 页面)

数据拿到了,但还躺在代码里。要把 8 首歌变成页面上的 8 张小卡,需要新工具(标序号):

songs.forEach(function(song, i) { /* ① 数组里每个档案袋都跑一遍;i = 第几个(下标) */
  let div = document.createElement('div'); /* ② 造一张新小卡 */
  div.className = 'song';
  div.innerHTML = '<img src="' + song.artworkUrl100 + '">' +
      '<strong>' + song.trackName + '</strong><br><span>' + song.artistName + '</span>'; /* ③ 填内容 */
  div.onclick = function() { play(i); }; /* ④ 点击小卡 → 播第 i 首 */
  list.appendChild(div); /* ⑤ 把小卡挂进结果区 */
});
序号新零件说明
forEach第四课 for 循环的"便民版"——数组里每个东西都跑一遍。对比:for 循环自己管下标管长度,forEach 全自动
createElement用 JS 凭空造一个 HTML 标签。之前你只会改已有的标签,现在会"造"了
innerHTML把小卡的内部内容用字符串塞进去(textContent 只能塞纯文字,innerHTML 能塞标签)
div.onclick刚造出来的小卡也装上事件——注意 play(i) 要把 i 传进去,不然不知道点的是第几首
appendChild把小卡挂进结果区——造出来不用等于白造
📌 为什么不直接用 textContent?textContent 只能放文字,而一张小卡要装图片+两个文字标签,是"一个元素"。造元素 → 填内容 → 挂上去,三步走。

🔊 5. 第三步:真播放(HTML 自带的隐形喇叭)

网页播放声音不用装任何东西——HTML 自带一个 <audio> 喇叭元素(标序号):

// ① HTML 里的隐形喇叭:<audio id="player"></audio>(不给它 controls 属性,它就隐身)
function play(i) {
  index = i; /* ② 记住:正在播第 i 首 */
  let song = songs[index];
  audio.src = song.previewUrl; /* ③ 把歌装进喇叭 */
  audio.play(); /* ④ 开播! */
  // ⑤ 顺手更新控制台:封面图、歌名、按钮变 ⏸
}

function togglePlay() { /* ⑥ 播放/暂停二合一按钮 */
  if (audio.paused) { audio.play(); } /* ⑦ 问喇叭"停着吗?" */
  else { audio.pause(); }
}
序号新零件说明
<audio>HTML 自带喇叭。加 controls 属性会显示系统自带的播放条;不写就隐身,按钮自己做(自己做才好看)
index一个全局变量 = "正在播第几首"。切歌、下一首全靠它记住位置(数组下标的老本行)
audio.src把歌装进喇叭——src 就是歌曲网址(previewUrl)
play() / pause()喇叭的两个开关。对比记忆:play() 是"按播放键",pause() 是"按暂停键"
audio.paused问喇叭:"你现在停着吗?"——返回 true(停着)或 false(在响)。配合 if 就做成 ▶️/⏸ 二合一按钮
📌 previewUrl 只有 30 秒:iTunes 免费 API 只给 30 秒试听(保护版权)。想播完整歌曲?把自己的 mp3 文件放进网页文件夹,把 src 换成本地文件名就行——挑战里可以试试。

🎛 6. 第四步:控制台进阶(切歌 + 进度条 + 自动切歌)

最后一波新零件(标序号):

function next() {
  index = (index + 1) % songs.length; /* ① 下一首:取余绕圈 */
  play(index);
}

// ② 喇叭边播边汇报 → 进度条跟着走
audio.addEventListener('timeupdate', function() {
  let p = (audio.currentTime / audio.duration) * 100; /* ③ 已播 ÷ 全长 = 百分比 */
  document.getElementById('bar').style.width = p + '%';
});

// ④ 播完了喇叭喊"结束了" → 自动切下一首
audio.addEventListener('ended', next);

// ⑤ 搜索框里按回车 = 点"点歌"按钮
document.getElementById('kw').addEventListener('keydown', function(e) {
  if (e.key === 'Enter') search(); /* ⑥ e.key 是"按了哪个键" */
});
序号新零件说明
% 取余除法取余数:8 首歌,播第 8 首再下一首 → (7+1) % 8 = 0 → 回到第一首。比喻:操场绕圈跑,跑出圈尾 = 回到圈头,永远不会越界
timeupdate 事件新类型的事件!以前学的 click、resize 都是"用户动作",这是喇叭自己的"汇报"——边播边喊"现在到 X 秒了",我们监听它更新进度条
currentTime / duration已播秒数 ÷ 全长秒数 = 百分比。这就是进度条的原理:没有魔法,就是除法
ended 事件播完喇叭喊"结束了"——直接把 next 函数交给它:30 秒试听播完自动切歌,连播不冷场
⑤⑥keydown + e.key"按了键盘"事件;e.key 是"具体哪个键"。对比:click 事件对象有 target,键盘事件对象有 key
✨ 本课最值钱的一句话:事件不一定要"人"来触发。
喇叭会自己喊 timeupdate、ended,你监听它们就能让进度条动、让歌自动切——
网页上的"自动",全是这么来的。

🛠️ 7. 动手挑战

✏️ 三个挑战:

① 搬新家(必做)
新建 my-music.html 放进 my-site 文件夹:把上面操作台的代码搬过去,打扮成你自己的音乐播放器(改改颜色、标题),然后在主页「作品」区加一张卡片链接它。
老规矩:改完同步副本 + 拖拽上传。

② 随机播放 🔀(必做)
加一个「🔀」按钮,点到谁播谁。新知识就一个:
index = Math.floor(Math.random() * songs.length); play(index);
  · Math.random() = 摇骰子,摇出 0~1 之间的随机小数
  · × 歌单长度 → 变成 0 到 长度-1 之间的随机小数
  · Math.floor() = 向下取整(小数点后全砍掉)→ 合法的随机下标

③ 收藏 ⭐(附加题)
每首歌加一个 ⭐ 按钮,点一下收藏这首歌,页面加「我的收藏」列表,点收藏也能播。
第七课三件套 + 第十课 JSON:收藏列表是档案袋数组,存 localStorage 前记得 JSON.stringify(抽屉只收字符串),取出来 JSON.parse

老规矩:改代码 → 保存 → 刷新 → F12 看报错。播放器常见的坑:① 忘了 country=cn(歌名变拼音)② 没把 i 传进 play(i)(点谁都播第一首)③ 收藏时没 stringify(抽屉里变 [object Object])。

📋 8. 今日总结

知识点一句话
input 搜索框 ⭐.value 拿用户打的字
encodeURIComponent ⭐中文翻成网址密码
forEach + createElement ⭐数组循环造小卡,appendChild 挂上页面
audio 喇叭 ⭐src 装歌、play()/pause() 开关、paused 问状态
% 取余 ⭐操场绕圈,切歌永不越界
timeupdate / ended喇叭自己的"汇报"事件 → 进度条 + 自动切歌
keydown + e.key按回车 = 点按钮
🎉 学完这一课:你做出了人生第一个"小软件"——
有输入(搜歌)、有输出(声音)、会联网(点歌 API)、有界面(控制台)。
第一课的你只会写 <h1>你好</h1>,现在的你已经会做软件了。
回头看看主页上的那些技能卡——它们全都"通电"了。

✅ 下一步?

第十三课:Git 版本管理把代码装进"时间机器"——改坏了随时回到过去,再也不用手动存副本。第九课注册的 GitHub 账号要正式上场了 🐙

先做挑战,做完喊我检查!🚀