AJAX causing countdown timer dysfunction

I have been working on creating a JavaScript counter and so far it's working well. However, I recently ran into an issue when trying to use AJAX to retrieve the countdown time. It's strange because it works fine in my original file but not with a PHP file called by AJAX.

This is the link that works perfectly: https://jsfiddle.net/6kvp25vv/

I'm puzzled about what could be causing the problem. Here is the HTML page:

<button onclick="upgrade('meat_max')" id="up_meat_max">+</button>

Clicking the button triggers a function in this JS file which sends a GET request to upgrade.php:

function upgrade(building) {
  var file = 'upgrade.php?building=' + building;
  ajax(file, function(response) {
    document.getElementById('construction').innerHTML += response;
  })
}

function ajax(file, fn) {
  var xmlhttp = new XMLHttpRequest();
  xmlhttp.onreadystatechange = function() {
    if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
      fn(xmlhttp.responseText);
    }
  };
  xmlhttp.open('GET', file, true);
  xmlhttp.send();
}

Below is the code from upgrade.php (the variables sent to this file via AJAX are for testing purposes only):

<div class="time">Time: <span id="timer">?</span></div>
  var hour = 2;
  var minute = 46;
  var second = 45;

  // function to create a counter
  function clockIt() {
    function clockO(digit) {
      if(digit<10) {
        return '0';
      } else {
        return '';
      }
    }

    document.getElementById('timer').textContent = hour + ':' + clockO(minute) + minute + ':' + clockO(second) + second;
    if(second>0) {
      second -= 1;
    } else if(minute>0) {
      minute -= 1;
      second += 59;
    } else if(hour>0) {
      hour -= 1;
      minute += 59;
    }
  }

  // runs the function every second
  clockIt();
  setInterval(function (){clockIt()}, 1000);

Answer №1

When using innerHTML to load scripts via ajax, keep in mind that the scripts won't be executed. In this scenario, a solution would be to return a JSON encoded string containing the necessary variables. You can then have a function in your main script ready to handle this provided data, making it easier to pass parameters along with the ajax response.

To decode a JSON string, use the following:

obj = JSON.parse(jsonString);

Here's an example of an Ajax JSON response string:

{"time": {"hour":2, "minute":46, "second": 45}, "html": "<div class=\"time\">Time: <span id=\"timer\"></span></div>"}

An upgraded version of the function:

function upgrade(building) {
  var file = 'upgrade.php?building=' + building;
  ajax(file, function(response) {
    obj = JSON.parse(response);
    time = obj.time;
    document.getElementById('construction').innerHTML += obj.html;
    startCountdown(time.hour, time.minute, time.second);
  })
}

The new function to start the countdown is as follows:

function startCountdown(hour, minute, second) {

    // Function for counting down
    function clockIt() {
      function clockO(digit) {
        if(digit<10) {
          return '0';
        } else {
          return '';
        }
     }

    document.getElementById('timer').textContent = hour + ':' +     clockO(minute) + minute + ':' + clockO(second) + second;
     if(second>0) {
        second -= 1;
     } else if(minute>0) {
       minute -= 1;
       second += 59;
     } else if(hour>0) {
       hour -= 1;
       minute += 59;
     }
 }

 // Run the function every second
 clockIt();
 setInterval(function (){clockIt()}, 1000);
}

Answer №2

One issue I encountered was with the accuracy of the countdown timer. Setting an interval of 1000 milliseconds doesn't guarantee a precise timing of 1000 milliseconds. The timer loop triggers as soon as it reaches the specified interval after 1000 milliseconds have elapsed, leading to potential delays over time. For accurate timing, it's advisable to store the initial settings and calculate the duration from when the countdown started to the current time. Check out the code below that utilizes the internal clock for more precision in timing. Once you have the total number of seconds, converting it to hours, minutes, and seconds is straightforward by dividing by 3600 for hours, and using division and modulo arithmetic for minutes and seconds.

For more information, visit https://www.sitepoint.com/creating-accurate-timers-in-javascript/

Also, refer to this link for insights on creating an accurate timer in JavaScript: How to create an accurate timer in javascript?

<!DOCTYPE html />
<html>

