Forçar redesenho / atualização de DOM no Chrome / Mac

De vez em quando, o Chrome processa HTML / CSS perfeitamente válido ou não é de todo correto. Explorar o inspetor DOM geralmente é o suficiente para que ele perceba o erro de suas formas e seja redesenhado corretamente, então é provável que a marcação seja boa. Isso acontece com freqüência (e previsivelmente) o suficiente em um projeto em que estou trabalhando que eu coloquei código no lugar para forçar um redesenho em determinadas circunstâncias.

Isso funciona na maioria das combinações de navegador / sistema operacional:

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

Como em, ajuste algumas propriedades CSS não usadas, em seguida, peça algumas informações que forcem um redesenho e, em seguida, desmarque a propriedade. Infelizmente, a equipe inteligente por trás do Chrome para Mac parece ter encontrado uma maneira de obter esse offsetHeight sem redesenhar. Assim, matando um hack de outra forma útil.

Até agora, o melhor que eu criei para obter o mesmo efeito no Chrome / Mac é esse tipo de feiura:

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

Como, na verdade, forçar o elemento a pular um pouco, depois resfriar um segundo e pular de volta. Para piorar, se você deixar cair esse tempo limite abaixo de 500 ms (para onde seria menos perceptível), muitas vezes não terá o efeito desejado, já que o navegador não será redesenhado antes de retornar ao seu estado original.

Alguém se importa em oferecer uma versão melhor desse hack de redesenho / atualização (de preferência baseado no primeiro exemplo acima) que funciona no Chrome / Mac?

Não sei exatamente o que você está tentando alcançar, mas este é um método que eu usei no passado com sucesso para forçar o navegador a redesenhar, talvez funcione para você.

 // in jquery $('#parentOfElementToBeRedrawn').hide().show(0); // in plain js document.getElementById('parentOfElementToBeRedrawn').style.display = 'none'; document.getElementById('parentOfElementToBeRedrawn').style.display = 'block'; 

Se esse simples redesenho não funcionar, você pode tentar este. Ele insere um nó de texto vazio no elemento que garante um redesenho.

 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: Em resposta a Šime Vidas o que estamos conseguindo aqui seria um restream forçado. Você pode descobrir mais do próprio mestre http://paulirish.com/2011/dom-html5-css3-performance/

Nenhuma das respostas acima funcionou para mim. Eu notei que resize minha janela causou um redesenho. Então, isso fez isso por mim:

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

Esta solução sem tempos limite! Força real redesenhar! Para Android e iOS.

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

Recentemente, encontramos isso e descobrimos que promover o elemento afetado em uma camada composta com o translateZ resolveu o problema sem precisar de um javascript extra.

 .willnotrender { transform: translateZ(0); } 

Como esses problemas de pintura aparecem principalmente no Webkit / Blink, e essa correção atinge principalmente o Webkit / Blink, é preferível em alguns casos. Especialmente porque muitas soluções JavaScript causam um restream e repintura , não apenas uma repintura.

Isso funciona para mim. O elogio vai aqui .

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

Ocultar um elemento e depois mostrá-lo novamente dentro de um setTimeout de 0 forçará um redesenho.

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

Isso parece fazer o truque para mim. Além disso, realmente não mostra nada.

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

chamada window.getComputedStyle() deve forçar um restream

Eu me deparei com esse desafio hoje no OSX El Capitan com o Chrome v51. A página em questão funcionou bem no Safari. Eu tentei quase todas as sugestões nesta página – nenhuma funcionou direito – todos tinham efeitos colaterais … Acabei implementando o código abaixo – super simples – sem efeitos colaterais (ainda funciona como antes no Safari).

Solução: Alterne uma class no elemento problemático conforme necessário. Cada alternância irá forçar um redesenho. (Eu usei o jQuery por conveniência, mas o JavaScript de baunilha não deveria ser um problema …)

jQuery Class Toggle

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

Classe CSS

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

E é isso…

OBSERVAÇÃO: você precisa executar o snippet abaixo de “Página inteira” para ver o efeito.

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

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.

Not Forced
Forced

Se você quiser preservar os estilos declarados em suas folhas de estilo, é melhor usar algo como:

 jQuery.fn.redraw = function() { this.css('display', 'none'); var temp = this[0].offsetHeight; this.css('display', ''); temp = this[0].offsetHeight; }; $('.layer-to-repaint').redraw(); 

Nota: com o último webkit / blink, o armazenamento do valor de offsetHeight é obrigatório para acionar a repintura, caso contrário, a VM (para fins de otimização) provavelmente ignorará essa instrução.

Atualização: adicionada a segunda leitura do offsetHeight , é necessário evitar que o navegador enfileire / armazene em cache uma alteração de propriedade / class CSS seguinte com a restauração do valor de display (dessa forma, uma transição de CSS que pode seguir deve ser renderizada)

Exemplo de Html:

 

Js:

  jQuery.fn.redraw = function() { return this.hide(0,function() {$(this).show(100);}); // hide immediately and show with 100ms duration }; 

function de chamada:

$('article.child').redraw(); // < == má ideia

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

Uma abordagem que funcionou para mim no IE (eu não poderia usar a técnica de exibição porque havia uma input que não deve perder o foco)

Funciona se você tiver 0 margem (também está mudando o preenchimento)

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

Apenas CSS. Isso funciona para situações em que um elemento filho é removido ou adicionado. Nessas situações, bordas e cantos arredondados podem deixar artefatos.

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

Minha correção para o IE10 + IE11. Basicamente, o que acontece é que você adiciona um DIV dentro de um elemento de quebra que precisa ser recalculado. Em seguida, basta removê-lo e voila; Funciona como um encanto 🙂

  _initForceBrowserRepaint: function() { $('#wrapper').append('
'); $('#dummydiv').width(function() { return $(this).width() - 1; }).width(function() { return $(this).width() + 1; }); $('#dummydiv').remove(); },

A maioria das respostas requer o uso de um tempo limite asynchronous, que causa uma piscadela irritante.

Mas eu criei este, que funciona sem problemas porque é sincrônico:

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

Esta é a minha solução que funcionou para o desaparecimento de conteúdo …

  
 function resizeWindow(){    var evt = document.createEvent('UIEvents');    evt.initUIEvent('resize', true, false,window,0);    window.dispatchEvent(evt); } 

chame esta function após 500 milissegundos.

Eu me deparei com um problema semelhante e esta simples linha de JS ajudou a corrigi-lo:

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

No meu caso, foi um erro com uma extensão do Chrome não redesenhar a página depois de alterar seu CSS de dentro da extensão.