Paksa DOM menggambar ulang / menyegarkan di Chrome / Mac


215

Sekali-sekali, Chrome akan membuat HTML / CSS yang valid dengan benar salah atau tidak sama sekali. Menggali melalui inspektur DOM sering cukup untuk membuatnya menyadari kesalahan cara dan menggambar ulang dengan benar, sehingga terbukti bahwa markupnya baik. Ini sering terjadi (dan dapat diprediksi) cukup dalam proyek yang sedang saya kerjakan sehingga saya telah menempatkan kode untuk memaksa redraw dalam keadaan tertentu.

Ini berfungsi di sebagian besar kombinasi browser / os:

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

Seperti pada, atur beberapa properti CSS yang tidak digunakan, lalu minta beberapa informasi yang memaksa redraw, lalu unduh properti tersebut. Sayangnya, tim yang cemerlang di belakang Chrome untuk Mac tampaknya telah menemukan cara untuk mendapatkan offsetHeight itu tanpa menggambar ulang. Dengan demikian membunuh hack yang bermanfaat.

Sejauh ini, yang terbaik yang saya hasilkan untuk mendapatkan efek yang sama pada Chrome / Mac adalah keburukan ini:

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

Seperti dalam, sebenarnya memaksa elemen untuk melompat sedikit, lalu dinginkan sebentar dan melompat kembali. Yang lebih parah, jika Anda menjatuhkan batas waktu di bawah 500 ms (ke tempat yang kurang terlihat), sering kali tidak akan memiliki efek yang diinginkan, karena browser tidak akan berkeliling untuk menggambar ulang sebelum kembali ke keadaan semula.

Adakah yang mau menawarkan versi yang lebih baik dari hack redraw / refresh ini (lebih disukai berdasarkan contoh pertama di atas) yang bekerja di Chrome / Mac?


Saya mengalami masalah yang sama beberapa menit yang lalu. Saya mengubah elemen untuk div (itu rentang) dan sekarang browser menggambar perubahan dengan benar. Saya tahu ini agak tua tetapi ini dapat membantu beberapa bros di luar sana saya pikir.
Andrés Torres

2
Silakan lihat jawaban di bawah ini terkait dengan opacity 0,99 - jawaban terbaik di sini - tetapi tidak mudah ditemukan karena begitu dalam pada halaman.
Grouchal

1
Ini terjadi di Linux juga :-(
schlingel

1
Anda dapat mengganti batas waktu dengan requestAnimationFrame, dalam hal ini Anda akan mencapai hal yang sama tetapi dengan lag 16ms, bukan 1000ms.
trusktr

3
Harap ajukan masalah Chromium untuk rendering yang tidak valid. Tampaknya tidak ada yang melakukannya, meskipun ini sudah 4 tahun yang lalu.
Bardi Harborow

Jawaban:


183

Tidak yakin persis apa yang ingin Anda capai tetapi ini adalah metode yang saya gunakan di masa lalu dengan keberhasilan memaksa browser untuk menggambar ulang, mungkin itu akan berhasil untuk Anda.

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

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

Jika gambar sederhana ini tidak berfungsi, Anda dapat mencoba yang ini. Ini menyisipkan simpul teks kosong ke dalam elemen yang menjamin redraw.

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: Menanggapi Šime Vidas apa yang kami capai di sini akan menjadi reflow paksa. Anda dapat mengetahui lebih lanjut dari master sendiri http://paulirish.com/2011/dom-html5-css3-performance/


3
Afaik, tidak mungkin untuk menggambar ulang hanya sebagian dari viewport. Browser selalu menggambar ulang seluruh halaman web.
Šime Vidas

38
bukannya $ ('# parentOfElementToBeRedrawn'). sembunyikan (). show (); Saya membutuhkan .hide.show (0) agar berfungsi dengan baik di Chrome
l.poellabauer

1
@ l.poellabauer sama di sini - ini tidak masuk akal ... tapi terima kasih. Bagaimana kamu bisa tahu ??? :)
JohnIdol

