适合JavaScript初学者的5个练手小项目案例

作者:流萤时间:2026-10-30 18:53:05浏览:495

精选5个适合JavaScript初学者的练手小项目案例,涵盖计数器、待办清单、猜数字游戏、字数统计器与选项卡切换,解析核心DOM操作、事件处理流程与避坑指南。

学习 JavaScript 最直接且高效的路径,是通过体量轻、逻辑完整的小练习,将语法概念与真实的 DOM 交互绑定起来。初学者在看完基础语法后,往往在面对“网页中的按钮怎么起作用”、“输入框里的内容怎么取出来”、“如何动态加一行内容”时感到无从下手。本文选取了 5 个梯度连贯的练手案例:简易计数器、待办事项清单、猜数字游戏、实时文本字数统计器与原生选项卡切换。这些项目不需要配置复杂脚手架或打包工具,用一个 HTML 文件加一个 JS 脚本即可运行,能够帮助初学者扎实掌握事件监听、DOM 元素增删改、条件分支及数据与界面同步的核心方法。

适合JavaScript初学者的5个练手小项目案例
图 1:5 个练手案例的技术进阶与核心知识分布

案例一:简易计数器(Counter)

计数器是初学者接触 JavaScript 交互最理想的第一个项目。它的业务逻辑极其聚焦:页面展示一个当前数值,用户点击“增加”、“减少”或“重置”按钮,数值随之改变,并将最新的计算结果重新渲染到页面上。这个项目帮助你理解什么叫“内存中的变量状态”以及“如何将变量状态同步到网页文本中”。

1. 核心掌握的 API 与概念

  • document.getElementById() 或 document.querySelector():从 DOM 树中抓取需要绑定的按钮与展示文本的容器。
  • element.addEventListener('click', handler):为按钮注册点击事件监听器。
  • element.textContent:安全且直接地修改元素内部的纯文本内容。

2. 完整可执行代码

<!-- index.html 结构 -->
<div class="counter-box">
  <h2 id="countDisplay">0</h2>
  <button id="decreaseBtn">-1</button>
  <button id="resetBtn">重置</button>
  <button id="increaseBtn">+1</button>
</div>

<script>
  // 1. 获取 DOM 节点
  const countDisplay = document.getElementById('countDisplay');
  const decreaseBtn = document.getElementById('decreaseBtn');
  const resetBtn = document.getElementById('resetBtn');
  const increaseBtn = document.getElementById('increaseBtn');

  // 2. 在内存中声明状态变量
  let count = 0;

  // 3. 编写专门用于更新界面的渲染函数
  function updateDisplay() {
    countDisplay.textContent = count;
    // 根据数值正负动态改变文字颜色
    if (count > 0) {
      countDisplay.style.color = '#438A80'; // 正数显示青绿
    } else if (count < 0) {
      countDisplay.style.color = '#E53E3E'; // 负数显示红色
    } else {
      countDisplay.style.color = '#456581'; // 归零显示默认深蓝
    }
  }

  // 4. 绑定点击事件
  increaseBtn.addEventListener('click', () => {
    count++;
    updateDisplay();
  });

  decreaseBtn.addEventListener('click', () => {
    count--;
    updateDisplay();
  });

  resetBtn.addEventListener('click', () => {
    count = 0;
    updateDisplay();
  });
</script>
注意事项:初学者常犯的错误是直接尝试对 countDisplay.textContent++。由于 textContent 得到的是字符串类型(String),直接做自增自减容易导致类型转换混乱或代码难以维护。规范的做法是维护一个纯粹的数字变量(如 let count = 0),数值计算完成后再写回 DOM。

案例二:待办事项清单(To-Do List)

待办事项清单是前端面试和练习中经典的“进阶里程碑”。它涉及完整的 DOM 节点生命周期:用户在输入框输入文本后点击添加,页面动态创建一条包含文本与操作按钮的 <li> 节点;点击完成可以为文本划横线,点击删除可以将整行节点从 DOM 中移除。

