Forza ridisegno / aggiornamento DOM su Chrome / Mac


216

Ogni tanto Chrome esegue il rendering di HTML / CSS perfettamente validi in modo errato o per niente. Scavare attraverso l'ispettore DOM è spesso sufficiente per renderlo consapevole dell'errore dei suoi modi e ridisegnare correttamente, quindi è dimostrabilmente il caso che il markup sia buono. Questo accade frequentemente (e prevedibilmente) abbastanza in un progetto a cui sto lavorando che ho messo il codice in atto per forzare un ridisegno in determinate circostanze.

Funziona nella maggior parte delle combinazioni browser / os:

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

Come in, modifica alcune proprietà CSS inutilizzate, quindi chiedi alcune informazioni che impongono un ridisegno, quindi annulla la proprietà. Sfortunatamente, il brillante team dietro Chrome per Mac sembra aver trovato un modo per ottenere quell'offsetHeight senza ridisegnare. Uccidendo così un trucco altrimenti utile.

Finora, il migliore che ho ideato per ottenere lo stesso effetto su Chrome / Mac è questo brutto aspetto:

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

Come in, forzare effettivamente l'elemento a saltare un po ', quindi raffreddare un secondo e saltarlo indietro. A peggiorare la situazione, se si abbassa quel timeout al di sotto di 500 ms (dove sarebbe meno evidente), spesso non avrà l'effetto desiderato, poiché il browser non tornerà al ridisegno prima di tornare allo stato originale.

Qualcuno vuole offrire una versione migliore di questo hack di ridisegno / aggiornamento (preferibilmente basato sul primo esempio sopra) che funziona su Chrome / Mac?


Ho riscontrato lo stesso problema qualche minuto fa. Cambio l'elemento per un div (era un intervallo) e ora il browser ridisegna le modifiche correttamente. So che è un po 'vecchio ma questo può aiutare alcuni fratelli là fuori penso.
Andrés Torres,

2
Si prega di vedere la risposta di seguito relativa all'opacità 0,99 - la migliore risposta qui - ma non è facile da trovare in quanto è così profonda nella pagina.
Grouchal,

1
Questo succede anche su Linux :-(
schlingel

1
Puoi sostituire il timeout con requestAnimationFrame, nel qual caso otterrai la stessa cosa ma con un ritardo di 16 ms anziché 1000 ms.
trusktr,

3
Si prega di presentare un problema di Chromium per il rendering non valido. Sembra che nessuno lo abbia fatto, nonostante ciò sia stato 4 anni fa.
Bardi Harborow,

Risposte:


183

Non so esattamente cosa stai cercando di ottenere, ma questo è un metodo che ho usato in passato con successo per forzare il ridisegno del browser, forse funzionerà per te.

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

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

Se questo semplice ridisegno non funziona, puoi provare questo. Inserisce un nodo di testo vuoto nell'elemento che garantisce un ridisegno.

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: In risposta a Šime Vidas ciò che stiamo ottenendo qui sarebbe un riflusso forzato. Puoi saperne di più dal maestro stesso http://paulirish.com/2011/dom-html5-css3-performance/


3
Dopo tutto, non è possibile ridisegnare solo una parte del riquadro di visualizzazione. Il browser ridisegna sempre l'intera pagina Web.
Šime Vidas,

39
invece di $ ('# parentOfElementToBeRedrawn'). hide (). show (); Avevo bisogno di .hide.show (0) per funzionare correttamente in Chrome
l.poellabauer,

1
@ l.poellabauer stesso qui - questo non ha senso ... ma grazie. Come diamine hai scoperto ??? :)
JohnIdol,

6
Cordiali saluti, l'area rilevante su cui stavo lavorando era una lista di scorrimento infinita. È stato un po 'impraticabile nascondere / mostrare l'intero UL, quindi ho giocato e ho scoperto che se lo fai .hide().show(0)su qualsiasi elemento (ho scelto il piè di pagina) dovrebbe aggiornare la pagina.
treeface

1
È possibile ridisegnare solo una parte della finestra. Non esiste un'API per farlo, ma il browser può scegliere di farlo. La finestra è dipinta in piastrelle per cominciare. E gli strati compositi sono dipinti indipendentemente.
altro

62

Nessuna delle risposte sopra ha funzionato per me. Ho notato che il ridimensionamento della mia finestra ha causato un ridisegno. Quindi questo ha fatto per me:

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

11
suggerimento: questo ridisegnerà la tua finestra completa, che è costosa in termini di prestazioni e probabilmente non è ciò che OP vuole
hereandnow78

7
Il ridimensionamento della finestra attiva sempre un riflusso, ma il codice $(window).trigger('resize')no, genera solo l'evento 'ridimensiona' che attiva il relativo gestore se assegnato.
guari,