6
FYI, area relevan yang sedang saya kerjakan adalah daftar gulir yang tak terbatas. Agak tidak bisa dilakukan untuk menyembunyikan / menampilkan seluruh UL, jadi saya bermain-main dan menemukan bahwa jika Anda melakukan .hide().show(0)pada elemen apa pun (saya memilih footer halaman) itu harus menyegarkan halaman.
treeface

1
Dimungkinkan untuk menggambar ulang hanya sebagian dari viewport. Tidak ada API untuk melakukannya, tetapi browser dapat memilih untuk melakukannya. Pada awalnya viewport dicat dengan ubin. Dan lapisan komposit dicat secara independen.
buruk

62

Tidak ada jawaban di atas yang berfungsi untuk saya. Saya memang memperhatikan bahwa mengubah ukuran jendela saya memang menyebabkan redraw. Jadi ini berhasil untuk saya:

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

11
petunjuk: ini akan menggambar ulang jendela lengkap Anda, yang merupakan kinerja mahal dan mungkin bukan apa yang diinginkan OP
hereandnow78

7
Mengubah ukuran jendela selalu memicu reflow tetapi kode $(window).trigger('resize')tidak, itu hanya melempar peristiwa 'mengubah ukuran' yang memicu penangan terkait jika ditugaskan.
guari

1
Anda menyelamatkan saya seminggu penuh! Masalah saya adalah setelah mengatur <body style = "zoom: 67%">, halaman diperbesar ke tingkat yang saya harapkan, tetapi halaman dibekukan dan tidak dapat digulir !. Jawaban Anda segera memecahkan masalah ini
user1143669

30

Kami baru-baru ini menemukan ini dan menemukan bahwa mempromosikan elemen yang terpengaruh ke lapisan komposit dengan translateZ memperbaiki masalah tanpa memerlukan javascript tambahan.

.willnotrender { 
   transform: translateZ(0); 
}

Karena masalah melukis ini sebagian besar muncul di Webkit / Blink, dan perbaikan ini sebagian besar menargetkan Webkit / Blink, dalam beberapa kasus lebih baik. Terutama karena banyak solusi JavaScript menyebabkan reflow dan pengecatan ulang , bukan hanya pengecatan ulang.


1
ini bekerja dengan baik bagi saya ketika masalah adalah elemen HTML yang dihapus digambar di atas kanvas meskipun kanvas terus menerus dianimasikan.
Knaģis

Ini memperbaiki masalah tata letak yang saya alami neon-animated-pages. Terima kasih: +1:
coffeesaga

1
Ini berhasil. Safari meninggalkan elemen yang diatur dalam div untuk menampilkan tidak ada, terlihat dan tidak dapat dipilih. Mengubah ukuran jendela membuat mereka menghilang. Menambahkan transformasi ini menyebabkan "artefak" menghilang seperti yang diharapkan.
Jeff Mattson

28

Solusi ini tanpa batas waktu! Redraw kekuatan nyata ! Untuk Android dan iOS.

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

1
Ini tidak berfungsi untuk saya di Chrome atau FF di Linux. Namun hanya mengakses element.offsetHeight (tanpa mengubah properti tampilan) tidak berfungsi. Ini seperti browser akan melewatkan perhitungan offsetHeight ketika elemen tidak terlihat.
Grodriguez

Solusi ini berfungsi sempurna untuk mengatasi bug yang saya temui di browser berbasis chrome yang digunakan dalam "overwolf". Saya mencoba mencari cara untuk melakukan ini dan Anda menyelamatkan saya upaya.
nacitar sevaht

13

Ini bekerja untuk saya. Kudos pergi ke sini .

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

$(el).redraw();

1
Tetapi ia meninggalkan beberapa 'sisa' seperti 'tampilan: blok' dll yang terkadang dapat menghancurkan struktur halaman.
pie6k