适合JavaScript初学者的5个练手小项目案例
图 2:待办事项清单的核心操作步骤与事件委托处理

1. 完整实现代码

<!-- HTML 结构 -->
<form id="todoForm">
  <input type="text" id="todoInput" placeholder="请输入任务内容..." autocomplete="off" />
  <button type="submit">添加</button>
</form>
<ul id="todoList"></ul>

<script>
  const todoForm = document.getElementById('todoForm');
  const todoInput = document.getElementById('todoInput');
  const todoList = document.getElementById('todoList');

  // 1. 监听表单提交
  todoForm.addEventListener('submit', (e) => {
    e.preventDefault(); // 拦截默认页面刷新行为

    const taskText = todoInput.value.trim();
    if (!taskText) {
      alert('任务内容不能为空!');
      return;
    }

    // 2. 动态创建 li 元素
    const li = document.createElement('li');
    
    // 创建文字包裹标签
    const span = document.createElement('span');
    span.textContent = taskText;
    li.appendChild(span);

    // 创建删除按钮
    const deleteBtn = document.createElement('button');
    deleteBtn.textContent = '删除';
    deleteBtn.className = 'delete-btn';
    li.appendChild(deleteBtn);

    // 3. 挂载到列表中
    todoList.appendChild(li);

    // 4. 重置输入框
    todoInput.value = '';
    todoInput.focus();
  });

  // 2. 使用事件委托处理点击事件(完成与删除)
  todoList.addEventListener('click', (e) => {
    const target = e.target;

    // 点击删除按钮时,移除对应的整行 li
    if (target.classList.contains('delete-btn')) {
      const parentLi = target.parentElement;
      todoList.removeChild(parentLi);
    } 
    // 点击任务文字时,切换完成状态样式
    else if (target.tagName === 'SPAN') {
      target.style.textDecoration = 
        target.style.textDecoration === 'line-through' ? 'none' : 'line-through';
    }
  });
</script>
为什么使用事件委托?如果每次 appendChild 时都给新按钮写一个 button.addEventListener('click', ...),当列表条目增加时,会创建大量闭包和事件监听器,消耗内存;更麻烦的是重新渲染或清空时容易造成内存泄漏。把监听器挂载在静态的父级 <ul> 上,通过 e.target 判断触发源,是前端通用且专业的标准做法。

案例三:简易数字猜大小游戏(Number Guessing Game)

该项目围绕纯 JavaScript 的条件分支逻辑、数学计算和状态重置展开。系统在后台秘密生成一个 1 到 100 之间的随机整数,玩家在输入框输入猜测的数字,程序比对后输出“太大了”、“太小了”或“猜对了”,并记录玩家已经尝试的次数。

1. 核心技术点解析

  • Math.floor(Math.random() * 100) + 1:生成指定范围内的随机整数。
  • parseInt(val, 10) 或 Number(val):将用户输入的文本类型显式转为数字类型。
  • 防呆边界校验:校验是否输入了非数字(isNaN)或超出了 1~100 的有效范围。

2. 完整代码

<!-- HTML 结构 -->
<div class="game-box">
  <p>系统已生成 1 到 100 之间的随机整数,请在下方输入猜测:</p>
  <input type="number" id="guessInput" min="1" max="100" placeholder="1-100" />
  <button id="guessBtn">确认猜测</button>
  <button id="restartBtn" style="display:none;">重新开始</button>
  <p id="feedbackText"></p>
  <p id="attemptsText">已猜次数:0</p>
</div>

