Як змусити WebKit перемальовувати / перефарбовувати, щоб поширити зміни стилю?


247

У мене є тривіальний 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.

Це впливає на двох нащадків, які, можливо, є побратимами. Перший брат поновлюється, але другий не робить. Дочірня другого елемента також має фокус і містить тег <a>, який містить вищевказаний код в атрибуті onclick .

У вікні Chrome "Інструменти для розробників", якщо я натискаю (наприклад, зніміть прапорець і перевіряю) будь-який атрибут будь-якого елемента, другий брат може оновити відповідний стиль.

Чи існує рішення, щоб легко і програмно «підштовхнути» WebKit до правильних дій?


Я думаю, що я відчуваю цю проблему на Android 4.2.2 (Samsung I9500) під час обгортання програми "Полотно" у WebView. Смішно!
Джош

3
what-forces-layout.md дуже гарне місце для читання
vsync

Відповіді:


312

Я знайшов кілька складних пропозицій і багато простих, які не спрацювали, але коментар до одного з них Василя Дінькова дав просте рішення, щоб змусити перемалювати / перефарбувати, що працює чудово:

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

Я дозволю комусь іншому коментувати, якщо він працює для стилів, відмінних від «блоку».

Спасибі, Василю!


32
Щоб уникнути мерехтіння, ви можете спробувати 'inline-block', 'table'або 'run-in'замість цього 'none', але це може мати побічні ефекти. Також тайм-аут 0 викликає поповнення так само, як і запит offsetHeight:sel.style.display = 'run-in'; setTimeout(function () { sel.style.display = 'block'; }, 0);
user123444555621

15
Ця відповідь все ще корисна через 2 роки. Я просто використовував це для вирішення проблеми, що стосується лише Chrome, де тіні вікон CSS залишалися на екрані після зміни розміру браузера певними способами. Дякую!
rkulla

5
@danorton Ви відповіли в 2010 році. Це 2013 рік, і помилка все ще існує. Дякую. До речі, хтось знає, чи є якась проблема, зареєстрована в webkit tracker?
RaphaelDDL

13
PS: для мене вищевказане рішення більше не працювало. Натомість я використав це (jQuery): sel.hide (). Show (0); Джерело: stackoverflow.com/questions/8840580/…
ProblemsOfSumit

7
Все, що вам потрібно зробити, це прочитати властивість розміру, вам не потрібно змінювати його вперед і назад.
Ендрю Буллок

93

Нещодавно ми стикалися з цим і виявили, що просування пошкодженого елемента до складеного шару з translateZ в CSS виправило проблему, не потребуючи додаткового JavaScript.

.willnotrender { 
   transform: translateZ(0); 
}

Оскільки ці проблеми з фарбуванням виявляються в основному в Webkit / Blink, і це виправлення в основному націлено на Webkit / Blink, в деяких випадках краще. Тим більше, що прийнята відповідь майже напевно викликає повторне перефарбування , а не просто перефарбування.

Вебкіт і Блинк наполегливо працюють над рендерингами, і подібні глюки є нещасливим побічним ефектом оптимізацій, спрямованих на зменшення зайвих потоків і фарб. В майбутньому рішенням, швидше за все, буде зміна CSS або інша наступна специфікація.

Є й інші способи досягнення складеного шару, але це найпоширеніший.


1
Працював для мене під час використання кутової каруселі !
gustavohenke

1
Виправлено мою проблему, коли радіус кордону загубився в елементі всередині розсувної панелі
Тимо

1
Працює і для проектів "Кордова"!
Quispie

1
Для мене працював затискач -webkit-line, який не оновився
Адам Маршалл

1
Працював для мене на ipad (6.0) з -webkit-transform: перекладати (-50%, -50%).
Пролежав

51

Данортон рішення не працювало для мене. У мене виникли справді дивні проблеми, коли вебкіт взагалі не малював би якісь елементи; де текст у введеннях не оновлювався до включення; і зміна className не призведе до перерисування.

Моє рішення, я випадково виявив, було додати порожній елемент стилю до тіла після сценарію.

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

