Принудительная перерисовка / обновление DOM на Chrome / Mac - PullRequest
201 голосов
/ 12 января 2012

Время от времени Chrome отображает совершенно корректный HTML / CSS неправильно или не отображает его вообще. Копания через инспектора DOM часто достаточно, чтобы заставить его осознать ошибку своих путей и правильно перерисовать, поэтому вполне вероятно, что разметка хорошая. Это происходит достаточно часто (и, как и ожидалось, достаточно) в проекте, над которым я работаю, и я поместил код на место, чтобы вызвать перерисовку при определенных обстоятельствах.

Это работает в большинстве комбинаций браузер / операционная система:

    el.style.cssText += ';-webkit-transform:rotateZ(0deg)'
    el.offsetHeight
    el.style.cssText += ';-webkit-transform:none'

Например, настройте неиспользуемое свойство CSS, затем запросите информацию, которая вызывает перерисовку, а затем отмените свойство. К сожалению, яркая команда Chrome для Mac, похоже, нашла способ получить это offsetHeight без перерисовки. Таким образом убивая полезного в противном случае взломать.

Пока что лучшее, что я придумал, чтобы получить тот же эффект на Chrome / Mac, это уродство:

    $(el).css("border", "solid 1px transparent");
    setTimeout(function()
    {
        $(el).css("border", "solid 0px transparent");
    }, 1000);

Как в действительности, заставить элемент немного подскочить, затем охладить секунду и отскочить назад. Хуже того, если вы сократите этот тайм-аут ниже 500 мс (туда, где он будет менее заметен), он часто не будет иметь желаемого эффекта, так как браузер не дойдет до перерисовки, пока не вернется в исходное состояние.

Кто-нибудь хочет предложить лучшую версию этого хака для перерисовки / обновления (предпочтительно на основе первого примера выше), который работает на Chrome / Mac?

Ответы [ 21 ]

174 голосов
/ 12 января 2012

Не уверен, что именно вы пытаетесь достичь, но этот метод я успешно использовал в прошлом, чтобы заставить браузер перерисовать, возможно, он будет работать для вас.

// in jquery
$('#parentOfElementToBeRedrawn').hide().show(0);

// in plain js
document.getElementById('parentOfElementToBeRedrawn').style.display = 'none';
document.getElementById('parentOfElementToBeRedrawn').style.display = 'block';

Еслиэта простая перерисовка не работает, вы можете попробовать это.Он вставляет пустой текстовый узел в элемент, который гарантирует перерисовку.

var forceRedraw = function(element){

    if (!element) { return; }

    var n = document.createTextNode(' ');
    var disp = element.style.display;  // don't worry about previous display style

    element.appendChild(n);
    element.style.display = 'none';

    setTimeout(function(){
        element.style.display = disp;
        n.parentNode.removeChild(n);
    },20); // you can play with this timeout to make it as short as possible
}

РЕДАКТИРОВАТЬ: В ответ на Šime Vidas, что мы достигаем здесь будет принудительное перекомпоновка.Вы можете узнать больше от самого мастера http://paulirish.com/2011/dom-html5-css3-performance/

61 голосов
/ 02 сентября 2014

Ни один из приведенных выше ответов не работал для меня. Я заметил, что изменение размера мое окно вызвало перерисовку. Так что это сделало это для меня:

$(window).trigger('resize');
25 голосов
/ 15 июля 2014

Это решение без таймаутов! Реальная сила перерисовка! Для Android и iOS.

var forceRedraw = function(element){
  var disp = element.style.display;
  element.style.display = 'none';
  var trick = element.offsetHeight;
  element.style.display = disp;
};
23 голосов
/ 15 января 2015

Недавно мы столкнулись с этим и обнаружили, что перевод затронутого элемента в составной слой с помощью translateZ исправил проблему, не требуя дополнительного JavaScript.

.willnotrender { 
   transform: translateZ(0); 
}

