Как переполнение: скрыто; & border-radius в контейнере вызывает массовое замедление до "Paint/Render Layer" внутри контейнера, только в IE?
У меня много проблем с плохой производительностью в IE (все версии, включая IE11), в виджетах с поддержкой javascript HTML/SVG, только когда виджет размещен на определенной странице.
После определения того, что основной причиной замедления был перерисован слой paint/render, и исчерпывающий информацию об этом, я мог бы выйти из IE Developer Tools, я прибегал для проб и ошибок, отключающих классы предков по одному, пока производительность не улучшится; затем, при определении класса, отключая правила стиля по одному.
Вся моя проблема, похоже, сводится к одному правилу overflow: hidden;
для предка нескольких divs вверх по дереву.
Разница, которую он делает, невероятна: с overflow: hidden;
вверх по дереву, простое взаимодействие с пользователем (выделение пути SVG, создание текстовой метки HTML, отображение метки и ее расположение относительно пути и контейнера SVG) процессор уменьшает частоту кадров пользовательского интерфейса до нуля и замораживает все мертвые в пределах от 1000 до 4000 миллисекунд за взаимодействие. Без overflow: hidden;
у предка он заканчивается в десятки миллисекунд, и частота кадров никогда не опускается ниже половины (не IE-браузеры одинаковы независимо от overflow: hidden;
).
-
Здесь профиль с overflow: hidden;
для предка, профилирующий как входящие, так и выключенные взаимодействия, фильтруется для рисования событий:
![введите описание изображения здесь]()
-
Здесь профиль без overflow: hidden;
для предка, профилирующий как входящие, так и выключенные взаимодействия, фильтруется для рисования событий. Единственное изменение заключалось в тике или отключении галочки рядом со стилем overflow: hidden;
в инспекторе DOM, и неважно, в каком порядке выполняю тесты:
![введите описание изображения здесь]()
Я не хочу просто переопределять этот overflow: hidden;
как липкую штукатурку и говорить о выполненной работе, не понимая, как это происходит, и рискуя повторением проблемы с другими, казалось бы, тривиальными изменениями CSS. Я бы предпочел понять, почему overflow: hidden;
делает такую разницу и адрес надежным способом, который работает независимо от применяемого правила переполнения.
К сожалению, я не могу опубликовать полную демоверсию, но здесь приводится резюме соответствующей части структуры DOM с комментариями к стилям, связанным с компоновкой:
<div class="responsive-grid">
<!-- ...lots of nested divs that simply inherit styles, I can't change this aspect of the Drupal layout -->
<div id="panel-5" class="col-12"> <!-- width: 100%; float: left -->
<!-- this is the first element IE looks at for offsetWidth when doing the changes below -->
<!-- ...a few more nested divs without layout-changing styles -->
<div class="panel"> <!-- overflow: hidden; clear: both; border: 1px; -->
<!-- this is the element where removing the overflow: hidden changes everything -->
<!-- I'm not sure what clear:both is for here, since no siblings. Seems redundant -->
<!-- ...a few more nested divs with no style rules, some contain <p>s <h2>s etc... -->
<div class="container"> <!-- position: relative; -->
<div class="sub-column col-8"> <!-- width: 66%; display: inline-block -->
<div class="square"> <!-- width: 100%; padding-bottom: 100%; position: relative -->
<svg viewbox="0 0 500 500" preserveAspectRatio="XMinYMin meet" ...>
<!-- svg position: absolute; width:100%; height: 100% -->
Many paths here
<div class="label"> <!-- fixed width in pixels; position: absolute -->
Some text here
</div>
</div>
</div>
<div class="sub-column col-4"> <!-- width: 33%; display: inline-block -->
<div class="sidebar">
Many interactive controls here
<!-- .square, svg andd .sidebar contain the only elements that are updated -->
</div>
</div>
</div>
<!-- some more ordinary position: static text containers -->
</div>
</div>
</div>
Что может происходить здесь, и есть ли способ предотвратить это, не удаляя/запрещая overflow: hidden;
в элементе предка?
Я видел Как избежать затрат на переполнение: скрытый?, но оба вопроса и ответы кажутся специфичными для таблиц HTML и старой ошибки Webkit с тех пор исправлено.
Они также кажутся специфичными для случаев, когда контент, обрезанный переполнением, становится ненужным; причуда моего случая состоит в том, что overflow: hidden;
на самом деле не обрезается (на всякий случай) на этой странице (но я не могу просто удалить его, потому что эта часть шаблона затрагивает сотни других страниц, где это имеет эффект).
Обновление: Сюжет сгущается. Мне удалось повторить проблему с моим виджетами в более простой структуре HTML и обнаружил, что проблема возникает только в том случае, если и overflow: hidden;
и border-radius
(в моем случае, 3px) установлены в одном контейнере. С одной, но не с другой, проблема исчезает.
-
Здесь образец с overflow: hidden;
, но не border-radius
. Может быть, немного меньше, чем выше, но разница тривиальна:
![введите описание изображения здесь]()
-
Здесь образец с overflow: hidden;
и border-radius
из той же упрощенной структуры:
![введите описание изображения здесь]()
Ответы
Ответ 1
После гораздо большего тестирования я думаю, что начинаю понимать, что происходит здесь. Это чисто основано на наблюдении, хотя, поэтому я все равно буду увлекаться более авторитетным ответом, если у кого-нибудь есть.
Что его вызывает?
Это происходит, только если все это верно:
- Браузер IE (любая версия)
- Элемент предка, который мы будем называть X, содержит как
overflow: hidden
, так и border-radius
(или -ms-border-radius
). В моем тестировании это не происходит, если разные предки в одной ветки имеют эти стили.
- Существует много сложных элементов, таких как пути SVG или div-width, которые находятся в ветке DOM, где они или элемент между ними и элементом X имеют
position: absolute;
или position: relative;
Проблема также кажется более выраженной пропорционально количеству элементов, затронутых position: absolute;
/relative
, и их сложности. В случаях, когда были обнаружены пути SVG в гибко масштабируемом контейнере SVG с пропускной способностью% -width с% padding-bottom для фиксированного соотношения сторон, например, проблема была очень выраженной; если эта ветвь была задана position: static
, но другая ветвь имела div-width с предком position: absolute;
, тогда проблема по-прежнему наблюдалась по сравнению с удалением одного из overflow: hidden;
или border-radius
, но была гораздо менее серьезной.
Но почему?
У меня нет окончательного ответа, но у меня есть правдоподобная теория, которая, похоже, соответствует фактам. По иронии судьбы, это было бы попыткой ускорить оптимизацию производительности IE.
Я заметил, что вычисления offsetWidth
для элементов между X и путями проходили весь путь до путей, которые не имели для меня никакого смысла и вызвали связанный с ним вопрос потому что пути в контейнере SVG, безусловно, не могут влиять на компоновку вне контейнера.
Я также заметил, что, исследуя это, другие браузеры, в частности, старая версия Chrome, похоже, имели другую проблему: элементы, которые должны были быть скрыты, были рендеринг, вызывающий замедление.
Сложив их вместе, я думаю, что есть правдоподобное объяснение того, что происходит здесь. Если это правда, по иронии судьбы, мои проблемы с производительностью были вызваны попыткой IE IE, направленной на то, чтобы оптимизировать производительность и избежать таких проблем, как связанная с Chrome проблема Chrome.
Если эта теория верна, что-то вроде этого происходит в IE:
- Он видит
overflow: hidden;
и делает вывод, что он может повысить производительность, идентифицируя элементы, которые полностью находятся за пределами границ элемента, прежде чем делать на них события перерисовывания /reflow/paint и т.д.
- Он видит
position: absolute;
и position: relative;
дальше DOM и заключает, что они и их дети потенциально могут быть полностью вне контейнера и могут быть проигнорированы как
- Во время таких событий, как перерисовка и перепланирование, он сначала циклически проходит через все эти элементы и проверяет, что они не полностью за пределами границ. Это происходит даже без
border-radius
, но в таких случаях оно тривиально и принимает миллисекунды.
- ... однако IE отмечает граничный радиус и заключает, что форма этого элемента
overflow: hidden
не является прямоугольником, и поэтому требуется более сложный расчет, чтобы определить, что находится за пределами границ.
- Предположительно, он также заключает, что пути SVG нужно сравнивать на основе их фактических форм, а не их простых ограничивающих прямоугольных координат, так как это уже не просто сравнение параллельных прямоугольников.
- Внезапно вычисление для проверки того, можно ли игнорировать элемент во время красок/перерисовки/переплавки, стало сложным. Если повторять сотни раз для сотен элементов, он значительно ухудшает производительность при каждом запуске события.
Как это исправить?
Если вы можете, просто переместите overflow: hidden
или border-radius
в дочерний элемент, чтобы они не были на одном элементе. Выполнение задания.
Для меня, однако, я создаю плагин, который должен быть способен быть удаленным в любом месте и не будет иметь никакого контроля над страницами, на которых он был развернут. Я не знаю, как я могу заставить IE отключить это поведение.
Лучший подход, который я могу придумать, состоит в том, чтобы предположить, что стиль border-radius
не имеет существенного значения для эстетики и что overflow: hidden;
может быть существенным для структуры, и поэтому, если браузер IE, найдите дерево предков и удалите border-radius
из любого элемента, у которого есть и это, и overflow: hidden;
.
В моем приложении уже используется jQuery, поэтому этот тест выглядит примерно так:
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();
}
С CSS как:
.remove-border-radius {
border-radius: 0 0 0 0 !important;
-ms-border-radius: 0 0 0 0 !important;
}
Ответ 2
Проблема была привлечена к Microsoft внимание в этот отчет об ошибке, написанный Джоппе Кроном в феврале 2014 года:
[IE 9] [IE 11] div с граничным радиусом и переполнением за исключением видимого изменения размера при заполнении div с помощью позиции относительно
Перерисовывание страницы выполняется очень плохо, когда есть много большие элементы DIV с комбинацией положения "абсолютный", "фиксированный", или "относительный", переполнение, отличное от "видимого" и радиус границы. Это можно увидеть при изменении размера окна или прокрутки. Отдельные события рисования могут занять до 1,5 секунд, в результате чего страница перестает реагировать.
Эта проблема производительности появляется в IE 11 и IE 9, но не в IE 10.
<ч/" > Единственный существенный ответ Microsoft был опубликован в апреле 2016 года. Он не обеспечивает решение или обходное решение:
[...] Мы рассмотрели проблему, о которой вы сообщили очень близко, и нашли он очень похож на аналогичные сообщенные проблемы. Ни один из наших обычных резолюции ( "BY DESIGN", "DUPLICATE", "FIXED" и т.д.) в точности совпадают поэтому мы отмечаем это как "WONT FIX" из-за отсутствия что-то лучше, но то, что мы действительно имеем в виду, хорошо справляется с этим в агрегированный вид, а не смотреть на этот точный инцидент в этой точной ситуации. [...]
Все лучшее, команда MS Edge Team
Состояние ошибки: Закрыто, поскольку не будет исправлено