Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prevent IE11 and Microsoft Edge from throttling events aggressively?

Background

I have a pannable app window which works by listening for mousemove events and then using transform: translate3d(...) to move the screen accordingly. It's a large app and there is considerable UI work associated to facilitate this functionality. Here comes the MCVE, in which the real workload is mocked by a dummy for loop:

var container = document.getElementById("container");
var contents = document.getElementById("contents");
var input = document.getElementById("iterations");

var posX = 50;
var posY = 50;
var previousX = null;
var previousY = null;

var mousedownHandler = function (e) {
    window.onmousemove = globalMousemoveHandler;
    window.onmouseup = globalMouseupHandler;
    previousX = e.clientX;
    previousY = e.clientY;
}

var globalMousemoveHandler = function (e) {
    var now = Date.now();
    for (var i = 0, n = parseInt(input.value); i < n; i++);
    var elapsed = Date.now() - now;

    posX += e.clientX - previousX;
    posY += e.clientY - previousY;
    previousX = e.clientX;
    previousY = e.clientY;
    contents.style.transform = "translate3d(" + posX + "px, " + posY + "px, 0)";
    contents.innerText = elapsed + "ms";
}

var globalMouseupHandler = function (e) {
    window.onmousemove = null;
    window.onmouseup = null;
    previousX = null;
    previousY = null;
}

container.onmousedown = mousedownHandler;
contents.style.transform = "translate3d(" + posX + "px, " + posY + "px, 0)";
#container {
  height: 180px;
  width: 600px;
  background-color: #ccc;
  overflow: hidden;
  cursor: -webkit-grab;
  cursor: -moz-grab;
  cursor: grab;
  -moz-user-select: none;
  -ms-user-select: none;
  -webkit-user-select: none;
  user-select: none;
}

#container:active {
  cursor: move;
  cursor: -webkit-grabbing;
  cursor: -moz-grabbing;
  cursor: grabbing;
}
<label>Iterations: <input id="iterations" type="number" value="20000000" step="5000000" /></label>

<div id="container">
    <div id="contents">
        Pannable container contents...
    </div>
</div>

JSFiddle example with larger drag-area

Please hold and drag the example.

This rudimentary snippet runs a dummy for loop every time mousemove fires, and the duration it takes for the loop to complete is displayed in the draggable container. This is needed to demonstrate the problem below. You may need to adjust the number of iterations, so that the loop takes somewhere above 10ms to run, but not much longer.

Problem

This snippet runs as fast as possible in Google Chrome, no problems there. Untested in Firefox.

However, in Microsoft Edge (and presumably, IE11 as well) if globalMousemoveHandler runs for longer than about 10ms, the browser starts throttling the event mercillesly, making it fire much less frequently, and obliterating the panning progress down to a crawl.

Also quite strange is that the for loop actually runs faster in Microsoft Edge than in Chrome (almost 50% faster), but the event still fires much less frequently.

This is observable in the above snippet when viewed from the mentioned browsers. Now I understand the theoretical desire behind this functionality, but it renders my application unusable on these browsers -- I also don't really understand what's the point for the throttling to kick in below 16ms (I'm well under the 60 FPS frame budget), but that's besides the question now (although I'd be glad to hear some details about this).

How can I prevent this from happening?

like image 200
John Weisz Avatar asked Oct 26 '16 14:10

John Weisz


People also ask

What is Internet Explorer mode on Microsoft Edge?

For organizations that require Internet Explorer 11 (IE11) for legacy compatibility, Internet Explorer mode (IE mode) on Microsoft Edge provides a seamless, single browser experience. Users can access legacy applications from within Microsoft Edge without having to switch back to IE11.

What happens when I enable or disable Internet Explorer 11?

When you enable the Disable Internet Explorer 11 as a standalone browser policy, all IE11 activity is redirected to Microsoft Edge and users have the following experience: The IE11 icon on the Start Menu will be removed, but the one on the taskbar will remain.

Why does Microsoft Edge launch instead of IE11?

When users try to launch shortcuts or file associations that use IE11, they will be redirected to open the same file/URL in Microsoft Edge. When users try to launch IE11 by directly invoking the iexplore.exe binary, Microsoft Edge will launch instead.

How does tracking prevention work in the new Microsoft Edge browser?

The new Microsoft Edge is designed to detect and block known trackers. We let you decide what trackers to block. You can choose from three levels of tracking prevention: Basic, Balanced, and Strict. Balanced is selected as the default.


1 Answers

Here is an example that uses requestAnimationFrame.

var container = document.getElementById("container");
var contents = document.getElementById("contents");
var input = document.getElementById("iterations");

var posX = 50;
var posY = 50;
var previousX = null;
var previousY = null;

var mousedownHandler = function (e) {
    window.onmousemove = globalMousemoveHandler;
    window.onmouseup = globalMouseupHandler;
    previousX = e.clientX;
    previousY = e.clientY;
}

var globalMousemoveHandler = function (e) {
    posX += e.clientX - previousX;
    posY += e.clientY - previousY;
    previousX = e.clientX;
    previousY = e.clientY;
  
    window.requestAnimationFrame(function () {        
      var now = Date.now();
      for (var i = 0, n = parseInt(input.value); i < n; i++);
      var elapsed = Date.now() - now;

      contents.style.transform = "translate3d(" + posX + "px, " + posY + "px, 0)";
      contents.innerText = elapsed + "ms";
    });
}

var globalMouseupHandler = function (e) {
    window.onmousemove = null;
    window.onmouseup = null;
    previousX = null;
    previousY = null;
}

container.onmousedown = mousedownHandler;
contents.style.transform = "translate3d(" + posX + "px, " + posY + "px, 0)";
#container {
  height: 180px;
  width: 600px;
  background-color: #ccc;
  overflow: hidden;
  cursor: -webkit-grab;
  cursor: -moz-grab;
  cursor: grab;
  -moz-user-select: none;
  -ms-user-select: none;
  -webkit-user-select: none;
  user-select: none;
}

#container:active {
  cursor: move;
  cursor: -webkit-grabbing;
  cursor: -moz-grabbing;
  cursor: grabbing;
}
<label>Iterations: <input id="iterations" type="number" value="20000000" step="5000000" /></label>

<div id="container">
    <div id="contents">
        Pannable container contents...
    </div>
</div>

This stops the heavy lifting from blocking the handler function, effectively preventing the issue from happening, at the cost of a 16ms increased latency.

like image 195
Keith Avatar answered Oct 05 '22 20:10

Keith