Це і виправило. Як це дивно? Сподіваюся, це комусь корисно.


3
Я б схвалив 1000 000 разів, якби міг. Це єдиний спосіб, коли я міг отримати хром, щоб перефарбувати дурний дів, який я перетягував. До речі, вам не потрібно додавати елемент стилю (принаймні я цього не робив) жоден елемент буде працювати. Я створив div і дав йому ідентифікатор, щоб я міг видалити, потім додав елемент на кожному кроці, щоб не заповнити DOM непотрібними тегами.
hobberwickey

6
щойно використав це змішане з коментарем Pumbaa80 щодо іншої відповіді. Виправлення, в якому я закінчився,var div = $('<div>').appendTo(element); setTimeout(function(){ div.remove(); }, 0);
бендман

33
Цей єдиний рядок для мене чудово працює! $('<style></style>').appendTo($(document.body)).remove();
pdelanauze

1
Відповідь @pdelanauze прекрасно працює. У мене є проблема перемальовування співати перекладати css3 і перетворювати в ios.
Марсель Фальєр

11
Просто знайте, що це змушує перефарбувати всю сторінку, тоді як більшість часу ви хочете лише перефарбувати певний розділ сторінки ...
Ryan Wheale

33

Оскільки тригер дисплея та зміщення не працював для мене, я знайшов тут рішення:

http://mir.aculo.us/2009/09/25/force-redraw-dom-technique-for-webkit-based-browsers/

тобто

element.style.webkitTransform = 'scale(1)';

3
Я використовував це в хаке, який я намагався, але замість цього scale(1)я призначив його собі як element.style.webkitTransform = element.style.webkitTransform. Причиною цього було те, що встановивши його на колишнє, було дещо спотворити сторінку для absoluteрозміщених елементів!
bPratik

Це працювало для мене, і у мене не було проблем мерехтіння або скидання положення прокрутки, що було displayзроблено рішенням.
davidtbernal

У мене була програма Кордова, яка використовувала багато динамічних SVG. Працював чудово скрізь, крім iOS7, де якби не відображалися елементи SVG при запуску. Додано простий $ ('body') [0] .style.webkitTransform = 'шкала (1)'; до коду ініціалізації, і проблема зникла!
Герк

Зараз це не працює на найновіших Safari та iOS.
setholopolus

@setholopolus, які версії (и) це були б?
EricG

23

Мені страждало те саме питання. Виправлення "перемикання дисплея" Данртона спрацювало для мене, коли його додали до крокової функції моєї анімації, але мене хвилювала продуктивність, і я шукав інші варіанти.

На мою обставину, елемент, який не перефарбовувався, знаходився в абсолютно позиційному елементі, який у той час не мав z-індексу. Додавання z-індексу до цього елемента змінило поведінку Chrome, і перефарбування відбулося так, як очікувалося -> анімація стала гладкою.

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

Ура, Роб

tl; dr >> Спробуйте додати z-індекс до елемента або батьківського елемента.


8
Блискуча. Це приголомшливо і вирішило для мене проблему. Знову A +++ буде з-індексувати.
trisweb

Не працював у моїй ситуації, займаючись прокрутками та перетвореннями.
Ріккі Бойс

16

Наступні роботи. Його потрібно встановити лише один раз у чистому CSS. І вона працює надійніше, ніж функція JS. Продуктивність видається без змін.

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

Схоже, це працює і для мене. Використовували це, щоб виправити проблему візуалізації з мобільним Safari
Кріс

Це виправляє мою проблему, змушує сафарі перепрощатися. Однак я використовував zoomзамість прокладки:@-webkit-keyframes safariBugFix {from { zoom: 100%; } to { zoom: 99.99999%; }}
Ахмед Мусаллам

13

Чомусь я не міг отримати відповідь Данортона на роботі, я міг бачити, що це потрібно зробити, тому я трохи переробив це:

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

і це працювало на мене.


7
Я спробував усе вище цього, але тільки цей працював на мене. Вебкіт WTF! Це не інформатика! Це чорна магія!
Чарлі Мартін

7