<head>
  <meta encoding="UTF-8" />
  <title>Testing XMLHttpRequest</title>
  <script>
    var request;
    var button1;
    var display1;
    var display2;
    var display3;
    var start;
    var counter;

    function second() {
      display2.value = display2.value + "\r\nreadyState=" + request.readyState + " status=" + request.status + "\r\n";;
      if (request.readyState == 4 && request.status == 200) {
        display1.value = display1.value + request.responseText + "\r\n";
      }
    }

    function first() {
      display2.value = display2.value + "\r\n" +
        "Starting page     \r\n";
      request = new XMLHttpRequest();
      request.onreadystatechange = second;
      var file = "http://localhost:80/";
      request.open('GET', file, true);
      request.send();
      setInterval(timed, 1000);
    }

    function starter() {
      display1 = document.getElementById("display1");
      display2 = document.getElementById("display2");
      display3 = document.getElementById("display3");
      button1 = document.getElementById("button1");
      button1.onclick = first;
      start = new Date();
      counter = 60;
    }

    function timed() {
      var duration = (start.getTime() - new Date().getTime()) / 1000.0;
      display3.value = (duration + counter).toFixed(0);
    }

    window.onload = starter;
  </script>

</head>

<body>
  <form>
    <p>
      <input type="button" id="button1" value="Start" />Timer:
      <input type="text" readonly="readonly" id="display3" />
    </p>
    <p>Status:
      <textarea rows="5" cols="30" id="display2"></textarea>
    </p>
    <p>Response:
      <textarea rows="60" cols="80" id="display1"></textarea>
    </p>
  </form>
</body>

</html>

Answer №3

Discovered a solution to handle delays by utilizing the original version :

function upgrade(building) {
  var file = 'upgrade.php?building=' + building;
  ajax(file, function(response) {
    var obj = JSON.parse(response);
    var time = obj.time;
    document.getElementById('construction').innerHTML += obj.html;
    run_clockIt(time.hour, time.minute, time.second);
  })
}

// AJAX launcher for general use
function ajax(file, fn) {
  var xmlhttp = new XMLHttpRequest();
  xmlhttp.onreadystatechange = function() {
    if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
      fn(xmlhttp.responseText);
    }
  };
  xmlhttp.open('GET', file, true);
  xmlhttp.send();
}


// countdown timer with adjustments to manage delay
function clockIt(hour, minute, second, finished, nbOfLoop) {

  // run delay correction every 5 seconds
  if(nbOfLoop%5 == 0) {
    var actualTimeLeft = adjustClock(finished);
    minute = actualTimeLeft[0];
    second = actualTimeLeft[1];
  }
  nbOfLoop += 1;

  // add a "0" before digit if needed
  function clockO(digit) {
    if(digit<10) {
      return '0';
    } else {
      return '';
    }
  }

  document.getElementById('timer').textContent = hour + ':' + clockO(minute) + minute + ':' + clockO(second) + second;

  // update displayed timer
  if(second>0) {
    second -= 1;
  } else if(minute>0) {
    minute -= 1;
    second += 59;
  } else if(hour>0) {
    hour -= 1;
    minute += 59;
  }

  // wait 1 sec before launching the next iteration
  setTimeout(function() {
    clockIt(hour, minute, second, finished, nbOfLoop);
  }, 1000);
}

// first-time execution of the function
function run_clockIt(hour, minute, second) {
  var finished = new Date();
  finished.setUTCHours(finished.getUTCHours() + hour);
  finished.setUTCMinutes(finished.getUTCMinutes() + minute);
  finished.setUTCSeconds(finished.getUTCSeconds() + second);

  clockIt(hour, minute, second, finished, 1);
}

function adjustClock(finished) {
  var now = new Date();
  var diff = new Date(Math.abs(now - finished));

  return [diff.getUTCMinutes(), diff.getUTCSeconds()];
}

This approach ensures a smooth countdown timer without any lagging, allowing you to set the interval at which the adjust function corrects the timer.

This PHP file prepares the JSON object (credits to @Miguel) :

<?php
header('Content-Type: application/json');

// obtain variables from AJAX call
$building = $_REQUEST['building'];

// process variable with database...

// prepare JSON object
$jsonData = '
{
  "time":{"hour":2, "minute":46, "second": 45},
  "html": "<div class=\"time\">Time: <span id=\"timer\"></span></div>"
}
';

echo $jsonData;

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Guide on incorporating amcharts into a Nuxt application

I am attempting to integrate amCharts into my Nuxt project. Within the svg-map.vue component, I have added the following code snippet: head() { return { script: [ { src: 'js/amcharts/core.js' } ] }; }, However, I e ...

typescript - transforming text into numerical values

newbalance = (Number(this.balance)) + (Number(this.pastAmount)); The result for my newbalance calculation is coming back as undefined, even though this.balance is 34 and this.pastAmount is 23. I've set this up in the controller and I'm trying t ...

Get only the rows that have been altered in DataTables

