web-dev-qa-db-fra.com

Forcer le rafraîchissement / rafraîchissement du DOM sur Chrome / Mac

De temps en temps, Chrome rendra le code HTML/CSS parfaitement valide de manière incorrecte ou pas du tout. Diguer via l'inspecteur DOM est souvent suffisant pour lui faire comprendre l'erreur de son chemin et redessiner correctement, il est donc vraisemblable que le balisage est bon. Cela se produit assez souvent (et de manière prévisible) dans un projet sur lequel je travaille et que j'ai mis en place un code pour forcer le redessin dans certaines circonstances.

Cela fonctionne dans la plupart des combinaisons navigateur/système d'exploitation:

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

Comme dans Tweak, ajustez quelques propriétés CSS inutilisées, puis demandez des informations qui obligent à redessiner, puis annulez la propriété. Malheureusement, la brillante équipe derrière Chrome pour Mac semble avoir trouvé le moyen de récupérer offsetHeight sans redessiner. Tuer ainsi un hack autrement utile.

Jusqu'ici, le mieux que j'ai trouvé pour obtenir le même effet sur Chrome/Mac est ce morceau de laideur:

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

Au fur et à mesure, forcez l'élément à sauter un peu, puis refroidissez-le une seconde et sautez-le en arrière. Pour aggraver les choses, si vous passez le délai d’attente à moins de 500 ms (là où il serait moins perceptible), il n’aura souvent pas l’effet escompté, car le navigateur ne pourra pas redessiner avant de revenir à son état initial.

Quelqu'un souhaite-t-il proposer une meilleure version de cette procédure de redessinage/d'actualisation (de préférence basée sur le premier exemple ci-dessus) qui fonctionne sur Chrome/Mac?

206
Jason Kester

Vous ne savez pas exactement ce que vous essayez d'atteindre, mais c'est une méthode que j'ai déjà utilisée avec succès pour forcer le navigateur à se redessiner, cela fonctionnera peut-être pour vous.

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

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

Si cette simple mise à jour ne fonctionne pas, vous pouvez essayer celui-ci. Il insère un nœud de texte vide dans l’élément qui garantit une mise à jour.

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: En réponse à Šime Vidas, ce que nous réalisons ici serait un refoulement forcé. Vous pouvez en savoir plus auprès du maître lui-même http://paulirish.com/2011/dom-html5-css3-performance/

174
Juank

Aucune des réponses ci-dessus n'a fonctionné pour moi. J'ai remarqué que redimensionnement ma fenêtre a provoqué un redessinage. Alors ça m'a fait ça:

$(window).trigger('resize');
61
Vincent Sels

Cette solution sans délai d'attente! Réel force redessiner! Pour Android et iOS.

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

Nous avons récemment rencontré ce problème et découvert que la promotion de l'élément affecté en un couche composite avec translateZ corrigeait le problème sans recourir à JavaScript.

.willnotrender { 
   transform: translateZ(0); 
}

Comme ces problèmes de peinture apparaissent principalement dans Webkit/Blink, et que ce correctif vise principalement Webkit/Blink, il est préférable dans certains cas. D'autant que de nombreuses solutions JavaScript provoquent un refusion et un repeindre , pas seulement un repeinte.

23
morewry

Cela fonctionne pour moi. Kudos allez ici .

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

$(el).redraw();
13
zupa

Masquer un élément, puis l'afficher à nouveau dans un setTimeout de 0 forcera le rafraîchissement.

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

appel window.getComputedStyle() devrait forcer une refusion

4
qlqllu

Cela semble faire l'affaire pour moi. En plus, ça ne se voit pas du tout.

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

J'ai rencontré ce défi aujourd'hui sous OSX El Capitan avec Chrome v51. La page en question a bien fonctionné dans Safari. J'ai essayé presque toutes les suggestions sur cette page - aucune ne fonctionnait correctement - toutes avaient des effets secondaires ... J'ai fini par implémenter le code ci-dessous - très simple - aucun effet secondaire (fonctionne toujours comme auparavant dans Safari).

Solution: Basculez une classe sur l'élément problématique si nécessaire. Chaque bascule forcera un redessin. (J'ai utilisé jQuery pour plus de commodité, mais JavaScript JavaScript ne devrait poser aucun problème ...)

jQuery Class Toggle

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

Classe CSS

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

Et c'est tout...

REMARQUE: vous devez exécuter l'extrait de code sous "Pleine page" afin de voir l'effet.

$(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>
3
Ben Feely

Si vous souhaitez conserver les styles déclarés dans vos feuilles de style, utilisez de préférence quelque chose comme:

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: avec le dernier kit Web/blink, le stockage de la valeur offsetHeight est obligatoire pour déclencher le repeinte, sinon le VM (à des fins d'optimisation) ignorera probablement cette instruction.

Mise à jour: ajout de la deuxième lecture offsetHeight, il est nécessaire d'empêcher le navigateur de mettre en file d'attente/de mettre en cache un changement de propriété/classe CSS suivant avec la restauration de la valeur display (de cette manière, une transition CSS peut suivre). être rendu)

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

appelez cette fonction après 500 millisecondes.

2
Shobhit

Sample 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

    };

fonction d'appel:

$('article.child').redraw(); // <== mauvaise idée

$('#parent').redraw();
1
Mehdi Rostami

La plupart des réponses nécessitent l'utilisation d'un délai d'attente asynchrone, ce qui provoque un clignotement gênant.

Mais je suis venu avec celui-ci, qui fonctionne bien car il est synchrone:

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

Css ci-dessous fonctionne pour moi sur IE 11 et Edge, aucun JS n’est nécessaire. scaleY(1) fait le tour ici. Semble la solution la plus simple.

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

J'ai rencontré un problème similaire et cette simple ligne de JS a aidé à résoudre le problème:

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

Dans mon cas, il s’agissait d’un bogue dont l’extension Chrome ne redessinait pas la page après avoir modifié son code CSS à partir de l’extension.

0
Rotareti

Je voulais ramener tous les états à l’état précédent (sans rechargement), y compris les éléments ajoutés par jquery. L'implémentation ci-dessus ne fonctionnera pas. et j'ai fait comme suit.

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

CSS uniquement. Cela fonctionne dans les situations où un élément enfant est supprimé ou ajouté. Dans ces situations, les bordures et les coins arrondis peuvent laisser des artefacts.

el:after { content: " "; }
el:before { content: " "; }
0
rm.rf.etc

Une approche qui a fonctionné pour moi sur IE (je ne pouvais pas utiliser la technique d'affichage car il y avait une entrée qui ne devait pas perdre le focus)

Cela fonctionne si vous avez 0 marge (changer le rembourrage fonctionne aussi)

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

Ceci est ma solution qui a fonctionné pour la disparition de contenu ...

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

J'avais une liste de composants de réaction qui, lors du défilement, ouvrait une autre page, puis lors du retour, la liste n'était pas rendue sur Safari iOS tant que la page n'était pas défilée. C'est donc la solution.

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

Mon correctif pour IE10 + IE11. En gros, vous ajoutez une DIV dans un élément wrapping qui doit être recalculé. Ensuite, retirez-le et le tour est joué; fonctionne comme un charme :)

    _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
Narayan