You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在打字速度测试中实现文本遍历与光标移动?

解决打字速度测试的光标移动与WPM统计问题

看起来你已经搭好了不错的基础框架,我来帮你梳理实现思路,解决光标移动和WPM计算的核心问题,同时修正你之前尝试代码里的小错误。

核心需求拆解

你需要实现两个关键功能:

  • 仅在输入正确字符时,让闪烁光标在目标文本中逐步移动
  • 统计用时并计算标准的WPM(每分钟字数)

一步步实现解决方案

1. 调整HTML结构(优化光标与文本的关联)

首先,我们把目标文本和光标分开,这样更容易精准控制光标的位置:

<body>
  <div class="jumbotron title-of-page container-fluid">
    <h1>Typing Counter</h1>
  </div>
  <nav class="navbar navigation-bar container-fluid">
    <div class="">
      <ul class="nav navbar-nav">
        <li><a href="#">Home</a></li>
        <li><a href="#">Contest</a></li>
        <li><a href="#">About</a></li>
        <li><a href="#">Leaderboard</a></li>
      </ul>
    </div>
  </nav>
  <div>
    <div id="timer">
      <button type="button" class="btn">Start</button>
    </div>
    <div class="typing-field">
      <p id="display-text">
        <span class="typed-cursor">|</span>
        <span id="target-text">This is to test your typing speed. So type like you'll never type again.</span>
      </p>
    </div>
    <div class="user-input">
      <input type="text" name="user-input-text-box" id="user-input" />
    </div>
  </div>
</body>

2. 重写JavaScript逻辑(实现光标移动与WPM计算)

替换你现有的JS代码,这个版本会处理输入校验、光标移动、倒计时和WPM计算:

// 全局状态变量,跟踪测试进度
let targetText = '';
let currentInputIndex = 0;
let testStartTime = null;
let countdownInterval = null;

window.onload = function() {
  // 获取DOM元素
  const userInputBox = document.getElementById('user-input');
  const timerDisplay = document.getElementById('timer');
  const targetTextElement = document.getElementById('target-text');
  const startButton = timerDisplay.querySelector('button');

  // 初始化目标文本
  targetText = targetTextElement.textContent.trim();

  // 绑定开始按钮点击事件
  startButton.addEventListener('click', initTest);

  // 监听用户输入(用input事件比keyup更可靠,支持粘贴等操作)
  userInputBox.addEventListener('input', handleUserInput);

  // 初始化测试状态
  function initTest() {
    // 重置所有状态
    currentInputIndex = 0;
    testStartTime = Date.now();
    userInputBox.value = '';
    userInputBox.disabled = false;
    userInputBox.focus();
    resetTextStyling();

    // 启动倒计时
    let remainingSeconds = 3;
    clearInterval(countdownInterval);
    countdownInterval = setInterval(() => {
      if (remainingSeconds === -1) {
        clearInterval(countdownInterval);
        timerDisplay.textContent = "Time Up";
        userInputBox.disabled = true;
        calculateAndShowWPM();
      } else {
        timerDisplay.textContent = `Time Left: ${remainingSeconds} seconds`;
        remainingSeconds--;
      }
    }, 1000);
  }

  // 处理用户输入校验
  function handleUserInput() {
    if (!testStartTime || userInputBox.disabled) return;

    const inputValue = this.value;
    // 只校验最新输入的字符
    const latestChar = inputValue[inputValue.length - 1];
    const expectedChar = targetText[currentInputIndex];

    if (latestChar === expectedChar) {
      // 输入正确:移动光标,高亮已输入文本
      currentInputIndex++;
      updateTextStyling();

      // 如果完成所有文本,提前结束测试
      if (currentInputIndex >= targetText.length) {
        clearInterval(countdownInterval);
        timerDisplay.textContent = "Completed!";
        userInputBox.disabled = true;
        calculateAndShowWPM();
      }
    } else {
      // 输入错误:删除错误字符,给出视觉提示
      this.value = inputValue.slice(0, -1);
      this.classList.add('input-error');
      setTimeout(() => this.classList.remove('input-error'), 500);
    }
  }

  // 更新文本样式:高亮已输入内容,移动光标
  function updateTextStyling() {
    const typedPart = targetText.slice(0, currentInputIndex);
    const remainingPart = targetText.slice(currentInputIndex);
    targetTextElement.innerHTML = `
      <span style="color: green;">${typedPart}</span>
      <span class="typed-cursor">|</span>
      ${remainingPart}
    `;
  }

  // 重置文本样式到初始状态
  function resetTextStyling() {
    targetTextElement.innerHTML = `<span class="typed-cursor">|</span>${targetText}`;
  }

  // 计算并显示WPM
  function calculateAndShowWPM() {
    if (!testStartTime) return;

    // 计算用时(转换为分钟)
    const timeElapsed = (Date.now() - testStartTime) / 1000 / 60;
    // 标准WPM计算:每5个字符(含空格)算一个单词
    const totalTypedChars = currentInputIndex;
    const typedWords = totalTypedChars / 5;
    const wpm = Math.round(typedWords / timeElapsed);

    // 在页面显示结果
    const resultElement = document.createElement('div');
    resultElement.style.cssText = `
      margin-top: 2em;
      font-size: 1.5em;
      text-align: center;
      color: #2ecc71;
    `;
    resultElement.textContent = `你的打字速度:${wpm} WPM`;
    document.body.appendChild(resultElement);
  }
};

3. 补充CSS样式(优化错误提示)

在你的CSS里添加这行,给错误输入一个清晰的视觉提示:

#user-input.input-error {
  border-color: #e74c3c;
  outline-color: #e74c3c;
}

你之前代码的问题说明

你尝试的JS代码有几个小错误,我帮你指出来:

  • document.getElementById('user-input').innerHTML:input元素的内容要通过value获取,不是innerHTML
  • 目标文本应该是#display-text里的内容,而不是输入框的内容
  • letter.style.color="green":letter是字符串,不是DOM元素,不能直接设置样式
  • 使用keyup事件:不如input事件可靠,input能捕获粘贴、长按输入等场景

内容的提问来源于stack exchange,提问作者3.14159

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:52:36