James Chiltern
James Chiltern

Reputation: 11

Why don't Firefox, Chrome, Opera keep up with mouse events?

Why can't modern browsers keep up with the mouse? Firefox 3.6, Chrome 8.xx, Opera 10.xx, all fail to fire mouse events like mouseover, mouseout and mousemove at anything like a high enough rate to keep up with a fast moving mouse.

How does the browser decide when to generate a mouseover or mouseout event under these conditions? In practice it bears no resemblance to the accepted definition of "This mouseout event is sent to an element when the user moves the mouse outside the element. This event is the reverse of mouseover." [MDC]

The code below is the simplest case I can find to demonstrate this behaviour. Event listeners are attached to the document object.

<?xml version="1.0" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" >
<html xmlns="http://www.w3.org/1999/xhtml"
      xml:lang="en" lang="en" >
  <head>
    <title>Mouse event tracking</title>
    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
    <style type="text/css">
      div {
        position: absolute;
        border:   1px solid black;
        width:    40px;
        height:   100px;
      }
    </style>
  </head>
  <body>
    <h1 style="position: absolute; top: 10px; left: 10px; font-size: 14px">Start on green, move mouse to yellow</h1>
    <div id="ladder_start"  style="top: 50px;  left: 10px; background-color: #44ff44;"></div>
    <div id="ladder"        style="top: 50px;  left: 50px;"></div>    
    <div id="ladder_end"    style="top: 50px;  left: 850px; background-color: #ffff44;"></div>
    <div id="ladder_p_over" style="top: 50px;  left: 900px; width: 100px; height: 50px"></div>
    <div id="ladder_p_out"  style="top: 100px; left: 900px; width: 100px; height: 50px"></div>
    <canvas id="ladder_trace"  style="position: absolute; border: 1px solid black; top: 155px; left: 50px; height: 20px"></canvas>

    <script type="text/javascript">
      window.addEventListener("load", function (event) {
        if (window.event_tests_loaded === true) {
          return;
        }

        var ladder           = document.getElementById("ladder");
        var ladder_trace     = document.getElementById("ladder_trace");
        var ladder_start     = document.getElementById("ladder_start");
        var ladder_end       = document.getElementById("ladder_end");
        var ladder_p_over    = document.getElementById("ladder_p_over");
        var ladder_p_out     = document.getElementById("ladder_p_out");
        var mouse_over_count = 0;
        var mouse_out_count  = 0;
        var steps            = [];
        var trace            = ladder_trace.getContext('2d');  


        var step_over = function (event) {
          event.preventDefault();
          event.stopPropagation();
          if (typeof event.target.parentNode === "object") {
            if (event.target.parentNode === ladder) {
              event.target.style.backgroundColor = event.target.highlight_color;
              mouse_over_count++;
              ladder_p_over.textContent = mouse_over_count;
            }
          }
        };

        var step_out = function (event) {
          event.preventDefault();
          event.stopPropagation();
          if (typeof event.target.parentNode === "object") {
            if (event.target.parentNode === ladder) {
              mouse_out_count++;
              ladder_p_out.textContent = mouse_out_count;
            }
          }
        };

        var move = function (event) {
          event.preventDefault();
          event.stopPropagation();
          if ((event.clientX >= 50) && (event.clientX < 850)) {
            trace.fillStyle   = "black";
            trace.strokeStyle = "black";
            trace.lineWidth   = 1;
            trace.beginPath();
            trace.moveTo(event.clientX - 50 + 0.5, 0);
            trace.lineTo(event.clientX - 50 + 0.5, 20);
            trace.stroke();
          }
        };

        ladder_start.addEventListener("mouseover", function (event) {
          event.preventDefault();
          event.stopPropagation();
          mouse_over_count = 0;
          mouse_out_count  = 0;
          ladder_p_over.textContent = mouse_over_count;
          ladder_p_out.textContent  = mouse_out_count;
          var i;
          for (i = 0; i < 40; i++) {
            steps[i].style.backgroundColor = steps[i].original_color;
          }
          trace.clearRect(0, 0, 800, 20);
          document.addEventListener("mouseover", step_over, false);
          document.addEventListener("mouseout", step_out, false);
          document.addEventListener("mousemove", move, false);
        }, false);

        ladder_end.addEventListener("mouseover", function (event) {
          document.removeEventListener("mouseover", step_over, false);
          document.removeEventListener("mouseout", step_out, false);
          document.removeEventListener("mousemove", move, false);
        }, false);

        (function () {
          var i;
          for (i = 0; i < 40; i++) {
            steps[i] = document.createElement("div");
            steps[i].style.width           = "20px";
            steps[i].style.border          = "0";
            steps[i].style.top             = "0px";
            steps[i].style.left            = (i * 20) + "px";
            steps[i].original_color        = "rgb(" + (i * 4) + ",0,0)";
            steps[i].highlight_color       = "rgb(255," + (i * 4) + ",255)";
            steps[i].style.backgroundColor = steps[i].original_color;
            ladder.appendChild(steps[i]);
          }

          ladder.style.width       = (i * 20) + "px";
          ladder_trace.style.width = (i * 20) + "px";
          ladder_trace.width       = (i * 20);
          ladder_trace.height      = 20;
        }());

        ladder_p_over.textContent = mouse_over_count;
        ladder_p_out.textContent  = mouse_out_count;
      }, false);
    </script>
  </body>
</html>

Upvotes: 1

Views: 894

Answers (1)

Vercas
Vercas

Reputation: 9141

That would be because they render first, and then check for input.
I've been frustrated about this for a while too, but a few tests confirmed this.
They render and then check for input.

Edit: If you have a good computer and use a good recording software, try recording your mouse in your monitor's full FPS. Now play it in slow motion, like 1/10 speed.

You'll notice that the mouse cursor doesn't pass through every pixel on the screen. If it did, it could either...

  • Go through every pixel, every frame.
    This would limit your mouse speed (pixels/second) to your monitor's FPS (hertz).
  • Virtually go through every pixel in a sub-frame manner.
    This would definitely take much processor time to think.

To fix this, every OS makes the mouse jump/skip to the required pixel every frame.
... and thus, your problem.

Upvotes: 2

Related Questions