How does overflow: hidden; & border-radius on a container cause massive slowdowns to “Paint / Render Layer” within container, only on IE?

纵饮孤独 提交于 2019-11-30 17:31:41
user568458

After a lot more testing I think I'm starting to understand what's going on here. This is purely based on observation though, so I'd still be keen for a more authoritative answer if anyone has one.


What causes it?

It seems to happen only if all these are true:

  • The browser is IE (any version)
  • An ancestor element we'll call X contains both overflow: hidden and border-radius (or -ms-border-radius). In my testing, it doesn't happen if different ancestors in the same branch have these styles.
  • There are many complex elements such as SVG paths or %-width divs that are in a DOM branch where they, or an element between them and element X, has position: absolute; or position: relative;

The problem also seems to be more pronounced in proportion to the number of elements affected by position: absolute;/relative and their complexity. In cases where there were SVG paths in a responsively scaling %-width SVG container with % padding-bottom for fixed aspect ratio, for example, the problem was very pronounced; if this branch was given position: static but another branch had %-width divs with a position: absolute; ancestor, then the problem was still observable compared to removing one of overflow: hidden; or border-radius, but was much less severe.


But why?

I don't have any definitive answer, but I've got a plausible theory that seems to fit the facts. Ironically, it'd be a backfiring attempt at performance optimisation by IE.

I noticed that offsetWidth calculations for elements between X and the paths were going all the way to the paths, which made no sense to me and prompted a related question because paths within an SVG container surely can't influence the layout outside the container.

I also noticed while researching this that other browsers - particularly, an older version of Chrome - seemed to have a different problem: elements that should have been hidden were being rendered, causing slowdowns.

Putting these together, I think there's a plausible explanation for what's going on here. If true, ironically, my performance woes were being caused by a backfiring attempt by IE to optimise performance and avoid problems like the above-linked now-fixed Chrome issue.

If this theory is true, something like this would be happening within IE:

  • It sees the overflow: hidden; and concludes that it can improve performance by identifying elements that are wholly outside the element's bounds before doing redraw / reflow / paint etc events on them.
  • It sees the position: absolute; and position: relative; further down the DOM and concludes that these and their children might potentially be wholly outside the container and could potentially be disregarded like this
  • During events such as redraw and reflow, it first cycles through all these elements and checks they're not wholly outside the bounds. This still happens even without border-radius, but in such cases it's trivial and takes miliseconds.
  • ...however, IE notices the border-radius and concludes that the shape of this overflow: hidden element is not a rectangle and therefore a more complex calculation is needed to determine what is out of bounds
  • Presumably, it also concludes that SVG paths need to be compared based on their actual shapes not their simple bounding box co-ordinates, since this is no longer a simple matter of comparing parallel rectangles.
  • Suddenly, the calculation to test if an element can be disregarded during paint / redraw / reflow has become complex. If repeated hundreds of times for hundreds of elements, it massively worsens performance on every triggering event.

How to fix it?

If you can, simply move the overflow: hidden or border-radius to a child element so they're not both on the same element. Job done.

For me, however, I'm making a plugin that needs to be capable of being dropped in anywhere, and won't have any control over the pages its deployed on. I'm not aware of any way I could force IE to turn this behaviour off.

The best approach I can think of, is to assume that the border-radius style is non-essential for aesthetics and that the overflow: hidden; might be essential for structure, and so, if the browser is IE, look up the ancestor tree and remove border-radius from any element that has both it and overflow: hidden;.

My application already uses jQuery, so this test looks something like this:

if( isAnyIE() ) {
  $container.parentsUntil("body").filter(function(){

    var $this = $(this),
      overflow = $this.css('overflow');
    return ( overflow === 'hidden' && hasBorderRadius( $this ) );

  }).addClass( 'remove-border-radius' ); 
}

function hasBorderRadius( $element ){
  function getNum( style ){
    return parseFloat( $element.css( 'border-'+style+'-radius' ) ) || 0;
  }
  var number = 0;

  number += getNum( 'top-left' );
  number += getNum( 'bottom-left' );
  number += getNum( 'top-right' );
  number += getNum( 'bottom-right' );

  $element = null;

  return !!number;
}

function isAnyIE(){
  // isIE(): use conditional comments and classes, see https://stackoverflow.com/a/18615772/568458
  // isIE10: use user agent like navigator.appVersion.indexOf("MSIE 10") !== -1;
  // isIE11: use user agent like !!navigator.userAgent.match(/Trident.*rv[ :]*11\./);

  return isIE11() || isIE10() || isIE();
}

With CSS like:

.remove-border-radius {
  border-radius: 0 0 0 0 !important;
  -ms-border-radius: 0 0 0 0 !important;
}

The problem was brought to Microsoft's attention in this bug report written by Joppe Kroon in February 2014:

[IE 9] [IE 11] div's with a border-radius and overflow other than visible resize slow when filled with div's with position relative

Redrawing of the page performs very badly when there are a lot of large DIV elements with a combination of position "absolute", "fixed" or "relative", an overflow other than "visible" and a border-radius. This can be clearly seen when resizing the window or scrolling. Individual paint events can take up to 1.5 seconds to complete, causing the page to become unresponsive.

This performance issue shows up in IE 11 and IE 9 but not in IE 10.


The only significant reply from Microsoft was published in April 2016. It does not provide a solution or a workaround:

[...] We have looked at the issue you reported very closely and found it closely resembles similar reported issues. None of our usual resolutions (“BY DESIGN”, “DUPLICATE”, “FIXED”, etc.) exactly match this situation therefore we are marking it as “WON’T FIX” for lack of anything better but what we really mean is we’ll be dealing with it in an aggregated kind of way rather than looking at this exact incident in this exact situation. [...]

All the best, The MS Edge Team

The status of the bug is : Closed as Won't fix

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!