<script>
  let targetNumber = generateTarget();
  let attempts = 0;
  let isGameOver = false;

  const guessInput = document.getElementById('guessInput');
  const guessBtn = document.getElementById('guessBtn');
  const restartBtn = document.getElementById('restartBtn');
  const feedbackText = document.getElementById('feedbackText');
  const attemptsText = document.getElementById('attemptsText');

  function generateTarget() {
    return Math.floor(Math.random() * 100) + 1;
  }

  guessBtn.addEventListener('click', () => {
    if (isGameOver) return;

    // 获取并显式转换为整数
    const userGuess = parseInt(guessInput.value, 10);

    // 边界条件判断
    if (isNaN(userGuess) || userGuess < 1 || userGuess > 100) {
      feedbackText.textContent = '请输入 1 到 100 之间的有效数字!';
      feedbackText.style.color = '#D97706';
      return;
    }

    attempts++;
    attemptsText.textContent = `已猜次数:${attempts}`;

    // 核心分支判断
    if (userGuess === targetNumber) {
      feedbackText.textContent = `恭喜你!猜对了,数字就是 ${targetNumber}!`;
      feedbackText.style.color = '#438A80';
      isGameOver = true;
      guessBtn.disabled = true;
      restartBtn.style.display = 'inline-block';
    } else if (userGuess > targetNumber) {
      feedbackText.textContent = '太大了,再试一个更小的数字!';
      feedbackText.style.color = '#456581';
    } else {
      feedbackText.textContent = '太小了,再试一个更大的数字!';
      feedbackText.style.color = '#456581';
    }

    guessInput.value = '';
    guessInput.focus();
  });

  restartBtn.addEventListener('click', () => {
    targetNumber = generateTarget();
    attempts = 0;
    isGameOver = false;
    guessBtn.disabled = false;
    restartBtn.style.display = 'none';
    feedbackText.textContent = '';
    attemptsText.textContent = '已猜次数:0';
    guessInput.value = '';
    guessInput.focus();
  });
</script>
适合JavaScript初学者的5个练手小项目案例
图 3:直接绑定与事件委托机制对比及适用场景分析

案例四:实时文本字数统计器(Character Counter)

在各类社交媒体发布框或表单填写场景中,字符计数与超长警告是极常见的组件。该项目要求监听用户的键盘打字与粘贴输入,实时计算文本字符数和剩余可用字数,当接近或达到限制时,动态通过样式类切换给用户清晰的颜色警告。

1. 为什么必须使用 input 事件?

很多初学者会习惯性地监听 keydown 或 keyup。但如果用户通过鼠标右键菜单“粘贴”、直接将文字拖拽进文本域,或者通过中文输入法进行候选字选词,keydown 往往无法准确获取最终生效的文本内容。而原生的 input 事件是在文本框真实值发生任何变更时立即触发,能够全面覆盖打字、删除、粘贴、拖拽等全部交互场景。

2. 完整代码

<!-- HTML 结构 -->
<div class="counter-container">
  <textarea id="contentArea" rows="5" maxlength="150" placeholder="请输入内容(上限 150 字)..."></textarea>
  <div class="counter-info">
    <span id="currentCount">0</span> / <span id="maxLimit">150</span> 字符
    <span id="warningMsg" style="color: #E53E3E; margin-left: 10px; display: none;">已达最大字数限制!</span>
  </div>
</div>

<script>
  const contentArea = document.getElementById('contentArea');
  const currentCount = document.getElementById('currentCount');
  const maxLimit = document.getElementById('maxLimit');
  const warningMsg = document.getElementById('warningMsg');

  const MAX_CHARS = 150;
  maxLimit.textContent = MAX_CHARS;

  contentArea.addEventListener('input', () => {
    const len = contentArea.value.length;
    currentCount.textContent = len;

    // 当输入达到或接近上限时改变提示视觉
    if (len >= MAX_CHARS) {
      currentCount.style.color = '#E53E3E';
      currentCount.style.fontWeight = 'bold';
      warningMsg.style.display = 'inline';
    } else if (len >= MAX_CHARS * 0.9) {
      // 达到90%阈值时给出黄色预警
      currentCount.style.color = '#D97706';
      currentCount.style.fontWeight = 'bold';
      warningMsg.style.display = 'none';
    } else {
      currentCount.style.color = '#456581';
      currentCount.style.fontWeight = 'normal';
      warningMsg.style.display = 'none';
    }
  });
