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

HTML/Javascript函数执行异常求助及计时异常疑问

空格键时长测试网页问题修复

问题概述

  1. 首轮试验正常,但松开空格键后无法进入下一轮:无倒计时、轮次编号不更新、不生成新随机数,控制台报错:index.html:92 Uncaught TypeError: Cannot set properties of null (setting 'textContent')
  2. 按住空格键5-10秒,系统却记录为0.012秒左右的异常时长

问题1:TypeError与轮次切换失败修复

根本原因

displayRandomNumber函数通过innerHTML完全替换了page2的内容,导致原有的#countdown元素被删除。下一轮调用startCountdown时,document.getElementById('countdown')返回null,无法设置textContent,后续代码执行中断。同时,重复绑定keydown事件会导致多次触发,干扰逻辑。

修复步骤

  1. 拆分HTML结构:将page2拆分为倒计时容器和试验内容容器,避免替换时删除倒计时元素:
<div id="page2" style="display: none;">
  <div id="countdownContainer">
    <h2>Countdown</h2>
    <div id="countdown">5</div>
  </div>
  <div id="trialContent" style="display: none;"></div>
</div>
  1. 修改displayRandomNumber函数:仅更新试验内容区域,隐藏倒计时容器,同时避免重复绑定事件:
function displayRandomNumber() {
  document.getElementById('countdownContainer').style.display = 'none';
  document.getElementById('trialContent').style.display = 'block';
  let randomNumber = Math.floor(Math.random() * (upperBound - lowerBound + 1)) + lowerBound;
  document.getElementById('trialContent').innerHTML = `
                <h2>Trial number: ${trialNumber}</h2>
                <h2>Number of seconds to hold the spacebar: ${randomNumber}</h2>
                <p>Press and hold the spacebar to record the duration.</p>
            `;
  // 先移除旧监听,再添加新监听
  document.removeEventListener('keydown', handleKeyDown);
  document.addEventListener('keydown', handleKeyDown);
}
  1. 修改displayCountdown函数:切换轮次时显示倒计时容器,隐藏试验内容:
function displayCountdown() {
  document.getElementById('page1').style.display = 'none';
  document.getElementById('page2').style.display = 'block';
  document.getElementById('countdownContainer').style.display = 'block';
  document.getElementById('trialContent').style.display = 'none';
  startCountdown();
}
  1. 在handleKeyUp中移除keydown监听:避免下一轮重复触发:
function handleKeyUp(event) {
  if (event.key === ' ') {
    spacebarUpTime = new Date().getTime();
    let duration = spacebarUpTime - spacebarDownTime;
    if (duration > 0) {
      saveData(trialNumber, duration);
    }
    document.removeEventListener('keydown', handleKeyDown);
    nextTrial();
    document.removeEventListener('keyup', handleKeyUp);
  }
}

问题2:计时异常修复

根本原因

系统默认的按键重复功能会导致keydown事件被多次触发,spacebarDownTime被不断覆盖为最新的时间,最终计算出的时长接近0。

修复步骤

修改handleKeyDown和handleKeyUp函数,确保只记录一次按下时间,并在每次试验后重置变量:

function handleKeyDown(event) {
  // 仅在未记录按下时间时保存
  if (event.key === ' ' && !spacebarDownTime) {
    spacebarDownTime = new Date().getTime();
    document.addEventListener('keyup', handleKeyUp);
  }
}

function handleKeyUp(event) {
  if (event.key === ' ') {
    spacebarUpTime = new Date().getTime();
    let duration = spacebarUpTime - spacebarDownTime;
    if (duration > 0) {
      saveData(trialNumber, duration);
    }
    document.removeEventListener('keydown', handleKeyDown);
    spacebarDownTime = null; // 重置变量,方便下一轮记录
    nextTrial();
    document.removeEventListener('keyup', handleKeyUp);
  }
}

完整修正代码

HTML

<div class="container">
  <div id="page1">
    <h2>Enter Details</h2>
    <label for="numTrials">Number of Trials:</label>
    <input type="number" id="numTrials" min="1" required>
    <br>
    <label for="lowerBound">Lower Bound Time (seconds):</label>
    <input type="number" id="lowerBound" min="1" required>
    <br>
    <label for="upperBound">Upper Bound Time (seconds):</label>
    <input type="number" id="upperBound" min="1" required>
    <br>
    <button id="startButton">Start</button>
  </div>

  <div id="page2" style="display: none;">
    <div id="countdownContainer">
      <h2>Countdown</h2>
      <div id="countdown">5</div>
    </div>
    <div id="trialContent" style="display: none;"></div>
  </div>

  <div id="exportContainer" style="display: none;">
    <button id="exportBtn">Export Data</button>
    <br>
    <button id="resetBtn">Reset</button>
  </div>
