Как заставить WebKit перерисовывать/перерисовывать для распространения изменений стиля?

У меня есть тривиальный JavaScript для изменения стиля:

sel = document.getElementById('my_id');
sel.className = sel.className.replace(/item-[1-9]-selected/,'item-1-selected');
return false;

Это отлично работает с последними версиями FF, Opera и IE, но не работает в последних версиях Chrome и Safari.

Это затрагивает двух потомков, которые, возможно, являются братьями и сестрами. Первый sibling обновления, но второй doesnt. Ребенок второго элемента также имеет фокус и содержит тег <a> , который содержит указанный выше код в атрибуте onclick.

В окне "Инструменты разработчика" Chrome, если я подталкиваю (например, сниму и проверяю) какой-либо атрибут любого элемента, второй роднит обновляется до правильного стиля.

Есть ли обходной путь, чтобы легко и программно "подтолкнуть" WebKit к правильной работе?

Ответ 1

Я нашел несколько сложных предложений и много простых, которые не работали, но комментарий к одному из них Василь Динков предоставил простое решение для принудительно перерисовать/перерисовать, который работает отлично:

sel.style.display='none';
sel.offsetHeight; // no need to store this anywhere, the reference is enough
sel.style.display='';

Позволяет кому-то комментировать, если он работает для стилей, отличных от "block".

Спасибо, Василь!

Ответ 2

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

.willnotrender { 
   transform: translateZ(0); 
}

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

Webkit и Blink много работали над производительностью, и эти виды сбоев являются неудачным побочным эффектом оптимизации, целью которого является уменьшение ненужных потоков и красок. CSS изменится, или, скорее всего, будущая версия будет следующей версией.

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

Ответ 3

Решение danorton для меня не сработало. У меня были некоторые действительно странные проблемы, когда вебкит не рисовал какие-то элементы; где текст на входах не обновлялся до onblur; и изменение имени класса не приведет к перерисовке.

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

<body>
...
<script>doSomethingThatWebkitWillMessUp();</script>
<style></style>
...

Это исправлено. Как странно это? Надеюсь, это поможет кому-то.

Ответ 5

У меня была одна и та же проблема. Обновление danorton 'toggling display' помогло мне при добавлении к шаговой функции моей анимации, но я был обеспокоен производительностью, и я искал другие параметры.

В моем случае элемент, который не был перекрашен, находился в пределах элемента абсолютно позиции, который в то время не имел z-индекса. Добавление z-индекса в этот элемент изменило поведение Chrome, и переименование произошло, как ожидалось, → анимация стала гладкой.

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

Cheers, Rob

tl; dr → Попробуйте добавить z-index к элементу или его родительскому объекту.

Ответ 6

Следующие работы. Он должен быть установлен только один раз в чистом CSS. И он работает более надежно, чем функция JS. Производительность кажется незатронутой.

@-webkit-keyframes androidBugfix {from { padding: 0; } to { padding: 0; }}
body { -webkit-animation: androidBugfix infinite 1s; }

Ответ 7

По какой-то причине я не мог заставить danorton отвечать на работу, я мог видеть, что это должно было сделать, поэтому я немного изменил это:

$('#foo').css('display', 'none').height();
$('#foo').css('display', 'block');

и это сработало для меня.

Ответ 8

Я наткнулся на это сегодня: Element.redraw() для prototype.js

Использование:

Element.addMethods({
  redraw: function(element){
    element = $(element);
    var n = document.createTextNode(' ');
    element.appendChild(n);
    (function(){n.parentNode.removeChild(n)}).defer();
    return element;
  }
});

Однако иногда я заметил, что вы должны вызвать redraw() на проблемном элементе напрямую. Иногда перерисовка родительского элемента не решит проблему, с которой ребенок сталкивается.

Хорошая статья о том, как браузеры отображают элементы: Рендеринг: перерисовка, перепланирование/ретрансляция, рестайл

Ответ 9

Я пришел сюда, потому что мне нужно было перерисовать полосы прокрутки в Chrome после изменения его css.

Если у кого-то есть такая же проблема, я решил это, вызвав эту функцию:

//Hack to force scroll redraw
function scrollReDraw() {
    $('body').css('overflow', 'hidden').height();
    $('body').css('overflow', 'auto');
}

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

Вот скрипка, где я ее использовал: http://jsfiddle.net/promatik/wZwJz/18/

Ответ 10

У меня была эта проблема с несколькими div, которые были вставлены в другой div с position: absolute, в вставленные divs не было атрибута позиции. Когда я изменил это на position:relative, он работал нормально. (было действительно трудно определить проблему)

В моем случае элементы, вставленные Angular с ng-repeat.

Ответ 11

Я не могу поверить, что это все еще проблема в 2014 году. У меня просто была эта проблема при обновлении окна с фиксированной позицией в левой нижней части страницы во время прокрутки, заголовок "призрел" вверх по экрану. Пробовав все выше, безуспешно, я заметил, что многие вещи были либо медленными, либо вызывающими проблемы из-за создания очень коротких релейных сигналов DOM и т.д., Вызывающих несколько неестественных прокруток и т.д....