Я прийшов сюди, тому що мені потрібно було перемалювати смуги прокрутки в Chrome після зміни його css.

Якщо у когось така ж проблема, я вирішив її, зателефонувавши до цієї функції:

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

Цей метод не є найкращим рішенням, але він може працювати з усім, приховування та показ елемента, який потрібно переробити, може вирішити кожну проблему.

Ось скрипка, де я її використав: http://jsfiddle.net/promatik/wZwJz/18/


1
Чудово! Я також намагався анімувати прокрутки, і це те, що мені потрібно! Btw краще використовувати переповнення: накладення для анімованих
полос

6

Я натрапив на це сьогодні: 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 () на проблемному елементі безпосередньо. Іноді перемальовування батьківського елемента не вирішить проблему, яку переживає дитина.

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


1
appendChildє методом DOM, а не методом jQuery.
ChrisW

4

У мене була ця проблема з кількістю дівок, які були вставлені в інший div position: absolute, вкладені div не мали атрибута позиції. Коли я змінив це, position:relativeвін добре працював. (було дуже важко визначити проблему)

У моєму випадку елементи, куди вставлено Angular with ng-repeat.


1
Дякую! Це працювало і на мою проблему, і це набагато більше ваги, ніж у багатьох рішеннях JavaScript вище.
Dsyko

4

Я не можу повірити, що це все ще проблема в 2014 році. У мене просто виникла ця проблема, коли під час прокрутки оновив фіксовану коробку з фіксованою позицією в нижній лівій частині сторінки, підпис піддався «промахуванню». Спробувавши все вище без успіху, я помітив, що багато чого було або повільним / спричиняючи проблеми через створення дуже коротких ретрансляцій DOM тощо, викликаючи дещо неприродне почуття прокрутки тощо.

Я в кінцевому підсумку створив фіксовану позицію, повнорозмірний pointer-events: noneподіл і застосував відповідь дантортона до цього елемента, який, здається, примушує перемальовувати на весь екран, не втручаючись у 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';

Я не можу вам подякувати достатньо за ваше вирішення, яке єдине працювало на мене. Так, настав 2017 рік, і випуск триває. Моя проблема була пов'язана зі сторінкою мови RTL, яка зробить порожнім, якщо оновити її вручну на мобільних пристроях Android. Тут z-indexі pointer-eventsправила зайві.
Амін Моцафарі

Раніше я використовував виправлення дандортона, але справді боровся зі спалахом вмісту від налаштування відображення до жодного. Ваше рішення спрацювало для мене чудово, без флеш-контенту!
Джастін Ноель

3

Не те, що на це питання потрібна інша відповідь, але я виявив, що просто зміна кольору одним шматочком змусила перефарбуватись у моїй конкретній ситуації.

//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важливим стало переміщення другої зміни стилю поза поточним циклом подій.


1
Постановка тайм-ауту 0 виявилася корисною для мене в іншій, але подібній ситуації. Перемальовка не виникала до того, як ненав'язлива перевірка jquery застигла на екрані, коли вона розбирала велику форму. Думав, що я прокоментую, якщо хтось там опинився в одній ситуації. Інші рішення не працювали за цим сценарієм.
k29

2

Єдине рішення, яке працює для мене, схоже на відповідь sowasred2012:

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

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


2

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

Я не прихильник додавання та видалення класу, тому я спробував знайти спосіб вирішити це, і в кінцевому підсумку з’явився новий хак, який добре працює:

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

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

1

Оскільки, схоже, у кожного є свої проблеми та рішення, я подумав, що я додам щось, що працює для мене. На 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);
}

Ні мерехтіння екрана, ні справжнього оновлення, і прибирання після себе. Немає глобальних чи класових змінних, лише місцеві жителі. Здається, що браузери Mobile Safari / iPad або настільних ПК нічого не заважають.


1

Я працюю над іонною програмою html5, на декількох екранах я absoluteрозміщую елемент, коли прокручувати вгору чи вниз на пристроях IOS (iPhone 4,5,6, 6+) у мене була помилка перефарбовування.

Спробував багато рішень, жодне з них не працювало, окрім цього вирішити мою проблему.