1
Mi salvi un'intera settimana! Il mio problema è dopo aver impostato <body style = "zoom: 67%">, la pagina è stata ingrandita al livello previsto, ma la pagina è bloccata e non può scorrere !. La tua risposta ha risolto immediatamente questo problema
user1143669

30

Di recente abbiamo riscontrato questo e scoperto che la promozione dell'elemento interessato su un livello composito con translateZ ha risolto il problema senza bisogno di javascript extra.

.willnotrender { 
   transform: translateZ(0); 
}

Poiché questi problemi di pittura si manifestano principalmente in Webkit / Blink e questa correzione riguarda principalmente Webkit / Blink, in alcuni casi è preferibile. Soprattutto perché molte soluzioni JavaScript causano un riflusso e una riverniciatura , non solo una riverniciatura.


1
questo ha funzionato bene per me quando il problema era un elemento HTML rimosso che veniva disegnato su una tela anche se la tela veniva continuamente animata.
Knaģis,

Questo risolto un problema di layout che avevo riscontrato neon-animated-pages. Grazie: +1:
coffeesaga,

1
Questo ha funzionato. Safari stava lasciando elementi che in un div erano impostati per non visualizzarne nessuno, visibili e non selezionabili. Il ridimensionamento della finestra li ha fatti sparire. L'aggiunta di questa trasformazione ha causato la scomparsa degli "artefatti" come previsto.
Jeff Mattson,

28

Questa soluzione senza timeout! Ridisegno della forza reale ! Per Android e iOS.

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

1
Questo non funziona per me su Chrome né FF su Linux. Tuttavia il semplice accesso element.offsetHeight (senza modificare la proprietà di visualizzazione) fa lavoro. È come se il browser salta il calcolo offsetHeight quando l'elemento non è visibile.
Grodriguez,

Questa soluzione funziona perfettamente per aggirare un bug che ho riscontrato nel browser basato su Chrome utilizzato in "overwolf". Stavo cercando di capire come farlo e mi hai risparmiato lo sforzo.
nacitar sevaht

13

Questo funziona per me. Complimenti vai qui .

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

$(el).redraw();

1
Ma ha lasciato alcuni 'resti' come 'display: block' ecc. Che a volte possono distruggere la struttura della pagina.
pie6k,

Questo dovrebbe essenzialmente essere lo stesso di$().hide().show(0)
Mahn,

@Mahn l'hai provato? A mio avviso, .hide () non è bloccante, quindi salterebbe l'esecuzione del rendering nel browser, ovvero l'intero punto del metodo. (E se ricordo bene, l'ho provato allora.)
zupa

2
@zupa è testato e funziona su Chrome 38. Il trucco è show()diverso dal fatto show(0)che, specificando una durata in quest'ultimo, viene attivato dai metodi di animazione jQuery in un timeout piuttosto che immediatamente che dà il tempo di renderizzare nello stesso modo in cui lo hide(0, function() { $(this).show(); })fa .
Mahn,

@Mahn ok allora bella cattura allora. Sono sicuramente per la tua soluzione se funziona su più piattaforme e non è una funzionalità jQuery recente. Poiché non ho il tempo di provarlo, lo aggiungerò come "potrebbe funzionare". Puoi modificare la risposta se ritieni che si applichi quanto sopra.
zupa,

9

Nascondere un elemento e poi mostrarlo di nuovo all'interno di un setTimeout di 0 forzerà un ridisegno.

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

1
Questo ha funzionato per un bug di Chrome in cui i filtri SVG a volte non ridipingono, bugs.chromium.org/p/chromium/issues/detail?id=231560 . Il timeout è stato importante.
Jason D,

Questo è quello che ha funzionato per me mentre cercavo di forzare Chrome a ricalcolare i valori per i myElement.getBoundingClientRect()quali venivano memorizzati nella cache, probabilmente per scopi di ottimizzazione / prestazioni. Ho semplicemente aggiunto un timeout di zero millisecondi per la funzione che aggiunge il nuovo elemento al DOM e ottiene i nuovi valori dopo che il vecchio elemento con i valori precedenti (memorizzati nella cache) è stato rimosso dal DOM.
TheDarkIn1978,

6

Questo sembra fare il trucco per me. Inoltre, in realtà non mostra affatto.

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

Ha funzionato per me nella versione 60.0.3112.113 di Chromium riguardo a questo errore: Numero 727076 . Efficiente e sottile; grazie molto.
Lonnie Best