Ini pada dasarnya harus sama dengan$().hide().show(0)
Mahn

@ Ahn apakah Anda mencobanya? Dalam dugaan saya. Menyembunyikan () adalah non-pemblokiran sehingga akan melewatkan proses rendering ulang di browser alias seluruh titik metode. (Dan jika saya ingat dengan benar, saya mengujinya saat itu.)
zupa

2
@ zupa telah diuji dan bekerja di Chrome 38. Caranya adalah show()berbeda dari yang show(0)di mana dengan menentukan durasi di yang terakhir, itu dipicu oleh metode animasi jQuery dalam batas waktu, bukan segera yang memberikan waktu untuk membuat dengan cara yang sama yang hide(0, function() { $(this).show(); })tidak .
Mahn

@ Ah, oke, tangkapan yang bagus. Saya pasti untuk solusi Anda jika ini bekerja lintas platform dan bukan fitur jQuery terbaru. Karena saya tidak punya waktu untuk mengujinya, saya akan menambahkannya sebagai 'mungkin berhasil'. Anda dipersilakan untuk mengedit jawaban jika Anda pikir jawaban di atas berlaku.
zupa

9

Menyembunyikan elemen dan kemudian menampilkannya lagi dalam setTimeout 0 akan memaksa redraw.

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

1
Yang ini berfungsi untuk bug Chrome di mana filter SVG terkadang tidak mengecat ulang, bugs.chromium.org/p/chromium/issues/detail?id=231560 . Batas waktu itu penting.
Jason D

Ini yang bekerja untuk saya ketika mencoba memaksa Chrome untuk menghitung ulang nilai untuk myElement.getBoundingClientRect(), yang sedang di-cache, kemungkinan untuk keperluan optimasi / kinerja. Saya hanya menambahkan batas waktu nol milidetik untuk fungsi yang menambahkan elemen baru ke DOM dan mendapatkan nilai baru setelah elemen lama dengan nilai sebelumnya (di-cache) telah dihapus dari DOM.
TheDarkIn1978

6

Ini sepertinya melakukan trik untuk saya. Plus, itu benar-benar tidak terlihat sama sekali.

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

Ini bekerja untuk saya di Chromium Versi 60.0.3112.113 mengenai bug ini: Isu 727.076 . Efisien dan halus; terimakasih banyak.
Lonnie Best

@ wiktus239 Mungkin 20 millis terlalu cepat dan browser tidak punya waktu untuk berubah ke .99, sebelum waktu untuk kembali ke 1.0? - Trik opacity ini berfungsi bagi saya, untuk membuat konten dalam iframe terlihat di iPhone iOS 12 Safari, dan saya beralih setiap 1.000 mili. Itu juga apa yang ada dalam masalah tertaut 727076 dalam komentar di atas (1000 mili).
KajMagnus

4

panggilan window.getComputedStyle()harus memaksa reflow


2
tidak bekerja untuk saya, tapi itu mungkin karena saya membutuhkan offsetHeightproperti yang bukan css.
Sebas

3

Saya mengalami tantangan ini hari ini di OSX El Capitan dengan Chrome v51. Halaman yang dipermasalahkan bekerja dengan baik di Safari. Saya mencoba hampir setiap saran pada halaman ini - tidak ada yang berhasil - semua memiliki efek samping ... Saya akhirnya menerapkan kode di bawah ini - super sederhana - tidak ada efek samping (masih berfungsi seperti sebelumnya di Safari).

Solusi: Beralih kelas pada elemen yang bermasalah sesuai kebutuhan. Setiap toggle akan memaksa redraw. (Saya menggunakan jQuery untuk kenyamanan, tetapi JavaScript vanilla seharusnya tidak menjadi masalah ...)

jQuery Class Toggle

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

Kelas CSS

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

Dan itu saja ...

CATATAN: Anda harus menjalankan cuplikan di bawah "Halaman Penuh" untuk melihat efeknya.

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

