Quantcast
Channel: Recent Questions - Stack Overflow
Viewing all articles
Browse latest Browse all 15491

Total times in a dynamic table is not calculating correctly

$
0
0

Good afternoon,I have a simple HTML page that run batch stopwatches, and you can log athlete's bib numbers accordingly to log split times. However, my total time is calculated wrong, as it add to the current time every time. I am not an expert in coding, so need some help finding the issue and fix it. WIll really appreciate if someone can help me

Here is my code:

<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=0.8"><title>XCO Timer</title><style>    .stopwatch {      text-align: center;      margin-bottom: 20px;    }    table {      border-collapse: collapse;    }    th, td {      border: 1px solid black;      padding: 8px;      text-align: left;    }        .stopwatch {<!-- font-family: Arial, Helvetica, sans-serif; -->          border-collapse: collapse;          width: 100%;        }        .stopwatch td, .stopwatch th {          border: 1px solid #ddd;          padding: 8px;        }        .stopwatch tr:nth-child(even){background-color: #f2f2f2;}        .stopwatch tr:hover {background-color: #ddd;}        .stopwatch th {          padding-top: 12px;          padding-bottom: 12px;          text-align: left;          background-color: #04AA6D;          color: white;        }    .stopwatch th:nth-child(1) {      width: 50px;    }    .stopwatch th:nth-child(2) {      width: 200px;    }    .stopwatch th:nth-child(n+3) {      width: 80px;    }    #userInput {      text-align: center;      margin-bottom: 20px;    }    #userInput input[type="text"], #userInput button {      margin: 5px;    }    #userInput input[type="text"] {      padding: 5px;      border: 1px solid #ccc;      border-radius: 3px;    }    .green-button {      background-color: #04AA6D;      color: white;      border: none;      padding: 10px 20px;      text-align: center;      text-decoration: none;      display: inline-block;      font-size: 16px;      margin: 2px 2px;      cursor: pointer;      border-radius: 2px;    }    .blue-button {      background-color: #6495ED;      color: white;      border: none;      padding: 10px 20px;      text-align: center;      text-decoration: none;      display: inline-block;      font-size: 16px;      margin: 2px 2px;      cursor: pointer;      border-radius: 2px;    }           .red-button {      background-color: #E97451;      color: white;      border: none;      padding: 10px 20px;      text-align: center;      text-decoration: none;      display: inline-block;      font-size: 16px;      margin: 2px 2px;      cursor: pointer;      border-radius: 2px;    }       #athleteTable {  border-collapse: collapse;  width: 100%;}#athleteTable th, #athleteTable td {  border: 1px solid #ddd;  padding: 8px;}#athleteTable tbody tr:nth-child(even) {  background-color: #f2f2f2;}#athleteTable tbody tr:hover {  background-color: #ddd;}#athleteTable th {  padding-top: 12px;  padding-bottom: 12px;  text-align: left;  background-color: #04AA6D;  color: white;}#athleteTable th:nth-child(1) {  width: 30%; /* Adjust width as needed */}#athleteTable th:nth-child(n+2) {  width: 10%; /* Adjust width as needed */}#timerA,#timer${batch} {  font-weight: bold;  font-size: 24px; /* Adjust font size as needed */}#timerContainer {  font-weight: bold;  font-size: 24px; /* Adjust font size as needed */  display: inline-block; /* Ensure the container only takes up as much space as needed */}p.solid {border-style: solid;}#userInput input[type="text"] {  padding: 10px 20px; /* Adjust padding as needed */  border: none;  background-color: #f2f2f2; /* Match the button's background color */  color: black; /* Match the button's text color */  font-size: 16px; /* Match the button's font size */  margin: 5px; /* Match the button's margin */  cursor: pointer;  border-radius: 2px; /* Match the button's border radius */      width: 100px;}</style></head><body><div id="userInput"><input type="text" id="bibNumber" inputmode="numeric" pattern="\d*" placeholder="Enter Bib No" title="Please enter numeric value only" onkeyup="handleKeyPress(event)"><button class="green-button" onclick="recordBibNumber()">Enter</button></div><div id="stopwatches"><!-- Batch A Stopwatch --><div class="stopwatch" id="batchA"><h2>Batch A Stopwatch</h2><div id="timerContainer"><p class="solid" id="timerA">00:00:00.00</p></div><br><button class="green-button" onclick="startTimer('A')">Start</button><button class="green-button" onclick="stopTimer('A')">Stop</button><button class="green-button" onclick="resetTimer('A')">Reset</button><table id="tableA"><thead><tr><th>Bib No</th><th>Name</th><th>Total Laps</th><th>Total Time</th>                 <!-- Dynamic lap headers --><th id="lapHeadersA"></th></tr></thead><tbody id="tbodyA"></tbody></table></div></div><button class="blue-button" onclick="startNewBatch()">Add New Batch</button><!-- Form for adding athlete information --><h2>Add Athlete Information</h2><form id="athleteForm"><label for="name">Name:</label><input type="text" id="name" name="name" required autocomplete="off"><br><br><label for="bibNo">Bib No:</label><input type="text" id="bibNoInput" name="bibNo" pattern="\d*" title="Please enter numeric value only" required autocomplete="off"><br><br><label for="batch">Batch:</label><select id="batch" name="batch" required autocomplete="off"><option value="">Select batch</option><!-- Add options for batch A to Z --><script>      for (let i = 65; i <= 90; i++) {        let letter = String.fromCharCode(i);        document.write(`<option value="${letter}">${letter}</option>`);      }</script></select><br><br><label for="gender">Gender:</label><select id="gender" name="gender" required autocomplete="off"><option value="Male">Male</option><option value="Female">Female</option></select><br><br><label for="ageCategory">Age Category:</label><select id="ageCategory" name="ageCategory" required autocomplete="off"><option value="Nipper">Nipper (8-10 years old)</option><option value="Sprog">Sprog (11-12 years old)</option><option value="Youth">Youth (13-14 years old)</option><option value="Junior">Junior (15-16 years old)</option><option value="Under23">Under 23 (17-22 years old)</option><option value="Elite">Elite (23-29 years old)</option><option value="SubVeteran">Sub-Veteran (30-39 years old)</option><option value="Veteran">Veteran (40-49 years old)</option><option value="Master">Master (50-59 years old)</option><option value="GrandMaster">Grand Master (60-69 years old)</option><option value="GreatGrandMaster">Great Grand Master (70+ years old)</option></select><br><br><button class="green-button" type="submit">Add Athlete</button><button class="red-button" onclick="clearTable()">Clear Table</button></form><!-- Table to display athlete information --><h2>Athlete Information</h2><table id="athleteTable"><thead><tr><th>Name</th><th>Bib No</th><th>Batch</th><th>Gender</th><th>Age Category</th><th>Edit</th></tr></thead><tbody id="athleteTableBody"></tbody></table><script>   let athletes = [];  // Add default athletes for testing  athletes.push({ name: "John", bibNo: "1", batch: "A", gender: "Male", ageCategory: "Sprogs" });  athletes.push({ name: "Peter", bibNo: "2", batch: "B", gender: "Male", ageCategory: "Sprogs" });  athletes.push({ name: "Gids", bibNo: "3", batch: "B", gender: "Male", ageCategory: "Sprogs" });  athletes.push({ name: "Mack", bibNo: "4", batch: "C", gender: "Male", ageCategory: "Sprogs" });  let editIndex = -1;  // Render athlete table after adding default athletes  renderAthleteTable();document.getElementById('athleteForm').addEventListener('submit', function(event) {  event.preventDefault();  let name = document.getElementById('name').value.trim();  let bibNo = document.getElementById('bibNoInput').value.trim();  let batch = document.getElementById('batch').value.trim(); // Retrieve selected batch  let gender = document.getElementById('gender').value;  let ageCategory = document.getElementById('ageCategory').value;  if (editIndex === -1) {    // Check if the bib number already exists in the table    if (isBibNumberExist(bibNo)) {      alert("Bib number already exists in the athlete information.");      return;    }    athletes.push({ name, bibNo, batch, gender, ageCategory }); // Include batch information  } else {    athletes[editIndex] = { name, bibNo, batch, gender, ageCategory };    editIndex = -1;  }  renderAthleteTable();  document.getElementById('name').value = ''; // Clear the athlete name field  document.getElementById('bibNoInput').value = ''; // Clear the bib number field});    // Add this script to set the selected options based on the last selected values    document.addEventListener('DOMContentLoaded', function() {      let lastBatch = localStorage.getItem('lastBatch');      let lastAgeCategory = localStorage.getItem('lastAgeCategory');      if (lastBatch) {        document.getElementById('batch').value = lastBatch;      }      if (lastAgeCategory) {        document.getElementById('ageCategory').value = lastAgeCategory;      }    });    function renderAthleteTable() {      let tableBody = document.getElementById('athleteTableBody');      tableBody.innerHTML = '';      athletes.forEach(function(athlete, index) {        let row = tableBody.insertRow();        row.insertCell().innerText = athlete.name;        row.insertCell().innerText = athlete.bibNo;        row.insertCell().innerText = athlete.batch;        row.insertCell().innerText = athlete.gender;        row.insertCell().innerText = athlete.ageCategory;        let editCell = row.insertCell();        let editButton = document.createElement('button');        editButton.innerText = 'Edit';        editButton.onclick = function() { editAthlete(index); };        editCell.appendChild(editButton);      });    }    function editAthlete(index) {      editIndex = index;      let athlete = athletes[index];      document.getElementById('name').value = athlete.name;      document.getElementById('bibNoInput').value = athlete.bibNo;      document.getElementById('batch').value = athlete.batch;      document.getElementById('gender').value = athlete.gender;      document.getElementById('ageCategory').value = athlete.ageCategory;    }function recordBibNumber() {  let bibNumberInput = document.getElementById("bibNumber");  let bibNumber = bibNumberInput.value.trim();  if (bibNumber !== "") {    let batch = findBatch(bibNumber);    if (batch !== null) {      logBatchTime(batch);    } else {      // Add the bib number to Batch A      batch = 'A';      logBatchTime(batch);    }    // Update dynamic lap headers after logging the batch time    updateDynamicLapHeaders('A'); // Always update the lap headers for Batch A    bibNumberInput.value = "";    bibNumberInput.focus();  } else {    alert("Please enter a bib number.");  }}  function isBibNumberExist(bibNumber) {    // Check if the bib number already exists in the table    return athletes.some(athlete => athlete.bibNo === bibNumber);  }  // Function to find the batch of the participant based on bib number  function findBatch(bibNumber) {    // Find the athlete with the provided bib number    let athlete = athletes.find(athlete => athlete.bibNo === bibNumber);    return athlete ? athlete.batch : null; // Return batch if athlete is found, otherwise return null  }  function calculateTotalTime(batch, bibNo) {  let splitTimesArray = splitTimes[batch][bibNo];  let totalTimeMillis = 0;  // Sum up all the split times  for (let i = 0; i < splitTimesArray.length; i++) {    totalTimeMillis += timeStringToMillis(splitTimesArray[i]);  }  // Convert the total time back to the time string format  return millisToTimeString(totalTimeMillis);}// Update the logBatchTime function to add data-time attribute to cellsfunction logBatchTime(batch) {  // Get the current time from the corresponding stopwatch  let currentTime = document.getElementById('timer'+ batch).innerText;  let bibNo = document.getElementById('bibNumber').value.trim(); // Get bib number  let name = getNameFromBibNo(bibNo); // Get name from bib number  // Initialize an array to store split times for the current batch  if (!splitTimes[batch]) {    splitTimes[batch] = {};  }  // Initialize an array to store split times for the current participant  if (!splitTimes[batch][bibNo]) {    splitTimes[batch][bibNo] = [];  }  // Add current split time to the array  splitTimes[batch][bibNo].push(currentTime);  // Find the table body for the corresponding batch  let tbody = document.getElementById("tbody" + batch);  // Find existing row for the bib number  let existingRow = findRowByBibNo(tbody, bibNo);  if (!existingRow) {    // If the row doesn't exist, create a new row    let newRow = tbody.insertRow();    newRow.insertCell().innerText = bibNo;    newRow.insertCell().innerText = name;    newRow.insertCell(); // Add an empty cell for "Laps done"  newRow.insertCell(); // Add an empty cell for "Total Time"    existingRow = newRow;  }  // Add split time to the existing row  let lapIndex = getLapIndex(existingRow);  let splitTime = calculateSplitTime(batch, bibNo, lapIndex);  let timeCell = existingRow.insertCell();  timeCell.innerText = splitTime; // Add split time to the new cell    let totalTimeCell = existingRow.cells[3];    let totalTime = calculateTotalTime(batch, bibNo);    totalTimeCell.innerText = totalTime;  // Add data-time attribute to the cell  timeCell.setAttribute('data-time', 'true');  // Increment lap index for the next lap time  lapIndex++;  // Update the lap index attribute on the row for the next lap time  existingRow.setAttribute('data-lap-index', lapIndex);  // Update dynamic lap headers  updateDynamicLapHeaders(batch, lapIndex);  calculateLapTimes(batch, bibNo);}function updateDynamicLapHeaders(batch) {  // Find the table header row for lap headers in the corresponding batch  let headerRow = document.querySelector("#table" + batch +" thead tr");  // Clear existing lap headers  headerRow.innerHTML = "";  // Add static headers (Bib No, Name, Total Time)  let bibNoHeader = document.createElement("th");  bibNoHeader.textContent = "Bib No";  headerRow.appendChild(bibNoHeader);  let nameHeader = document.createElement("th");  nameHeader.textContent = "Name";  headerRow.appendChild(nameHeader);      let totalLapsHeader = document.createElement("th"); // Create new header element  totalLapsHeader.textContent = "Total Laps"; // Set its text content  headerRow.appendChild(totalLapsHeader); // Append it to the header row   let totalTimeHeader = document.createElement("th");    totalTimeHeader.textContent = "Total Time";    headerRow.appendChild(totalTimeHeader);  // Initialize an array to store the number of laps for each participant  let maxLapsArray = [];  // Iterate over all split times for the current batch  if (splitTimes[batch]) {    Object.values(splitTimes[batch]).forEach(splitTimeArray => {      maxLapsArray.push(splitTimeArray.length);    });  }  // Find the maximum number of laps across all participants  let maxLaps = Math.max(...maxLapsArray);  // Add dynamic lap headers based on maxLaps  for (let i = 1; i <= maxLaps; i++) {    let lapHeader = document.createElement("th");    lapHeader.textContent = "Lap " + i;    headerRow.appendChild(lapHeader);  }}function calculateLapTimes(batch, bibNo) {    const lapStartTime = document.querySelector(`#tbody${batch} tr[data-bib="${bibNo}"] td:nth-child(4)`);    const lapEndTime = document.querySelector(`#tbody${batch} tr[data-bib="${bibNo}"] td:nth-child(5)`);    if (lapStartTime && lapEndTime) {        const startTime = lapStartTime.innerText.trim();        const endTime = lapEndTime.innerText.trim();        // Your calculation logic here    } else {        console.error("Unable to find lap start or end time for batch " + batch +", bibNo " + bibNo);    }}function calculateSplitTime(batch, bibNo, lapIndex) {  let previousSplitTime = '00:00:00.00';  // Retrieve the previous split time from the array  if (splitTimes[batch] && splitTimes[batch][bibNo] && lapIndex > 0) {    previousSplitTime = splitTimes[batch][bibNo][lapIndex - 1];  }  let currentTime = document.getElementById('timer'+ batch).innerText;  return calculateTimeDifference(previousSplitTime, currentTime);}function calculateTimeDifference(previousTime, currentTime) {  // Convert time strings to milliseconds and calculate the difference  let previousMillis = timeStringToMillis(previousTime);  let currentMillis = timeStringToMillis(currentTime);  console.log("Previous Milliseconds:", previousMillis); // Log previous milliseconds  console.log("Current Milliseconds:", currentMillis); // Log current milliseconds  let difference = currentMillis - previousMillis;  console.log("Difference (Milliseconds):", difference); // Log difference in milliseconds  // Convert difference back to time string format  let splitTime = millisToTimeString(difference);  console.log("Split Time:", splitTime); // Log split time  return splitTime;}    function timeStringToMillis(timeString) {      let parts = timeString.split(':');      let hours = parseInt(parts[0]) * 3600000;      let minutes = parseInt(parts[1]) * 60000;      let seconds = parseFloat(parts[2]) * 1000;      return hours + minutes + seconds;    }    function millisToTimeString(milliseconds) {      let hours = Math.floor(milliseconds / 3600000);      milliseconds %= 3600000;      let minutes = Math.floor(milliseconds / 60000);      milliseconds %= 60000;      let seconds = Math.floor(milliseconds / 1000);      milliseconds %= 1000;      return pad(hours) +':'+ pad(minutes) +':'+ pad(seconds) +'.'+ pad(Math.floor(milliseconds / 10));    }  function findRowByBibNo(tbody, bibNo) {    // Find existing row for the bib number    for (let i = 0; i < tbody.rows.length; i++) {      let row = tbody.rows[i];      if (row.cells[0].innerText === bibNo) {        return row;      }    }    return null; // Return null if row is not found  }  function getLapIndex(row) {    // Get the lap index from the data attribute or default to 0    let lapIndex = parseInt(row.getAttribute('data-lap-index')) || 0;    return lapIndex;  }  function getNameFromBibNo(bibNo) {    // Find the athlete with the provided bib number    let athlete = athletes.find(athlete => athlete.bibNo === bibNo);    return athlete ? athlete.name : ''; // Return name if athlete is found, otherwise return empty string  }  let timers = {};  let isRunning = {};let splitTimes = {};  function startTimer(batch) {    if (!isRunning[batch]) {      let startTime = Date.now();      timers[batch] = setInterval(function() {        let elapsedTime = Date.now() - startTime;        updateTimer(batch, elapsedTime);      }, 10);      isRunning[batch] = true;    }  }  function stopTimer(batch) {    clearInterval(timers[batch]);    isRunning[batch] = false;  }  function resetTimer(batch) {    stopTimer(batch);    document.getElementById('timer'+ batch).innerText = '00:00:00.00';  }  function updateTimer(batch, elapsedTime) {    let milliseconds = Math.floor((elapsedTime % 1000) / 10);    let seconds = Math.floor((elapsedTime / 1000) % 60);    let minutes = Math.floor((elapsedTime / (1000 * 60)) % 60);    let hours = Math.floor((elapsedTime / (1000 * 60 * 60)) % 24);    document.getElementById('timer'+ batch).innerText =       pad(hours) +':'+ pad(minutes) +':'+ pad(seconds) +'.'+ pad(milliseconds);  }  function pad(number) {    if (number < 10) {      return '0'+ number;    }    return number;  }  let nextBatchIndex = 1;function handleKeyPress(event) {  if (event.key === "Enter") {    recordBibNumber();  }}    function clearTable() {      // Prompt the user for confirmation      let confirmation = confirm("Are you sure you want to clear the table?");      // If the user confirms, clear the table      if (confirmation) {        let tableBodies = document.querySelectorAll("tbody");        tableBodies.forEach(tableBody => {          tableBody.innerHTML = ""; // Clear the table body content        });      }    } document.addEventListener('DOMContentLoaded', function() {    // Get all tables within the stopwatches section    let tables = document.querySelectorAll('#stopwatches table');    // Iterate over each table    tables.forEach(function(table) {      // Get the table body of the current table      let tbody = table.querySelector('tbody');      // Add event listener to the table body      tbody.addEventListener('dblclick', function(event) {        // Check if the double-clicked element is a cell        if (event.target.tagName === 'TD') {          // Make the cell editable          let cell = event.target;          cell.contentEditable = true;          // Add a border to indicate editing          cell.style.border = '1px solid #ccc';          // Focus on the cell to allow immediate editing          cell.focus();        }      });      // Add touch event listeners to enable long press for mobile devices      tbody.addEventListener('touchstart', function(event) {        // Set a timeout to detect long press (500 milliseconds)        touchTimer = setTimeout(function() {          // Check if the touched element is a cell          if (event.target.tagName === 'TD') {            // Make the cell editable            let cell = event.target;            cell.contentEditable = true;            // Add a border to indicate editing            cell.style.border = '1px solid #ccc';            // Focus on the cell to allow immediate editing            cell.focus();          }        }, 500); // Adjust the timeout duration as needed      });      // Clear the touch timer if the touch ends before the timeout      tbody.addEventListener('touchend', function() {        clearTimeout(touchTimer);      });      // Add event listener to save edited content when blurring from cell      tbody.addEventListener('blur', function(event) {        // Check if the blurred element is a cell        if (event.target.tagName === 'TD') {          // Save the edited content          saveEditedContent(event.target);        }      });      // Add event listener to save edited content when pressing Enter      tbody.addEventListener('keypress', function(event) {        if (event.key === 'Enter') {          // Check if the focused element is a cell          if (document.activeElement.tagName === 'TD') {            // Prevent the default behavior of pressing Enter in a contenteditable element            event.preventDefault();            // Save the edited content            saveEditedContent(document.activeElement);          }        }      });    });  });  function saveEditedContent(cell) {    // Remove the border to indicate the end of editing    cell.style.border = 'none';    // Make the cell non-editable    cell.contentEditable = false;  }  function startNewBatch() {    let batchName = String.fromCharCode(65 + nextBatchIndex); // Convert index to character code (A=65, B=66, C=67, ...)    createNewBatch(batchName);    nextBatchIndex++;  }function createNewBatch(batch) {  let newDiv = document.createElement('div');  newDiv.classList.add('stopwatch');  newDiv.id = 'batch'+ batch;  newDiv.innerHTML = `<h2>Batch ${batch} Stopwatch</h2><div id="timerContainer"><p class="solid" id="timer${batch}">00:00:00.00</p></div><br><button class="green-button" onclick="startTimer('${batch}')">Start</button><button class="green-button" onclick="stopTimer('${batch}')">Stop</button><button class="green-button" onclick="resetTimer('${batch}')">Reset</button><table id="table${batch}"><thead><tr><th>Bib No</th><th>Name</th><th>Total Laps</th><th>Total Time</th>      <!-- Dynamic lap headers --><th id="lapHeaders${batch}"></th></tr></thead><tbody id="tbody${batch}"></tbody></table>  `;  document.getElementById('stopwatches').appendChild(newDiv);  // Add event listeners to the newly created table  let newTable = newDiv.querySelector('table');  let newTbody = newTable.querySelector('tbody');  // Add event listener to the table body  newTbody.addEventListener('dblclick', function(event) {    // Check if the double-clicked element is a cell    if (event.target.tagName === 'TD') {      // Make the cell editable      let cell = event.target;      cell.contentEditable = true;      // Add a border to indicate editing      cell.style.border = '1px solid #ccc';      // Focus on the cell to allow immediate editing      cell.focus();    }  });  // Add event listener to save edited content when blurring from cell  newTbody.addEventListener('blur', function(event) {    // Check if the blurred element is a cell    if (event.target.tagName === 'TD') {      // Save the edited content      saveEditedContent(event.target);    }  });  // Add event listener to save edited content when pressing Enter  newTbody.addEventListener('keypress', function(event) {    if (event.key === 'Enter') {      // Check if the focused element is a cell      if (document.activeElement.tagName === 'TD') {        // Prevent the default behavior of pressing Enter in a contenteditable element        event.preventDefault();        // Save the edited content        saveEditedContent(document.activeElement);      }    }  });}</script></body></html>

I am not an expert, and doing this with the help of ChatGPT. But it have its limitations. Cannot get the sum of each batch to calculate correctly.


Viewing all articles
Browse latest Browse all 15491

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>