Search code examples
javascriptevent-handlingtiming

Where does a browser event's timeStamp come from in Javascript?


Event objects passed to event handler callbacks contain an event.timeStamp of when the event occurred, but where is the timestamp produced? Is it something that bubbles up from the underlying OS, or is it generated by the browser?

I'm interested in gauging the reliability of the timestamp and recognise that the later the timestamp is produced the less accurate it's likely to be.

Also, does the way in which the timestamp is produced vary significantly between platforms and browser implementations?

Thanks.


Solution

  • According to the DOM4 specification §4.2, it's assigned when the event is created (you have to scroll down a bit):

    The timeStamp attribute must return the value it was initialized to. When an event is created the attribute must be initialized to the number of milliseconds that have passed since 00:00:00 UTC on 1 January 1970, ignoring leap seconds.

    Which leads to the question of: When is the event created? I can think of three times it might be done:

    1. When the OS notifies the browser

    2. When the JavaScript main UI thread is next able to do anything

    3. When the JavaScript main UI thread is about to dispatch the task related to the event

    Using the snippet below, at least for the click event, it seems to vary by browser:

    • Chrome seems to assign the timeStamp when the JavaScript main UI thread is ready to handle the event (#3 above); this can be significantly after the event actually happened, even if the main JavaScript UI thread has had a chance to do other things in the interim. (For me, this was very surprising.) (Update Sep 2019: Chrome no longer seems to do this.)

    • Chrome and Firefox seem to assign the timeStamp when the event occurs (#1 above), regardless of what the main JavaScript UI thread is doing. This is what I would have expected.

    • IE seems to assign the timeStamp when the JavaScript main UI thread is ready to handle the event (#3 above); this can be significantly after the event actually happened, even if the main JavaScript UI thread has had a chance to do other things in the interim. But I can't rule out the possibility it would assign the timestamp for the third click when the second click is processed (#2), because I can't get it to recognize a click on the third button when things are busy.

    // Sadly, Firefox has a bug (https://bugzilla.mozilla.org/show_bug.cgi?id=77992) where
    // timeStamp is not from The Epoch, it's from system start. So we work relative to the
    // moment you clicked the first button rather than working with nice clean absolute data.
    
    // Sadly, IE11 doesn't like you clicking the second button and then moving over to
    // click the third, but the data from just clicking the second suggests it's more like
    // Chrome than Firefox.
    var start1;
    var start2;
    document.getElementById("start").addEventListener("click", function(e) {
      // Remember this event's timestamp
      start1 = e.timeStamp;
      start2 = 0;
    
      // Start a busy-loop for three seconds, locking up the main JS thread
      busy(3000);
      display("Done with first busy loop");
    }, false);
    
    document.getElementById("then1").addEventListener("click", function(e) {
      // Show time since first event
      showElapsed(e.timeStamp);
    
      // Remember this event's timetsamp
      start2 = e.timeStamp;
    
      // Another busy-loop, just a second this time
      busy(1000);
      display("Done with second busy loop");
    }, false);
    
    document.getElementById("then2").addEventListener("click", function(e) {
      // Show time since first and second events
      showElapsed(e.timeStamp);
    }, false);
    
    function showElapsed(ts) {
      display("Elapsed from first event: " + (ts - start1) + "ms");
      if (start2) {
        display("Elapsed from second event: " + (ts - start2) + "ms");
      }
    }
    
    function busy(duration) {
      var target = Date.now() + duration;
      while (Date.now () < target) {
        // Wait
      }
    }
    
    function display(msg) {
      var p = document.createElement('p');
      p.innerHTML = msg;
      document.body.appendChild(p);
    }
    function format(ts) {
      return new Date(ts).toISOString().substring(11, 23);
    }
    <input type="button" id="start" value="Click me">
    <input type="button" id="then1" value="Then me quickly afterward">
    <input type="button" id="then2" value="Then me quickly after that (not on IE!)">