Принудительная перерисовка / обновление DOM на Chrome / Mac


216

Время от времени 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?


Я столкнулся с той же проблемой несколько минут назад. Я изменяю элемент для div (это был span), и теперь браузер корректно перерисовывает изменения. Я знаю, что это немного старовато, но это может помочь некоторым братьям там, я думаю.
Андрес Торрес

2
Пожалуйста, смотрите ответ ниже, касающийся непрозрачности 0,99 - лучший ответ здесь - но его нелегко найти, так как он так глубоко на странице.
Grouchal

1
Это происходит и в Linux :-(
schlingel

1
Вы можете заменить тайм-аут на requestAnimationFrame, и в этом случае вы получите то же самое, но с задержкой 16 мс вместо 1000 мс.
trusktr

3
Пожалуйста, отправьте Chromium для недопустимого рендеринга. Похоже, что никто не сделал этого, несмотря на это 4 года назад.
Барди Харбороу

Ответы:


183

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

// 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
}

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


3
Afaik, невозможно перерисовать только часть области просмотра. Браузер всегда перерисовывает всю веб-страницу.
Шиме Видас

39
вместо $ ('# parentOfElementToBeRedrawn'). hide (). show (); Мне нужно было .hide.show (0) для правильной работы в Chrome
l.poellabauer

1
@ l.poellabauer здесь то же самое - это не имеет смысла ... но спасибо. Как, черт возьми, ты узнал ??? :)
JohnIdol

6
К вашему сведению, соответствующая область, над которой я работал, был бесконечный список прокрутки. Было немного нереально спрятать / показать весь UL, поэтому я поиграл и обнаружил, что если вы делаете .hide().show(0)на любом элементе (я выбрал нижний колонтитул страницы), он должен обновить страницу.
treeface

1
Можно перерисовать только часть области просмотра. Для этого нет API, но браузер может сделать это. Для начала область просмотра окрашена в плитки. И композитные слои окрашиваются независимо.
ещё

62

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

$(window).trigger('resize');

11
Подсказка: это перерисовает ваше полное окно, что является дорогостоящим и, вероятно, не тем, что хочет OP
сейчас78

7
Изменение размера окна всегда запускает перекомпоновку, но код $(window).trigger('resize')этого не делает, оно только генерирует событие «resize», которое запускает связанный обработчик, если он назначен.
гуари

1
Вы спасете меня целую неделю! Моя проблема заключается в том, что после настройки <body style = "zoom: 67%"> страница была увеличена до ожидаемого уровня, но страница зависла и не может прокручиваться !. Ваш ответ решил эту проблему немедленно
user1143669

30

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

.willnotrender { 
   transform: translateZ(0); 
}

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


1
это работало хорошо для меня, когда проблема заключалась в удалении HTML-элемента, нарисованного на холсте, хотя холст непрерывно анимировался.
Knaģis

Это исправило проблему с макетом, с которой я столкнулся neon-animated-pages. Спасибо: +1:
Coffeesaga

1
Это сработало. Safari оставлял элементы, которые в элементе div не отображали ничего, видимые и недоступные для выбора. Изменение размера окна заставило их исчезнуть. Добавление этого преобразования привело к тому, что «артефакты» исчезли, как и ожидалось.
Джефф

28

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

var forceRedraw = function(element){
  var disp = element.style.display;
  element.style.display = 'none';
  var trick = element.offsetHeight;
  element.style.display = disp;
};

1
Это не работает для меня на Chrome и FF на Linux. Однако просто доступ к element.offsetHeight (без изменения свойства дисплея) делает работу. Это так же, как браузер пропустит вычисление offsetHeight, когда элемент не виден.
Гродригес

Это решение идеально подходит для работы с ошибкой, с которой я столкнулся в браузере на основе Chrome, используемом в "overwolf" Я пытался понять, как это сделать, и вы спасли меня от усилий.
Nacitar Sevaht

13

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

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

$(el).redraw();

1
Но он оставил некоторые «остатки», такие как «display: block» и т. Д., Которые иногда могут разрушить структуру страницы.
pie6k

По сути это должно быть так же, как$().hide().show(0)
Махн

@ Ман ты пробовал? На мой взгляд, .hide () не блокирует, поэтому он пропустит повторный рендеринг в браузере, то есть весь смысл метода. (И если я правильно помню, я проверял это тогда.)
zupa

2
@zupa, он протестирован и работает на Chrome 38. Хитрость в том, что show()отличается от того, show(0)что указав длительность в последнем, он запускается с помощью методов анимации jQuery, а не сразу, что дает время для рендеринга таким же образом, что hide(0, function() { $(this).show(); })и ,
Махн

@Ман, ладно, хорошо поймай. Я определенно за ваше решение, если оно работает кроссплатформенно и не является новой функцией jQuery. Поскольку у меня нет времени, чтобы проверить это, я добавлю это, как «может работать». Вы можете редактировать ответ, если считаете, что вышеизложенное применимо.
zupa

9

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

$('#page').hide();
setTimeout(function() {
    $('#page').show();
}, 0);

1
Этот работал для ошибки Chrome, когда фильтры SVG иногда не перерисовываются, bugs.chromium.org/p/chromium/issues/detail?id=231560 . Тайм-аут был важен.
Джейсон Д

Это то, что мне помогло, когда я пытался заставить Chrome пересчитать значения myElement.getBoundingClientRect(), которые были кэшированы, скорее всего, для целей оптимизации / производительности. Я просто добавил нулевую задержку в миллисекунду для функции, которая добавляет новый элемент в DOM и получает новые значения после того, как старый элемент с предыдущими (кэшированными) значениями был удален из DOM.
TheDarkIn1978