В итоге я сделал фиксированную позицию, полный размер div с pointer-events: none и применил ответ danorton к этому элементу, что, похоже, заставляет перерисовать весь экран, не мешая DOM.

HTML:

<div id="redraw-fix"></div>

CSS

div#redraw-fix {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 25;
    pointer-events: none;
    display: block;
}

JS:

sel = document.getElementById('redraw-fix');
sel.style.display='none';
sel.offsetHeight; // no need to store this anywhere, the reference is enough
sel.style.display='block';

Ответ 12

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

//Assuming black is the starting color, we tweak it by a single bit
elem.style.color = '#000001';

//Change back to black
setTimeout(function() {
    elem.style.color = '#000000';
}, 0);

setTimeout оказался критическим для перемещения второго изменения стиля за пределы текущего цикла событий.

Ответ 13

Единственное решение для меня похоже на sowasred2012 ответ:

$('body').css('display', 'table').height();
$('body').css('display', 'block');

У меня на странице много проблемных блоков, поэтому я изменяю свойство display корневого элемента. И я использую display: table; вместо display: none;, потому что none будет reset смещение прокрутки.

Ответ 14

Поскольку у каждого, похоже, есть свои проблемы и решения, я решил, что добавлю что-то, что сработает для меня. На Android 4.1 с текущим Chrome, пытаясь перетащить холст внутри div с переполнением: скрытый, я не смог получить перерисовку, если бы не добавил элемент в родительский div (где он не повредит).

var parelt = document.getElementById("parentid");
var remElt = document.getElementById("removeMe");
var addElt = document.createElement("div");
addElt.innerHTML = " "; // Won't work if empty
addElt.id="removeMe";
if (remElt) {
    parelt.replaceChild(addElt, remElt);
} else {
    parelt.appendChild(addElt);
}

Отсутствие мерцания экрана или реального обновления и очистка после себя. Нет глобальных или классных переменных, только локальных. Кажется, ничего не болит с мобильными браузерами Safari/iPad или настольными браузерами.

Ответ 15

Я работаю над ионным html5-приложением, на нескольких экранах у меня есть элемент absolute, при прокрутке вверх или вниз на устройствах IOS (iPhone 4,5,6, 6+) у меня была ошибка перерисовки.

Пробовал много решений, ни одна из них не работала, за исключением того, что эта проблема решает мою проблему.

Я использую класс css .fixRepaint для этих элементов абсолютных положений

.fixRepaint{
    transform: translateZ(0);
}

Это устранило мою проблему, возможно, это поможет кому-то

Ответ 16

Это хорошо для JS

sel.style.display='none';
sel.offsetHeight; // no need to store this anywhere, the reference is enough
sel.style.display='block';

Но в JQuery, и особенно когда вы можете использовать $(document).ready и не можете привязываться к событию .load объекта по какой-либо конкретной причине, будет работать следующее.

Вам нужно получить контейнер OUTER (MOST) объектов /divs, а затем удалить все его содержимое в переменную, а затем повторно добавить его. Это сделает ВСЕ изменения, сделанные во внешнем контейнере видимыми.