Я використовую клас css .fixRepaintдля цих елементів абсолютних позицій

.fixRepaint{
    transform: translateZ(0);
}

Це вирішило мою проблему, можливо, допоможе хтось


1
Можливо, деякі, як я цього не бачив. @morewry Дякую за вказівку
Anjum ....

0

Це добре для 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);
}

0

Я знайшов цей метод корисним при роботі з переходами

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

0

хак "display / offsetHeight" не працював у моєму випадку, принаймні, коли він застосовувався до анімованого елемента.

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

проте я знайшов інше рішення:

  1. перед тим, як елемент почне анімувати, додайте клас, який визначає "-webkit-backface-visibility: hidden;" на елементі (я б також міг використовувати стиль вбудованого, я б здогадався)
  2. після закінчення анімації видаліть клас (або стиль)

це все ще досить гакітно (він використовує властивість CSS3 для примусової апаратної візуалізації), але, принаймні, це впливає лише на питання, про який йдеться, і працював для мене як на сафарі, так і на chrome на ПК та Mac.


0

Це, мабуть, пов’язано з цим: стиль jQuery не застосовується в Safari

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

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

Це змушує сафарі перемальовуватися.


Щоб змусити це працювати в Chrome, мені довелося додати: $ ('body'). AddClass ('dummyclass'). Delay (0) .removeClass ('dummyclass');
mbokil

0

вище пропозиції не працювали для мене. але нижче.

Хочете динамічно змінити текст усередині якоря. Слово "Пошук". Створений внутрішній тег "шрифт" з атрибутом id. Керував вмістом за допомогою javascript (нижче)

Пошук

вміст сценарію:

    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);
    }

0

У мене виникли проблеми із 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>

День і половина пізніше, коли трюкнув і вибухнув, і не задоволений пропонованими тут хакітними рішеннями, я виявив, що проблема викликана тим, що, здавалося, зберігає елемент у пам’яті під час малювання нового. Рішення полягало в тому, щоб зробити ідентифікатор linearGradient на SVG унікальним, хоча він використовувався лише один раз на сторінці.

Цього можна досягти різними способами, але для нашого кутового додатка ми використовували функцію lodash uniqueId, щоб додати змінну до області:

Кутова директива (JS):

scope.indicatorColourPatternId = _.uniqueId('IndicatorColourPattern');

Оновлення HTML:

Рядок 5: <linearGradient ng-attr-id="{{indicatorColourPatternId}}" x1="0" x2="0" y1="0" y2="1">

Рядок 11: <rect x="0" y="0" rx="5" ry="5" width="100%" height="100%" ng-attr-fill="url(#{{indicatorColourPatternId}})"/>

Я сподіваюсь, що ця відповідь економить комусь іншому дні, що варті розгрому клавіатури.


0

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

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

0

Я виявив, що додавання стилю вмісту до елемента змушує його перефарбовуватись, це має бути різним значенням кожного разу, коли ви хочете його перемальовувати, і не потрібно бути на псевдоелементі.

.selector {
    content: '1'
}

0

Я спробував відповісти більше, але це не працює на мене. У мене виникли проблеми мати однакову ширину клієнта з сафарі порівняно з іншими браузерами, і цей код вирішив проблему:

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

Це дійсно дивно, тому що якщо я спробую знову отримати клієнтську ширину після get_safe_value, я отримаю неправильне значення для сафарі, отримувач повинен бути між 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

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


0

Це змусить перефарбовуватись, уникаючи мерехтіння, наявного майстрування елементів та будь-якої проблеми з компонуванням ...

function forceRepaint() {
    requestAnimationFrame(()=>{
      const e=document.createElement('DIV');
      e.style='position:fixed;top:0;left:0;bottom:0;right:0;background:#80808001;\
               pointer-events:none;z-index:9999999';
      document.body.appendChild(e);
      requestAnimationFrame(()=>e.remove());  
    });
}

-1

Просте рішення з 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.


Проблема такого підходу полягає в тому, що ви втрачаєте будь-які обробники подій, які ви зареєстрували на об'єкті або його нащадках.
Хака
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.