</div>

CSS

body {
  font-family: Arial, sans-serif;
  margin: 0;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
}

.container {
  text-align: center;
}

button {
  padding: 10px 20px;
  font-size: 16px;
  cursor: pointer;
  margin-top: 10px;
}

#countdown {
  font-size: 24px;
  margin-bottom: 20px;
}

JavaScript

let trialsLeft;
let lowerBound;
let upperBound;
let trialNumber = 1;
let csvContent = "data:text/csv;charset=utf-8,Trial,Duration\n";
let spacebarDownTime;
let spacebarUpTime;

function startCountdown() {
  let countdownElement = document.getElementById('countdown');
  let count = 5;
  countdownElement.textContent = 5;

  let timer = setInterval(() => {
    count--;
    countdownElement.textContent = count;

    if (count <= 0) {
      clearInterval(timer);
      displayRandomNumber();
    }
  }, 1000);
}

function displayRandomNumber() {
  document.getElementById('countdownContainer').style.display = 'none';
  document.getElementById('trialContent').style.display = 'block';
  let randomNumber = Math.floor(Math.random() * (upperBound - lowerBound + 1)) + lowerBound;
  document.getElementById('trialContent').innerHTML = `
                <h2>Trial number: ${trialNumber}</h2>
                <h2>Number of seconds to hold the spacebar: ${randomNumber}</h2>
                <p>Press and hold the spacebar to record the duration.</p>
            `;
  document.removeEventListener('keydown', handleKeyDown);
  document.addEventListener('keydown', handleKeyDown);
}

function handleKeyDown(event) {
  if (event.key === ' ' && !spacebarDownTime) {
    spacebarDownTime = new Date().getTime();
    document.addEventListener('keyup', handleKeyUp);
  }
}

function handleKeyUp(event) {
  if (event.key === ' ') {
    spacebarUpTime = new Date().getTime();
    let duration = spacebarUpTime - spacebarDownTime;
    if (duration > 0) {
      saveData(trialNumber, duration);
    }
    document.removeEventListener('keydown', handleKeyDown);
    spacebarDownTime = null;
    nextTrial();
    document.removeEventListener('keyup', handleKeyUp);
  }
}

function saveData(trialNumber, duration) {
  csvContent += `${trialNumber},${(duration/1000).toFixed(3)}\n`;
}

function nextTrial() {
  trialNumber++;
  if (trialNumber <= trialsLeft) {
    displayCountdown();
  } else {
    displayExportButton();
  }
}

function displayCountdown() {
  document.getElementById('page1').style.display = 'none';
  document.getElementById('page2').style.display = 'block';
  document.getElementById('countdownContainer').style.display = 'block';
  document.getElementById('trialContent').style.display = 'none';
  startCountdown();
}

function displayExportButton() {
  document.getElementById('page1').style.display = 'none';
  document.getElementById('page2').style.display = 'none';
  document.getElementById('exportContainer').style.display = 'block';
}

function exportData() {
  const encodedUri = encodeURI(csvContent);
  const link = document.createElement("a");
  link.setAttribute("href", encodedUri);
  link.setAttribute("download", "key_press_data.csv");
  document.body.appendChild(link);
  link.click();
}

function resetPage() {
  location.reload();
}

document.getElementById('startButton').addEventListener('click', function() {
  trialsLeft = parseInt(document.getElementById('numTrials').value);
  lowerBound = parseInt(document.getElementById('lowerBound').value);
  upperBound = parseInt(document.getElementById('upperBound').value);

  if (isNaN(trialsLeft) || isNaN(lowerBound) || isNaN(upperBound)) {
    alert('Please enter valid numbers');
    return;
  }

  if (lowerBound >= upperBound) {
    alert('Upper bound must be greater than lower bound');
    return;
  }

  displayCountdown();
});

document.getElementById('exportBtn').addEventListener('click', exportData);
document.getElementById('resetBtn').addEventListener('click', resetPage);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 10:57:03