Примушуйте DOM перемальовувати / оновлювати на Chrome / Mac


215

Раз у раз Chrome буде виводити ідеально правильний HTML / CSS неправильно або зовсім не. Копатися через інспектора DOM досить часто, щоб він зрозумів помилку своїх шляхів і правильно перемальовував, тому, очевидно, випадок, що розмітка хороша. Це трапляється досить часто (і передбачувано) в проекті, над яким я працюю, і я поставив код, щоб змусити перемальовувати певні обставини.

Це працює в більшості комбінацій браузера / os:

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

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

Поки що найкраще, що я придумав, щоб досягти такого ж ефекту на Chrome / Mac, це ця потворність:

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

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

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


Я зіткнувся з тією ж проблемою кілька хвилин тому. Я змінюю елемент на div (це був проміжок) і тепер браузер перемальовує зміни правильно. Я знаю, що це трохи старе, але це може допомогти якихось братах, я думаю.
Андрес Торес

2
Будь ласка, дивіться нижченаведену відповідь, що стосується непрозорості 0,99 - найкраща відповідь тут, - але її не просто знайти, оскільки вона настільки глибока на сторінці.
Грушаль

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
}

EDIT: У відповідь на Šime Vidas, що ми досягаємо тут, було б вимушеним відливом. Ви можете дізнатися більше від самого майстра http://paulirish.com/2011/dom-html5-css3-performance/


3
Афаїку, неможливо перемалювати лише частину вікна перегляду. Браузер завжди перемальовує всю веб-сторінку.
Шіме Відас

38
замість $ ('# parentOfElementToBeRedrawn'). hid (). show (); Мені потрібно .hide.show (0) для належної роботи в Chrome
l.poellabauer

1
@ l.poellabauer тут же - це не має сенсу ... але дякую. Як чорт ти дізнався ??? :)
JohnIdol

6
FYI, відповідним напрямком, над яким я працював, був нескінченний список прокрутки. Це було трохи непрацездатним , щоб приховати / показати всі UL, так що я грав і виявив , що якщо ви .hide().show(0)на якому - або елементі (я вибрав колонтитул), слід оновити цю сторінку.
дерево дерево

1
Можна перемалювати лише частину вікна перегляду. Для цього немає API, але браузер може зробити це. Для початку віконний огляд намальований плитками. А композитні шари фарбуються самостійно.
morewry

62

Жодна з наведених відповідей не працювала для мене. Я помітив, що зміна розміру вікна викликала перемальовування. Так це зробило для мене:

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

11
підказка: це перемальовує ваше повне вікно, що дорожче виконання і, мабуть, не те, чого хоче ОП
hereandnow78

7
Змінення розміру вікна завжди викликає поповнення, але код $(window).trigger('resize')не робить, він видає лише подію "зміни розміру", яка запускає відповідний обробник, якщо він призначений.
guari

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

30

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

.willnotrender { 
   transform: translateZ(0); 
}

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


1
для мене це спрацювало чудово, коли проблемою було видалений HTML-елемент, який малюється на полотні, навіть незважаючи на те, що полотно постійно анімовується.
Knaģis

Це вирішило проблему з макетом, з якою я мав neon-animated-pages. Дякую: +1:
coffeesaga

1
Це спрацювало. Сафарі залишав елементи, які там, де в діві встановлюють жодне, видиме і не може бути вибране. Зміна розміру вікна змусила їх зникнути. Додавання цієї трансформації призвело до зникнення "артефактів", як очікувалося.
Джефф

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, коли елемент не видно.
Grodriguez

Це рішення ідеально підходить для вирішення проблеми з помилкою, з якою я стикався в браузері на базі хрому, який використовується у "overolf". Я намагався розібратися, як це зробити, і ти врятував мене.
nacitar sevaht

13

Це працює для мене. Кудосі йдуть сюди .

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

$(el).redraw();

1
Але це залишило деякі "залишки", такі як "display: block" тощо, які іноді можуть зруйнувати структуру сторінки.
pie6k

По суті, це має бути те саме, що$().hide().show(0)
Ман

@Mahn ти це спробував? На мій погляд, .hide () не блокує, тому він би пропустив запуск повторного візуалізації в браузері, так само як і весь пункт методу. (І якщо я правильно пам’ятаю, тоді я перевірив це.)
zupa

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

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

9

Якщо приховати елемент, а потім знову показати його протягом setTimeout 0, примусить перемальовуватися.

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

1
Цей працював для помилки Chrome, де фільтри SVG іноді не перефарбовуються, bugs.chromium.org/p/chromium/isissue/detail?id=231560 . Час очікування був важливим.
Джейсон Д

Це те, що працювало на мене, намагаючись примусити Chrome перерахувати значення, для myElement.getBoundingClientRect()яких кеширувались, ймовірно, для оптимізації / продуктивності. Я просто додав нульовий миллисекундний тайм-аут для функції, яка додає новий елемент до DOM і отримує нові значення після того, як старий елемент з попередніми (кешованими) значеннями був видалений з DOM.
TheDarkIn1978

6

Це, здається, робить для мене трюк. Плюс, це насправді зовсім не показується.

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

Він працював для мене у Chromium Version 60.0.3112.113 щодо цієї помилки: Випуск 727076 . Ефективна і тонка; велике дякую.
Lonnie Best

@ wiktus239 Можливо, 20 мільйонів занадто швидко, і браузер не встигає перейти на 0,99, перш ніж час повернутися до 1,0? - Цей фокус з непрозорістю працює для мене в будь-якому випадку, щоб зробити вміст у iframe помітним на iPhone iOS 12 Safari, і я перемикаю кожні 1 000 міліс. Це також те, що у пов'язаному номері 727076 у коментарі вище (1000 мільйонів).
KajMagnus

4

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


2
не працювало для мене, але це, мабуть, тому, що мені потрібна була offsetHeightвластивість, яка не є css.
Себас

3

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

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

jQuery Класс переключення

$('.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();

Примітка: за допомогою останнього webkit / blink зберігання значення offsetHeightє обов'язковим для того, щоб викликати перефарбування, інакше VM (для оптимізації), ймовірно, пропустить цю інструкцію.

Оновлення: додано друге 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.