On a page I have set up with two columns, one containing input fields and the other checkboxes. Imagine this scenario: I update an entry in one of the input fields and uncheck a checkbox in a different row. Currently, when I click the Save button and exec ...

What occurs to the bound event once the DOM element disappears?

What happens if I attach an event handler to a DOM element and then remove the DOM element? Do I need to unbind the event handlers? <div id="el1"> <span id="c">Click Me!</span> </div> <span id="note">Note...</span> ...

The formatting of the datepicker-popup is malfunctioning when the initial value is set within the scope

I have implemented the Angular UI bootstrap date picker popup using a custom directive on Plunker (http://plnkr.co/edit/053VJYm1MpZUiKwFTfrT?p=preview): //Module var userModule = angular.module("userModule",['ui.bootstrap']); //Controller userM ...

I'm having trouble locating the source of the popstate loop that is generating numerous history entries

I am currently working on a project to create a dynamic webpage where the content of the main div gets replaced when certain navigation links are clicked. I have successfully implemented the pushstate function to update the div content and change the URL a ...

Updating a JSON file with new object using node.js

Currently, I am attempting to insert a single object into an extensive JSON file. My approach involves parsing the entire file using FS and JSON.Parse, adding the new JSON object in memory, and then rewriting the file. While I am aware of FS's append ...

Error: The function jquery_1.default is not recognized by webpack

I am encountering an issue with using external imports of jQuery in webpack. Despite trying different import syntaxes such as import $ from 'jquery', import * as $ from 'jquery, and const $ = require('jquery'), I continue to receiv ...

Removing elements from an array with reduced speed

Within my array, I am looking to remove N elements from the beginning. For instance, if my array contains 1 million floating point elements and I need to remove the first 500,000, I have two options. The first is to iterate and call the shift method 500,0 ...

Steps for updating the text of a dropdown button to display the name of the selected item:

I have been searching for a clear answer to this question, but I couldn't find one. So here I am seeking help: I have a button that functions as a dropdown menu: <body> <div class="dropdown"> <button onclick="changeDropdownVisibilit ...

What measures can I take to store data for a website user?

Check out my code on this website: If not, here is the HTML and JavaScript snippets: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8> <meta name="viewport" content="width=device-width, initial-scale=1.0"> ...

Enhance user experience with a dynamic Bootstrap combo box that updates based on

I am currently facing an issue with the bootstrap combobox plugin. I am having trouble changing the selection and sending that information from the view to the controller. $('#MyCombo').on('change', function () { var data = $(this) ...

Utilize the cube function in JavaScript to zoom in on the cube automatically when the page loads

I have the code below. When the page loads, I would like the cube to zoom out and stop. I am new to 3js and have no idea how to achieve this. I want the cube to start small and then grow into a larger cube before stopping. <script> //var re ...

How can I iterate through JSON data and showcase it on an HTML page?

I am in the process of developing a weather application using The Weather API. So far, I have successfully retrieved the necessary data from the JSON and presented it in HTML format. My next goal is to extract hourly weather information from the JSON and ...

The order of items in MongoDB can be maintained when using the $in operator by integrating Async

It's common knowledge that using {$in: {_id: []}} in MongoDB doesn't maintain order. To address this issue, I am considering utilizing Async.js. Let's consider an example: const ids = [3,1,2]; // Initial ids retrieved from aggregation con ...

What is the best way to assign the value of a dropdown menu to a textbox?

Click here for the code snippet <select id="dropdownMenu" name="1" class="form-control"> <option value="policy_no">Policy No</option> <option value="claim_no">Claim No</option> <option value="acc_no">Account No< ...

Ways to set Material UI tabs as active exclusively for particular URLs

Looking for a solution to address a conflict arising from the active tab indicator being located on the right tab. Within my navbar, I have tabs leading to three different routes, two of which are quite similar. <Tabs indicatorColor="primary" ...

Mastering the settimeout function for rotating a cube in three.js: Best practices and tips

I am currently developing a program that will rotate a cube based on quaternion data inputted by the user via a CSV file in an HTML format. The JavaScript code cycles through each quaternion in the dataset, applying them to the cube at regular intervals to ...

Is using JSONP the only choice for an XML API in a jQuery ajax call?

Currently, I am working in jQuery and attempting to make an Ajax call to query an external XML API located on a different domain. Unfortunately, my requests are being blocked due to the cross-domain restrictions. I am curious about alternative options fo ...

Change the left position of the sliding menu in real-time

I am currently designing a website with a sliding menu feature. By default, the menu is set to -370px on the left, displaying only the "MENU" text initially. When a user hovers over the menu, it expands to the right, allowing them to select different menu ...