panggil fungsi ini setelah 500 milidetik.


2

Jika Anda ingin mempertahankan gaya yang dinyatakan dalam lembar gaya Anda, lebih baik menggunakan sesuatu seperti:

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: dengan webkit / blink terbaru, menyimpan nilai offsetHeightwajib untuk memicu pengecatan ulang, jika tidak VM (untuk keperluan optimasi) mungkin akan melewati instruksi itu.

Pembaruan: menambahkan offsetHeightpembacaan kedua , perlu untuk mencegah browser dari antrian / caching perubahan properti / kelas CSS berikut dengan mengembalikan displaynilai (dengan cara ini transisi CSS yang dapat mengikuti harus dirender)


1

Contoh 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

    };

fungsi panggilan:

$('article.child').redraw(); // <== ide buruk

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

juga bekerja dengan .fadeIn(1)bukan .show(300)untuk saya - tidak mendorong elemen di sekitar. Jadi saya kira, itu adalah pemicu display:nonedan kembali keblock
BananaAcid

0

Suatu pendekatan yang bekerja untuk saya di IE (saya tidak bisa menggunakan teknik tampilan karena ada input yang tidak boleh kehilangan fokus)

Ini berfungsi jika Anda memiliki 0 margin (mengubah padding juga berfungsi)

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

0

Hanya CSS. Ini berfungsi untuk situasi di mana elemen anak dihapus atau ditambahkan. Dalam situasi ini, batas dan sudut bundar dapat meninggalkan artefak.

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

0

Perbaikan saya untuk IE10 + IE11. Pada dasarnya yang terjadi adalah Anda menambahkan DIV di dalam elemen pembungkus yang harus dihitung ulang. Kemudian cukup keluarkan dan voila; bekerja seperti pesona :)

    _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

Sebagian besar jawaban membutuhkan penggunaan batas waktu asynchroneous, yang menyebabkan kedipan yang mengganggu.

Tapi saya datang dengan yang ini, yang bekerja dengan lancar karena sinkron:

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

Apakah ada peristiwa terlampir pada elemen-elemen ini yang masih berfungsi?
Kerry Johnson

0

Ini solusi saya yang berfungsi untuk menghilangkan konten ...

<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

Saya mengalami masalah serupa dan garis JS sederhana ini membantu memperbaikinya:

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

Dalam kasus saya itu adalah bug dengan ekstensi Chrome yang tidak menggambar ulang halaman setelah mengubah CSS-nya dari dalam ekstensi.


4
Ini merusak aksesibilitas keyboard.
Pangamma

0

Saya ingin mengembalikan semua status ke status sebelumnya (tanpa memuat ulang) termasuk elemen yang ditambahkan oleh jquery. Implementasi di atas tidak akan berhasil. dan saya lakukan sebagai berikut.

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

Saya memiliki daftar komponen reaksi yang ketika digulir, kemudian membuka halaman lain, kemudian ketika kembali daftar tidak diberikan pada Safari iOS sampai halaman digulir. Jadi ini adalah perbaikannya.

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

0

Di bawah ini css berfungsi untuk saya di IE 11 dan Edge, tidak perlu JS. scaleY(1)lakukan triknya di sini. Tampaknya solusi paling sederhana.

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

0

Itu membantu saya

domNodeToRerender.style.opacity = 0.99;
setTimeout(() => { domNodeToRerender.style.opacity = '' }, 0);

0

2020: Lebih ringan dan lebih kuat

Solusi sebelumnya tidak berfungsi lagi untuk saya.

Saya kira browser mengoptimalkan proses menggambar dengan mendeteksi semakin banyak perubahan "tidak berguna".

Solusi ini membuat browser menggambar klon untuk mengganti elemen asli. Ini bekerja dan mungkin lebih berkelanjutan:

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

diuji pada Chrome 80 / Edge 80 / Firefox 75

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.