Какэти проблемы рисования в основном проявляются в Webkit / Blink, и это исправление в основном предназначено для Webkit / Blink, в некоторых случаях это предпочтительно.Тем более, что многие решения JavaScript вызывают перекомпоновку и перерисовку , а не просто перерисовку.

12 голосов
/ 17 мая 2013

Это работает для меня. Престижность иди сюда .

jQuery.fn.redraw = function() {
    return this.hide(0, function() {
        $(this).show();
    });
};

$(el).redraw();
7 голосов
/ 05 марта 2014

Если скрыть элемент, а затем снова показать его в пределах setTimeout 0, произойдет перерисовка.

$('#page').hide();
setTimeout(function() {
    $('#page').show();
}, 0);
4 голосов
/ 29 апреля 2015

Это, кажется, помогает мне. Плюс, это действительно не показывает вообще.

$(el).css("opacity", .99);
setTimeout(function(){
   $(el).css("opacity", 1);
},20);
4 голосов
/ 10 марта 2014

вызов window.getComputedStyle() должен форсировать

3 голосов
/ 02 июля 2016

Сегодня я столкнулся с этой проблемой в OSX El Capitan с Chrome v51. Рассматриваемая страница отлично работала в Safari. Я попробовал почти все предложения на этой странице - ни одно из них не сработало - у всех были побочные эффекты ... В итоге я реализовал приведенный ниже код - очень простой - без побочных эффектов (все еще работает, как и раньше в Safari).

Решение. При необходимости переключите класс на проблемный элемент. Каждое переключение вызовет перерисовку. (Я использовал jQuery для удобства, но ванильный JavaScript не должен быть проблемой ...)

JQuery Class Toggle

$('.slide.force').toggleClass('force-redraw');

Класс CSS

.force-redraw::before { content: "" }

И это все ...

ПРИМЕЧАНИЕ. Чтобы увидеть эффект, необходимо запустить фрагмент ниже «Полная страница».

$(window).resize(function() {
  $('.slide.force').toggleClass('force-redraw');
});
.force-redraw::before {
  content: "";
}
html,
body {
  height: 100%;
  width: 100%;
  overflow: hidden;
}
.slide-container {
  width: 100%;
  height: 100%;
  overflow-x: scroll;
  overflow-y: hidden;
  white-space: nowrap;
  padding-left: 10%;
  padding-right: 5%;
}
.slide {
  position: relative;
  display: inline-block;
  height: 30%;
  border: 1px solid green;
}
.slide-sizer {
  height: 160%;
  pointer-events: none;
  //border: 1px solid red;

}
.slide-contents {
  position: absolute;
  top: 10%;
  left: 10%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<p>
  This sample code is a simple style-based solution to maintain aspect ratio of an element based on a dynamic height.  As you increase and decrease the window height, the elements should follow and the width should follow in turn to maintain the aspect ratio.  You will notice that in Chrome on OSX (at least), the "Not Forced" element does not maintain a proper ratio.
</p>
<div class="slide-container">
  <div class="slide">
    <img class="slide-sizer" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7">
    <div class="slide-contents">
      Not Forced
    </div>
  </div>
  <div class="slide force">
    <img class="slide-sizer" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7">
    <div class="slide-contents">
      Forced
    </div>
  </div>
</div>
2 голосов
/ 22 апреля 2015

Если вы хотите сохранить стили, объявленные в ваших таблицах стилей, лучше использовать что-то вроде:

jQuery.fn.redraw = function() {
    this.css('display', 'none'); 
    var temp = this[0].offsetHeight;
    this.css('display', '');
    temp = this[0].offsetHeight;
};

$('.layer-to-repaint').redraw();

Примечание: с последним webkit / blink, сохранение значения offsetHeight обязательно для того, чтобывызвать перерисовку, иначе виртуальная машина (в целях оптимизации), вероятно, пропустит эту инструкцию.

Обновление: добавлено второе чтение offsetHeight, необходимо запретить браузеру ставить в очередь / кэшировать следующее свойство / класс CSSизменить с восстановлением значения display (таким образом, следует выполнить переход CSS, который может последовать)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...