</script>

案例五:纯原生选项卡切换(Tab Switching)

选项卡(Tabs)是网页布局中最普遍的交互形式之一。点击上方的不同标签头,下方的内容区域实时切换展示。在这个项目中,初学者将学到前端经典的“排他思想”:先清除所有同级元素的激活样式,再单独为当前被选中的目标元素添加激活类;同时借助 HTML5 的 data-* 自定义属性,实现标签与对应内容块的精准关联。

1. 关键逻辑与排他思想

<!-- HTML 结构 -->
<div class="tabs-component">
  <!-- 标签导航 -->
  <div class="tab-headers" id="tabHeaders">
    <button class="tab-btn active" data-target="tab-1">基础介绍</button>
    <button class="tab-btn" data-target="tab-2">技术细节</button>
    <button class="tab-btn" data-target="tab-3">参考资源</button>
  </div>

  <!-- 内容面板 -->
  <div class="tab-contents">
    <div class="tab-panel active" id="tab-1">这是【基础介绍】面板的内容,默认展示。</div>
    <div class="tab-panel" id="tab-2" style="display:none;">这是【技术细节】面板,包含核心代码和实现步骤。</div>
    <div class="tab-panel" id="tab-3" style="display:none;">这是【参考资源】面板,包含扩展阅读与文档链接。</div>
  </div>
</div>

<script>
  const tabHeaders = document.getElementById('tabHeaders');
  const allButtons = tabHeaders.querySelectorAll('.tab-btn');
  const allPanels = document.querySelectorAll('.tab-panel');

  tabHeaders.addEventListener('click', (e) => {
    // 确保点击的是标签按钮
    if (!e.target.classList.contains('tab-btn')) return;

    const clickedBtn = e.target;
    const targetPanelId = clickedBtn.dataset.target; // 读取 data-target 属性

    // 1. 排他处理:移除所有按钮的 active 类并隐藏所有面板
    allButtons.forEach(btn => btn.classList.remove('active'));
    allPanels.forEach(panel => {
      panel.classList.remove('active');
      panel.style.display = 'none';
    });

    // 2. 激活当前选中的按钮
    clickedBtn.classList.add('active');

    // 3. 显示对应的面板
    const activePanel = document.getElementById(targetPanelId);
    if (activePanel) {
      activePanel.classList.add('active');
      activePanel.style.display = 'block';
    }
  });
</script>
适合JavaScript初学者的5个练手小项目案例
图 4:5个练手案例的核心技能对照与进阶练习图谱

初学者练习小项目的实用建议

很多初学者在跟练项目时习惯于直接复制粘贴代码,或者只看懂了逻辑就不再动手。想要把代码真正转化成自己的肌肉记忆,建议按照以下三个步骤逐步推进:

  1. 先用自然语言理清数据流向:在写具体代码前,在纸上或注释中写下“触发条件是什么”、“需要读取什么数据”、“需要判断哪些分支”、“最终修改哪个 DOM 节点”。逻辑清晰之后,写代码只是填补具体的语法。
  2. 充分利用浏览器的开发者工具(DevTools):遇到界面没有反应时,第一步先按 F12 检查 Console 控制台是否有红色报错;第二步在关键变量前后写上 console.log('当前变量值:', val) 查看数据是否符合预期;第三步在 Elements 面板中检查元素上的类名与行内样式是否发生改变。
  3. 尝试在原有案例上做“微扩展”:当上述案例能够完全独立敲出并正常运行后,可以尝试在现有代码上加一两个小功能。例如为计数器增加上限警示、为待办清单接入 localStorage 本地缓存、给猜数字记录历史尝试列表。通过这种“小步快跑”的迭代,就能平滑迈入中大型前端项目的开发阶段。
作者最新文章
相关文章
更多