@ wiktus239 Forse 20 millis sono troppo veloci e il browser non ha il tempo di passare a .99, prima che il tempo torni a 1.0? - Questo trucco di opacità funziona comunque per me, per rendere visibili i contenuti di un iframe in iPhone iOS 12 Safari, e io commuto ogni 1000 millis. Questo è anche ciò che è nel problema collegato 727076 nel commento sopra (1000 millis).
KajMagnus

4

la chiamata window.getComputedStyle()dovrebbe forzare un riflusso


2
non ha funzionato per me, ma probabilmente perché avevo bisogno della offsetHeightproprietà che non è CSS.
Sebas,

3

Ho incontrato questa sfida oggi in OSX El Capitan con Chrome v51. La pagina in questione ha funzionato bene in Safari. Ho provato quasi tutti i suggerimenti in questa pagina - nessuno ha funzionato bene - tutti hanno avuto effetti collaterali ... Ho finito per implementare il codice qui sotto - super semplice - senza effetti collaterali (funziona ancora come prima in Safari).

Soluzione: attiva una classe sull'elemento problematico secondo necessità. Ogni comando forzerà un ridisegno. (Ho usato jQuery per comodità, ma JavaScript vaniglia non dovrebbe essere un problema ...)

Attiva / disattiva classe jQuery

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

Classe CSS

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

E questo è tutto ...

NOTA: per visualizzare l'effetto devi eseguire lo snippet sotto "Pagina intera".

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

chiama questa funzione dopo 500 millisecondi.


2

Se vuoi preservare gli stili dichiarati nei tuoi fogli di stile, è meglio usare qualcosa come:

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: con l'ultimo webkit / lampeggio, la memorizzazione del valore di offsetHeightè obbligatoria al fine di attivare la riverniciatura, altrimenti la VM (a fini di ottimizzazione) salterà probabilmente tale istruzione.

Aggiornamento: aggiunta la seconda offsetHeightlettura, è necessario impedire al browser di accodare / memorizzare nella cache una seguente modifica di proprietà / classe CSS con il ripristino del displayvalore (in questo modo dovrebbe essere visualizzata una transizione CSS che può seguire)


1

HTML di esempio:

<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

    };

funzione di chiamata:

$('article.child').redraw(); // <== cattiva idea

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

funziona anche con .fadeIn(1)anziché .show(300)per me, senza spingere l'elemento in giro. Quindi immagino sia l' display:noneblock
inizio

0

Un approccio che ha funzionato per me su IE (non potevo usare la tecnica di visualizzazione perché c'era un input che non doveva perdere la messa a fuoco)

Funziona se hai 0 margini (cambiando anche il padding funziona)

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

0

Solo CSS. Funziona in situazioni in cui un elemento figlio viene rimosso o aggiunto. In queste situazioni, i bordi e gli angoli arrotondati possono lasciare artefatti.

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

0

La mia correzione per IE10 + IE11. Fondamentalmente ciò che accade è che aggiungi un DIV all'interno di un elemento di avvolgimento che deve essere ricalcolato. Quindi rimuovilo e voilà; funziona come un fascino :)

    _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

La maggior parte delle risposte richiede l'uso di un timeout asincrono, che provoca un fastidioso battito di ciglia.

Ma ho pensato a questo, che funziona senza intoppi perché è sincrono:

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

Eventuali eventi collegati a questi elementi funzionerebbero ancora?
Kerry Johnson,

0

Questa è la mia soluzione che ha funzionato per la scomparsa dei contenuti ...

<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

Ho riscontrato un problema simile e questa semplice linea di JS ha contribuito a risolverlo:

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

Nel mio caso si trattava di un bug con un'estensione di Chrome che non ridisegnava la pagina dopo aver cambiato il CSS dall'interno.


4
Questa è la rottura dell'accessibilità della tastiera.
Pangamma,

0

Volevo riportare tutti gli stati allo stato precedente (senza ricaricare) compresi gli elementi aggiunti da jquery. L'implementazione di cui sopra non funzionerà. e ho fatto come segue.

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

Avevo un elenco di componenti di reazione che, una volta fatto scorrere, apriva un'altra pagina, quindi quando tornava la lista non veniva visualizzata su Safari iOS fino a quando la pagina non veniva fatta scorrere. Quindi questa è la soluzione.

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

0

Di seguito css funziona per me su IE 11 e Edge, non è necessario JS. scaleY(1)fa il trucco qui. Sembra la soluzione più semplice.

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

0

Mi ha aiutato

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

0

2020: più leggero e più forte

Le soluzioni precedenti non funzionano più per me.

Immagino che i browser ottimizzino il processo di disegno rilevando sempre più cambiamenti "inutili".

Questa soluzione consente al browser di disegnare un clone per sostituire l'elemento originale. Funziona ed è probabilmente più sostenibile:

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

testato su Chrome 80 / Edge 80 / Firefox 75

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.