HTML/Javascript函数执行异常求助及计时异常疑问
空格键时长测试网页问题修复
问题概述
- 首轮试验正常,但松开空格键后无法进入下一轮:无倒计时、轮次编号不更新、不生成新随机数,控制台报错:
index.html:92 Uncaught TypeError: Cannot set properties of null (setting 'textContent') - 按住空格键5-10秒,系统却记录为0.012秒左右的异常时长
问题1:TypeError与轮次切换失败修复
根本原因
displayRandomNumber函数通过innerHTML完全替换了page2的内容,导致原有的#countdown元素被删除。下一轮调用startCountdown时,document.getElementById('countdown')返回null,无法设置textContent,后续代码执行中断。同时,重复绑定keydown事件会导致多次触发,干扰逻辑。
修复步骤
- 拆分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>
- 修改
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); }
- 修改
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(); }
- 在
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
相关产品推荐
相关产品推荐