$(document).ready(function(){
    applyStyling(object);
    var node = $("div#body div.centerContainer form div.centerHorizontal").parent().parent();
    var content = node.html();
    node.html("");
    node.html(content);
}

Ответ 17

Я нашел этот метод полезным при работе с переходами

$element[0].style.display = 'table'; 
$element[0].offsetWidth; // force reflow
$element.one($.support.transition.end, function () { 
    $element[0].style.display = 'block'; 
});

Ответ 18

"display/offsetHeight" взлома не работал в моем случае, по крайней мере, когда он был применен к анимированному элементу.

У меня появилось выпадающее меню, которое было открыто/закрыто над содержимым страницы. после закрытия меню артефакты оставляли содержимое страницы (только в браузерах webkit). единственный способ взлома "display/offsetHeight" - использовать, если я применил его к телу, что кажется неприятным.

однако я нашел другое решение:

  • перед тем, как элемент начнет анимацию, добавьте класс, определяющий "-webkit-backface-visibility: hidden;" на элемент (вы также можете использовать встроенный стиль, я бы предположил)
  • при анимации, удалите класс (или стиль)

это все еще довольно хаки (он использует свойство CSS3 для принудительной аппаратной рендеринга), но по крайней мере он влияет только на этот элемент и работал у меня как на сафари, так и на хроме на ПК и Mac.

Ответ 19

Это похоже на следующее: стиль jQuery не применяется в Safari

Решение, предложенное в первом ответе, хорошо сработало для меня в этих сценариях, а именно: применить и удалить фиктивный класс в тело после внесения изменений в стиль:

$('body').addClass('dummyclass').removeClass('dummyclass');

Это заставляет сафари перерисовывать.

Ответ 20

выше предложения не работали для меня. но ниже это делает.

Хотите изменить текст внутри якоря динамически. Слово "Поиск". Создал внутренний тег "font" с атрибутом id. Управление содержимым с помощью javascript (ниже)

Search

script Содержание:

    var searchText = "Search";
    var editSearchText = "Edit Search";
    var currentSearchText = searchText;

    function doSearch() {
        if (currentSearchText == searchText) {
            $('#pSearch').panel('close');
            currentSearchText = editSearchText;
        } else if (currentSearchText == editSearchText) {
            $('#pSearch').panel('open');
            currentSearchText = searchText;
        }
        $('#searchtxt').text(currentSearchText);
    }

Ответ 21

У меня возникла проблема с SVG, который исчезал в Chrome для Android, когда ориентация была изменена в определенных обстоятельствах. Следующий код не воспроизводит его, но это настройка, которую мы имели.

body {
  font-family: tahoma, sans-serif;
  font-size: 12px;
  margin: 10px;
}
article {
  display: flex;
}
aside {
  flex: 0 1 10px;
  margin-right: 10px;
  min-width: 10px;
  position: relative;
}
svg {
  bottom: 0;
  left: 0;
  position: absolute;
  right: 0;
  top: 0;
}
.backgroundStop1 {
  stop-color: #5bb79e;
}
.backgroundStop2 {
  stop-color: #ddcb3f;
}
.backgroundStop3 {
  stop-color: #cf6b19;
}
<article>
  <aside>
    <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="100%" width="100%">
      <defs>
        <linearGradient id="IndicatorColourPattern" x1="0" x2="0" y1="0" y2="1">
          <stop class="backgroundStop1" offset="0%"></stop>
          <stop class="backgroundStop2" offset="50%"></stop>
          <stop class="backgroundStop3" offset="100%"></stop>
        </linearGradient>
      </defs>
      <rect x="0" y="0" rx="5" ry="5" width="100%" height="100%" fill="url(#IndicatorColourPattern)"></rect>
    </svg>
  </aside>
  <section>
    <p>Donec et eros nibh. Nullam porta, elit ut sagittis pulvinar, lacus augue lobortis mauris, sed sollicitudin elit orci non massa. Proin condimentum in nibh sed vestibulum. Donec accumsan fringilla est, porttitor vestibulum dolor ornare id. Sed elementum
      urna sollicitudin commodo ultricies. Curabitur tristique orci et ligula interdum, eu condimentum metus eleifend. Nam libero augue, pharetra at maximus in, pellentesque imperdiet orci.</p>
    <p>Fusce commodo ullamcorper ullamcorper. Etiam eget pellentesque quam, id sodales erat. Vestibulum risus magna, efficitur sed nisl et, rutrum consectetur odio. Sed at lorem non ligula consequat tempus vel nec risus.</p>
  </section>
</article>

Ответ 22

Я бы порекомендовал менее хакерский и более формальный способ форсирования: использовать forceDOMReflowJS. В вашем случае ваш код будет выглядеть следующим образом.

sel = document.getElementById('my_id');
forceReflowJS( sel );
return false;

Ответ 23

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

.selector {
    content: '1'
}

Ответ 24

Я пытался ответить больше, но он не работает для меня. У меня были проблемы с тем же clientWidth с сафари по сравнению с другими браузерами, и этот код решил проблему:

var get_safe_value = function(elm,callback){
    var sty = elm.style
    sty.transform = "translateZ(1px)";
    var ret = callback(elm)//you can get here the value you want
    sty.transform = "";
    return ret
}
// for safari to have the good clientWidth
var $fBody = document.body //the element you need to fix
var clientW = get_safe_value($fBody,function(elm){return $fBody.clientWidth})

Это действительно странно, потому что, если я попытаюсь снова получить clientWidth после get_safe_value, я получу неверное значение с помощью safari, получатель должен находиться между sty.transform = "translateZ(1px)"; и sty.transform = "";

Другое решение, которое работает окончательно

var $fBody = document.body //the element you need to fix
$fBody.style.display = 'none';
var temp = $.body.offsetHeight;
$fBody.style.display = ""
temp = $.body.offsetHeight;

var clientW = $fBody.clientWidth

Проблема в том, что вы теряете фокус и состояния прокрутки.

Ответ 25

Я использую transform: translateZ(0); метод, но в некоторых случаях этого недостаточно.

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

@keyframes redraw{
    0% {opacity: 1;}
    100% {opacity: .99;}
}

// ios redraw fix
animation: redraw 1s linear infinite;

Ответ 26

Простое решение с jquery:

$el.html($el.html());

или

element.innerHTML = element.innerHTML;

Имел SVG, который не показывался, когда он был добавлен в html.

Это может быть добавлено после того, как элементы svg находятся на экране.

Лучшее решение - использовать:

document.createElementNS('http://www.w3.org/2000/svg', 'svg');

и с помощью jQuery:

$(svgDiv).append($(document.createElementNS('http://www.w3.org/2000/svg', 'g'));

это будет корректно отображаться в Chrome.