HTML and Javascript newbie here. I am trying to make a webpage that allows you to specify the number of times a random number will be presented to you (random number will be between bounds that you specify) and you attempt to hold the spacebar for the matching amount of time. Each time you lift the spacebar for a trial, it progresses to the next trial. with a 5 second count down first. This works the first time, but after lifting the key, it seems that the functions to transition to the next trial are not executing. The output records the key holds, but there is no count down and the page does not update the trial number, or create a new random number within the bounds. Any help would be greatly appreciated!
I have tried googling and looking on forums, best I can find is that the function is throwing an: "index.html:92 Uncaught TypeError: Cannot set properties of null (setting ‘textContent’)" error, but I don’t know if this is the real cause, or how to correct it.
P.S. If anyone could also explain why the time recorded is 0.012 seconds when held for 5-10 seconds, that would be swell. But this is secondary.
let trialsLeft;
let lowerBound;
let upperBound;
let trialNumber = 1;
let csvContent = "data:text/csv;charset=utf-8,Trial,Durationn";
let spacebarDownTime;
let spacebarUpTime;
/*
function startCountdown() {
let countdownElement = document.getElementById('countdown');
let count = 5;
countdownElement.textContent = count;
let timer = setInterval(() => {
count--;
countdownElement.textContent = count;
if (count <= 0) {
clearInterval(timer);
displayRandomNumber();
}
}, 1000);
}
*/
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() {
let randomNumber = Math.floor(Math.random() * (upperBound - lowerBound + 1)) + lowerBound;
document.getElementById('page2').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.addEventListener('keydown', handleKeyDown);
}
*/
function displayRandomNumber() {
document.getElementById('page2').style.display = 'block'; // Ensure page2 is displayed
let randomNumber = Math.floor(Math.random() * (upperBound - lowerBound + 1)) + lowerBound;
document.getElementById('page2').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.addEventListener('keydown', handleKeyDown); // Add event listener
}
/*
function handleKeyDown(event) {
if (event.key === ' ') {
spacebarDownTime = new Date().getTime();
document.addEventListener('keyup', handleKeyUp);
}
}
*/
function handleKeyDown(event) {
if (event.key === ' ') {
spacebarDownTime = new Date().getTime(); // Record time of key press
document.addEventListener('keyup', handleKeyUp); // Add keyup listener
}
}
/*
function handleKeyUp(event) {
document.removeEventListener('keydown', handleKeyDown);
if (event.key === ' ') {
spacebarUpTime = new Date().getTime();
let duration = spacebarUpTime - spacebarDownTime;
if (duration > 0) { // Only save duration if it's a positive value
saveData(trialNumber, spacebarDownTime);
saveData(trialNumber, spacebarUpTime);
}
nextTrial();
document.removeEventListener('keyup', handleKeyUp);
}
}
*/
function handleKeyUp(event) {
if (event.key === ' ') {
spacebarUpTime = new Date().getTime(); // Record time of key release
let duration = spacebarUpTime - spacebarDownTime;
if (duration > 0) {
saveData(trialNumber, duration);
}
nextTrial(); // Move to the next trial
document.removeEventListener('keyup', handleKeyUp); // Remove keyup listener
}
}
function saveData(trialNumber, duration) {
csvContent += `${trialNumber},${duration/1000} seconds.n`;
}
/*
function nextTrial() {
trialNumber++;
if (trialNumber <= trialsLeft) {
//displayCountdown();
startCountdown();
} else {
displayExportButton();
}
}
*/
function nextTrial() {
trialNumber++;
if (trialNumber <= trialsLeft) {
displayCountdown(); // Correct function call to start countdown
} else {
displayExportButton(); // Show export options if trials are completed
}
}
/*
function displayCountdown() {
document.getElementById('page2').style.display = 'none';
document.getElementById('page1').style.display = 'none';
document.getElementById('countdown').textContent = '5';
document.getElementById('page2').style.display = 'block';
startCountdown();
}
*/
function displayCountdown() {
document.getElementById('page1').style.display = 'none'; // Hide page1
document.getElementById('page2').style.display = 'block'; // Ensure page2 is visible
startCountdown(); // Initiate countdown
}
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(); // Reload the page to reset everything
}
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);
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;
}
<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;">
<h2>Countdown</h2>
<div id="countdown">5</div>
</div>
<div id="exportContainer" style="display: none;">
<button id="exportBtn">Export Data</button>
<br>
<button id="resetBtn">Reset</button>
</div>
</div>
2
Answers
This is throwing an error after the the first time because of the following function:
When you redefine the
innerHTML
of page2 you remove this element<div id="countdown">5</div>
from the page. Therefore, this line:let countdownElement = document.getElementById('countdown');
can’t find any element and this line:countdownElement.textContent = 5;
throws the error you described:index.html:92 Uncaught TypeError: Cannot set properties of null (setting 'textContent')
I’m not sure if the rest of the code is correct, but fixing this will guide you in the right direction.
Update the content instead of replacing it. The div with the ID="page2" disappears when you update the innerHTML of page2
Have a page3 and update content and toggle when needed
Additionally only have ONE eventListener per event type on the page
MOVE
document.addEventListener('keydown', handleKeyDown);
to outside the function. You need this ONCE per page load so you can deletedocument.removeEventListener('keyup', handleKeyUp); // Remove keyup listener
tooSame for
document.addEventListener('keyup', handleKeyUp); // Add keyup listener