6

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

$(el).css("opacity", .99);
setTimeout(function(){
   $(el).css("opacity", 1);
},20);

У меня это работало в Chromium версии 60.0.3112.113 в отношении этой ошибки: выпуск 727076 . Эффективный и тонкий; большое спасибо.
Лонни Бест

@ wiktus239 Может быть, 20 миллисекунд слишком быстры, и браузер не успевает измениться на 0,99, прежде чем снова вернется к 1,0? - В любом случае, этот прием непрозрачности работает для меня, чтобы сделать содержимое в iframe видимым в iPhone iOS 12 Safari, и я переключаю каждые 1000 миллис. Это также то, что находится в связанном выпуске 727076 в комментарии выше (1000 миллис).
KajMagnus

4

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


2
не работает для меня, но это, вероятно, потому что мне нужно offsetHeightсвойство, которое не css.
Себас

3

Я столкнулся с этой проблемой сегодня в 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
function resizeWindow(){
    var evt = document.createEvent('UIEvents');
    evt.initUIEvent('resize', true, false,window,0);
    window.dispatchEvent(evt); 
}

вызовите эту функцию через 500 миллисекунд.


2

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

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

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

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

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


1

Образец HTML:

<section id="parent">
  <article class="child"></article>
  <article class="child"></article>
</section>

Js:

  jQuery.fn.redraw = function() {
        return this.hide(0,function() {$(this).show(100);});
        // hide immediately and show with 100ms duration

    };

функция вызова:

$('article.child').redraw(); // <== плохая идея

$('#parent').redraw();

также работает .fadeIn(1)вместо .show(300)меня - не толкая элемент вокруг. Так что я думаю, что это запуск display:noneи обратноblock
BananaAcid

0

Подход, который работал для меня в IE (я не мог использовать технику отображения, потому что был ввод, который не должен терять фокус)

Работает, если у вас 0 полей (смена отступов тоже работает)

if(div.style.marginLeft == '0px'){
    div.style.marginLeft = '';
    div.style.marginRight = '0px';
} else {
    div.style.marginLeft = '0px';
    div.style.marginRight = '';
}

0

Только CSS Это работает в ситуациях, когда дочерний элемент удален или добавлен. В этих ситуациях границы и закругленные углы могут оставить артефакты.

el:after { content: " "; }
el:before { content: " "; }

0

Мое исправление для IE10 + IE11. По сути, происходит добавление DIV в элемент-обертку, который необходимо пересчитать. Тогда просто удали это и вуаля; работает как шарм :)

    _initForceBrowserRepaint: function() {
        $('#wrapper').append('<div style="width=100%" id="dummydiv"></div>');
        $('#dummydiv').width(function() { return $(this).width() - 1; }).width(function() { return $(this).width() + 1; });
        $('#dummydiv').remove();
    },

0

Большинство ответов требуют использования асинхронного тайм-аута, который вызывает раздражающее мигание.

Но я придумал этот, который работает гладко, потому что это синхронно:

var p = el.parentNode,
    s = el.nextSibling;
p.removeChild(el);
p.insertBefore(el, s);

Будут ли работать прикрепленные события к этим элементам?
Керри Джонсон

0

Это мое решение, которое сработало для исчезновения контента ...

<script type = 'text/javascript'>
    var trash_div;

    setInterval(function()
    {
        if (document.body)
        {
            if (!trash_div)
                trash_div = document.createElement('div');

            document.body.appendChild(trash_div);
            document.body.removeChild(trash_div);
        }
    }, 1000 / 25); //25 fps...
</script>

0

Я столкнулся с подобной проблемой, и эта простая линия JS помогла исправить это:

document.getElementsByTagName('body')[0].focus();

В моем случае это была ошибка с расширением Chrome, которое не перерисовывало страницу после изменения ее CSS изнутри расширения.


4
Это нарушает доступность клавиатуры.
Пангамма

0

Я хотел вернуть все состояния в предыдущее состояние (без перезагрузки), включая элементы, добавленные jquery. Вышеуказанная реализация не сработает. и я сделал следующее.

// Set initial HTML description
var defaultHTML;
function DefaultSave() {
  defaultHTML = document.body.innerHTML;
}
// Restore HTML description to initial state
function HTMLRestore() {
  document.body.innerHTML = defaultHTML;
}



DefaultSave()
<input type="button" value="Restore" onclick="HTMLRestore()">

0

У меня был список реагирующих компонентов, который при прокрутке открывал другую страницу, затем при возврате список не отображался на Safari iOS до прокрутки страницы. Так что это исправление.

    componentDidMount() {
        setTimeout(() => {
            window.scrollBy(0, 0);
        }, 300);
    }

0

Ниже CSS работает на IE 11 и Edge, JS не требуется. scaleY(1)делает трюк здесь. Кажется самое простое решение.

.box {
    max-height: 360px;
    transition: all 0.3s ease-out;
    transform: scaleY(1);
}
.box.collapse {
    max-height: 0;
}


0

2020: легче и сильнее

Предыдущие решения больше не работают для меня.

Я предполагаю, что браузеры оптимизируют процесс рисования, обнаруживая все больше и больше «бесполезных» изменений.

Это решение заставляет браузер рисовать клон для замены исходного элемента. Это работает и, вероятно, более устойчиво:

const element = document.querySelector('selector');
if (element ) {
  const clone = element.cloneNode(true);
  element.replaceWith(clone);
}

протестировано на Chrome 80 / Edge 80 / Firefox 75

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.