it-swarm.com.de

Wie erzwinge ich, dass WebKit neu gezeichnet wird, um Stiländerungen zu verbreiten?

Ich habe etwas triviales JavaScript, um eine Stiländerung zu bewirken:

sel = document.getElementById('my_id');
sel.className = sel.className.replace(/item-[1-9]-selected/,'item-1-selected');
return false;

Dies funktioniert gut mit den neuesten Versionen von FF, Opera und IE, schlägt jedoch bei den neuesten Versionen von Chrome und Safari fehl.

Betroffen sind zwei Nachkommen, die Geschwister sind. Das erste Geschwister wird aktualisiert, das zweite jedoch nicht. Ein untergeordnetes Element des zweiten Elements hat ebenfalls den Fokus und enthält den Tag <a> , der den oben genannten Code in einem onclick -Attribut enthält.

Wenn Sie im Chrome-Fenster „Developer Tools“ das Element any des Elements any anstoßen (z. B. deaktivieren und das Kontrollkästchen deaktivieren), wird das zweite Geschwister auf den korrekten Stil aktualisiert.

Gibt es eine Problemumgehung, um das WebKit einfach und programmatisch dazu zu bewegen, das Richtige zu tun?

229
danorton

Ich habe einige komplizierte Vorschläge gefunden und viele einfache, die nicht funktionierten, aber ein Kommentar von Vasil Dinkov bot eine einfache Lösung zum Erzwingen eines erneuten Zeichnens, das gut funktioniert:

sel.style.display='none';
sel.offsetHeight; // no need to store this anywhere, the reference is enough
sel.style.display='';

Ich lasse eine andere Person kommentieren, wenn dies für andere Stile als „Block“ funktioniert.

Danke, Vasil!

292
danorton

Wir sind vor kurzem auf dieses Problem gestoßen und haben festgestellt, dass das Problem durch das Befördern des betroffenen Elements auf eine Verbundschicht mit translateZ in CSS behoben wurde, ohne dass zusätzliches JavaScript benötigt wurde.

.willnotrender { 
   transform: translateZ(0); 
}

Da diese Malprobleme hauptsächlich in Webkit/Blink angezeigt werden und dieses Update hauptsächlich auf Webkit/Blink abzielt, ist dies in manchen Fällen zu bevorzugen. Zumal die akzeptierte Antwort fast sicher zu einem reflow führt und repaint , not nur ein Repaint ist.

Webkit und Blink haben hart an der Rendering-Performance gearbeitet, und diese Art von Störungen sind der ungünstige Nebeneffekt von Optimierungen, die darauf abzielen, unnötige Flüsse und Farben zu reduzieren. CSS wird sich ändern oder eine andere nachfolgende Spezifikation wird höchstwahrscheinlich die zukünftige Lösung sein.

Es gibt andere Möglichkeiten, eine Verbundschicht zu erhalten, dies ist jedoch die häufigste.

80
morewry

die Lösung von Danorton hat bei mir nicht funktioniert. Ich hatte einige wirklich seltsame Probleme, bei denen das Webkit überhaupt keine Elemente zeichnete. Der Text in den Eingaben wurde erst bei Onblur aktualisiert. und das Ändern von className würde nicht zu einem Neuzeichnen führen.

Ich habe zufällig entdeckt, dass ich nach dem Skript ein leeres Stilelement in den Hauptteil einfügen wollte.

<body>
...
<script>doSomethingThatWebkitWillMessUp();</script>
<style></style>
...

Das hat es behoben. Wie komisch ist das? Hoffe, das ist für jemanden hilfreich.

51
Gerben

Da der Display + Offset-Trigger für mich nicht funktionierte, habe ich hier eine Lösung gefunden:

http://mir.aculo.us/2009/09/25/force-redraw-dom-technique-for-webkit-based-browsers/

d.h.

element.style.webkitTransform = 'scale(1)';
32
EricG

Ich hatte das gleiche Problem. danortons "toggling display" -update funktionierte für mich, als ich die schrittfunktion meiner animation hinzufügte, aber ich war besorgt über die performance und suchte nach anderen möglichkeiten.

In meinem Fall befand sich das Element, das nicht neu gestrichen wurde, innerhalb eines absoluten Positionselements, das zu der Zeit keinen Z-Index hatte. Durch Hinzufügen eines Z-Index zu diesem Element wurde das Verhalten von Chrome geändert, und Repaints erfolgten wie erwartet -> Animationen wurden glatter.

Ich bezweifle, dass dies ein Allheilmittel ist. Ich kann mir vorstellen, dass es davon abhängt , warum Chrome das Element nicht neu gezeichnet hat, aber ich poste diese spezifische Lösung hier in der Hilfe, die es jemandem erhofft.

Prost, Rob

tl; dr >> Versuchen Sie, dem Element oder einem übergeordneten Element einen Z-Index hinzuzufügen.

22
Rob Murphy

Die folgenden Arbeiten In reinem CSS muss es nur einmal gesetzt werden. Und es arbeitet zuverlässiger als eine JS-Funktion. Leistung scheint nicht betroffen zu sein.

@-webkit-keyframes androidBugfix {from { padding: 0; } to { padding: 0; }}
body { -webkit-animation: androidBugfix infinite 1s; }
16
sweeds

Aus irgendeinem Grund konnte ich nicht die Antwort von danorton zur Arbeit bekommen, ich konnte sehen, was es tun sollte, also habe ich es ein bisschen angepasst:

$('#foo').css('display', 'none').height();
$('#foo').css('display', 'block');

und es hat für mich funktioniert.

12
sowasred2012

Ich bin hier hergekommen, weil ich nach dem Ändern der CSS-Funktion Bildlaufleisten in Chrome neu zeichnen musste.

Wenn jemand dasselbe Problem hat, habe ich es gelöst, indem ich diese Funktion aufgerufen habe:

//Hack to force scroll redraw
function scrollReDraw() {
    $('body').css('overflow', 'hidden').height();
    $('body').css('overflow', 'auto');
}

Diese Methode ist nicht die beste Lösung, kann aber mit allem funktionieren. Das Ausblenden und Anzeigen des Elements, das neu gezeichnet werden muss, kann jedes Problem lösen.

Hier ist die Geige, wo ich sie benutzt habe: http://jsfiddle.net/promatik/wZwJz/18/

6
Toni Almeida

Ich bin heute darauf gestoßen: Element.redraw () für prototype.js

Mit: 

Element.addMethods({
  redraw: function(element){
    element = $(element);
    var n = document.createTextNode(' ');
    element.appendChild(n);
    (function(){n.parentNode.removeChild(n)}).defer();
    return element;
  }
});

Ich habe jedoch manchmal bemerkt, dass Sie redraw () für das problematische Element direkt aufrufen müssen. Manchmal löst das Neuzeichnen des übergeordneten Elements das Problem, das das Kind hat, nicht.

Guter Artikel über die Art und Weise, wie Browser Elemente rendern: Rendern: Neuzeichnen, Reflow/Relayout, Restyle

6
Adam Eberlin

Ich kann nicht glauben, dass dies 2014 immer noch ein Problem ist. Ich hatte gerade dieses Problem, als ich beim Scrollen ein Untertitelfeld mit fester Position in der linken unteren Seite der Seite aktualisiert habe. Nachdem ich alles zuvor ohne Erfolg ausprobiert hatte, bemerkte ich, dass viele Dinge entweder langsam waren oder Probleme verursachten, weil sehr kurze DOM-Relayouts usw. erstellt wurden, was zu einem unnatürlichen Gefühl beim Scrollen usw. führte.

Am Ende machte ich mit pointer-events: none eine feste Position, ein Full-Size-Div, und wendete danortons Antwort auf dieses Element an, das ein Neuzeichnen auf dem gesamten Bildschirm zu erzwingen scheint, ohne das DOM zu beeinträchtigen.

HTML:

<div id="redraw-fix"></div>

CSS:

div#redraw-fix {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 25;
    pointer-events: none;
    display: block;
}

JS:

sel = document.getElementById('redraw-fix');
sel.style.display='none';
sel.offsetHeight; // no need to store this anywhere, the reference is enough
sel.style.display='block';
4
waffl

Ich hatte dieses Problem mit einer Anzahl von divs, die mit position: absolute in ein anderes div eingefügt wurden. Die eingefügten divs hatten kein Positionsattribut. Als ich dies in position:relative änderte, hat es gut funktioniert. (war wirklich schwer das Problem zu lokalisieren)

In meinem Fall wurden die Elemente von Angular mit ng-repeat eingefügt.

4
JustGoscha

Nicht, dass diese Frage eine andere Antwort erfordert, aber ich fand es einfach, die Farbe durch ein einziges Bit zu ändern, was zu einem Neulackieren in meiner speziellen Situation führte.

//Assuming black is the starting color, we Tweak it by a single bit
elem.style.color = '#000001';

//Change back to black
setTimeout(function() {
    elem.style.color = '#000000';
}, 0);

Die Variable setTimeout erwies sich als entscheidend, um die zweite Stiländerung außerhalb der aktuellen Ereignisschleife zu verschieben.

3
TMan

Die einzige Lösung, die für mich funktioniert, ist der Antwort von sowasred2012 ähnlich:

$('body').css('display', 'table').height();
$('body').css('display', 'block');

Ich habe viele Problemblöcke auf der Seite, daher ändere ich die display-Eigenschaft des Wurzelelements ..__ und benutze display: table; anstelle von display: none;, da none den Scroll-Offset zurücksetzt.

2
sviriden

Da jeder scheint, seine eigenen Probleme und Lösungen zu haben, dachte ich, ich würde etwas hinzufügen, das für mich funktioniert. Unter Android 4.1 mit aktuellem Chrome wurde versucht, eine Leinwand innerhalb eines Div mit Overflow zu ziehen: Versteckt, ich konnte keine Neuzeichnung erstellen, es sei denn, ich habe dem übergeordneten Div ein Element hinzugefügt (bei dem es keinen Schaden anrichtete).

var parelt = document.getElementById("parentid");
var remElt = document.getElementById("removeMe");
var addElt = document.createElement("div");
addElt.innerHTML = " "; // Won't work if empty
addElt.id="removeMe";
if (remElt) {
    parelt.replaceChild(addElt, remElt);
} else {
    parelt.appendChild(addElt);
}

Kein Bildschirmflackern oder echtes Update und das Aufräumen nach mir. Keine globalen oder Klassenvariablen, nur lokale. Scheint auf Mobile Safari/iPad oder Desktop-Browsern nichts zu schaden.

1
Eric Ruck

Ich arbeite an einer ionischen html5-App, auf einigen Bildschirmen habe ich absolute positioniertes Element. Wenn Sie in IOS - Geräten (iPhone 4,5,6, 6+) nach oben oder unten scrollen, hatte ich einen Repaint-Fehler.

Habe viele Lösungen ausprobiert, aber keiner von ihnen funktionierte, außer dieser löste mein Problem.

Ich habe die css-Klasse .fixRepaint für diese absoluten Positionselemente verwendet 

.fixRepaint{
    transform: translateZ(0);
}

Dies hat mein Problem behoben, es kann jemandem helfen 

1
Anjum....

Ich habe festgestellt, dass durch das Hinzufügen eines Inhaltsstils zu dem Element eine Neulackierung erzwungen wurde. Dies sollte jedes Mal ein anderer Wert sein, wenn Sie möchten, dass es neu gezeichnet wird, und es muss sich nicht auf einem Pseudoelement befinden.

.selector {
    content: '1'
}
0
Steve

Ich hatte ein Problem mit einer SVG, die unter Chrome für Android verschwand, als die Ausrichtung unter bestimmten Umständen geändert wurde. Der folgende Code reproduziert es nicht, aber es ist das Setup, das wir hatten.

body {
  font-family: tahoma, sans-serif;
  font-size: 12px;
  margin: 10px;
}
article {
  display: flex;
}
aside {
  flex: 0 1 10px;
  margin-right: 10px;
  min-width: 10px;
  position: relative;
}
svg {
  bottom: 0;
  left: 0;
  position: absolute;
  right: 0;
  top: 0;
}
.backgroundStop1 {
  stop-color: #5bb79e;
}
.backgroundStop2 {
  stop-color: #ddcb3f;
}
.backgroundStop3 {
  stop-color: #cf6b19;
}
<article>
  <aside>
    <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="100%" width="100%">
      <defs>
        <linearGradient id="IndicatorColourPattern" x1="0" x2="0" y1="0" y2="1">
          <stop class="backgroundStop1" offset="0%"></stop>
          <stop class="backgroundStop2" offset="50%"></stop>
          <stop class="backgroundStop3" offset="100%"></stop>
        </linearGradient>
      </defs>
      <rect x="0" y="0" rx="5" ry="5" width="100%" height="100%" fill="url(#IndicatorColourPattern)"></rect>
    </svg>
  </aside>
  <section>
    <p>Donec et eros nibh. Nullam porta, elit ut sagittis pulvinar, lacus augue lobortis mauris, sed sollicitudin elit orci non massa. Proin condimentum in nibh sed vestibulum. Donec accumsan fringilla est, porttitor vestibulum dolor ornare id. Sed elementum
      urna sollicitudin commodo ultricies. Curabitur tristique orci et ligula interdum, eu condimentum metus eleifend. Nam libero augue, pharetra at maximus in, pellentesque imperdiet orci.</p>
    <p>Fusce commodo ullamcorper ullamcorper. Etiam eget pellentesque quam, id sodales erat. Vestibulum risus magna, efficitur sed nisl et, rutrum consectetur odio. Sed at lorem non ligula consequat tempus vel nec risus.</p>
  </section>
</article>

Nach anderthalb Jahren, nachdem ich gestoßen und angestoßen hatte und mit den hier angebotenen hacky -Lösungen nicht zufrieden war, entdeckte ich, dass das Problem auf die Tatsache zurückzuführen war, dass es das Element im Gedächtnis zu halten schien, während ein neues gezeichnet wurde. Die Lösung bestand darin, die ID des linearGradient in der SVG eindeutig zu machen, obwohl sie nur einmal pro Seite verwendet wurde.

Dies kann auf viele verschiedene Arten erreicht werden, aber für unsere Winkel-App verwendeten wir die lodash uniqueId-Funktion, um eine Variable zum Gültigkeitsbereich hinzuzufügen:

Winkelrichtlinie (JS):

scope.indicatorColourPatternId = _.uniqueId('IndicatorColourPattern');

HTML-Updates:

Zeile 5: <linearGradient ng-attr-id="{{indicatorColourPatternId}}" x1="0" x2="0" y1="0" y2="1">

Zeile 11: <rect x="0" y="0" rx="5" ry="5" width="100%" height="100%" ng-attr-fill="url(#{{indicatorColourPatternId}})"/>

Ich hoffe, diese Antwort erspart jemandem ein paar Tage, wenn es darum geht, die Tastatur zu zerschlagen.

0
Jamie Barker

die obigen Vorschläge haben für mich nicht funktioniert. aber das tut man unten.

Sie möchten den Text im Anker dynamisch ändern. Das Wort "Suche". Erstellt ein inneres Tag "font" mit einem id-Attribut. Verwaltete den Inhalt mit Javascript (unten)

Suche

skriptinhalt:

    var searchText = "Search";
    var editSearchText = "Edit Search";
    var currentSearchText = searchText;

    function doSearch() {
        if (currentSearchText == searchText) {
            $('#pSearch').panel('close');
            currentSearchText = editSearchText;
        } else if (currentSearchText == editSearchText) {
            $('#pSearch').panel('open');
            currentSearchText = searchText;
        }
        $('#searchtxt').text(currentSearchText);
    }
0
ganesh

Ich verwende die Methode transform: translateZ(0);, aber in einigen Fällen reicht es nicht aus.

Ich bin kein Fan des Hinzufügens und Entfernens einer Klasse, also habe ich versucht, einen Weg zu finden, um dieses Problem zu lösen, und bin zu einem neuen Hack gekommen, der gut funktioniert:

@keyframes redraw{
    0% {opacity: 1;}
    100% {opacity: .99;}
}

// ios redraw fix
animation: redraw 1s linear infinite;
0

Dies scheint damit zu zusammenhängen: Der jQuery-Stil wird in Safari nicht angewendet

Die in der ersten Antwort vorgeschlagene Lösung hat für mich in diesen Szenarien gut funktioniert, und zwar: Anwenden und Entfernen einer Dummy-Klasse auf den Körper, nachdem die Stiländerungen vorgenommen wurden:

$('body').addClass('dummyclass').removeClass('dummyclass');

Dies zwingt die Safari zum Neuzeichnen.

0
steve

Ich habe festgestellt, dass diese Methode nützlich ist, wenn Sie mit Übergängen arbeiten

$element[0].style.display = 'table'; 
$element[0].offsetWidth; // force reflow
$element.one($.support.transition.end, function () { 
    $element[0].style.display = 'block'; 
});
0
jschr

Das ist gut für JS

sel.style.display='none';
sel.offsetHeight; // no need to store this anywhere, the reference is enough
sel.style.display='block';

Aber in Jquery und insbesondere, wenn Sie $ (document) .ready nur verwenden können und aus einem bestimmten Grund nicht an das .load-Ereignis eines Objekts gebunden werden können, wird Folgendes funktionieren.

Sie müssen den OUTER (MOST) -Container der Objekte/divs abrufen und dann den gesamten Inhalt in einer Variablen entfernen und dann erneut hinzufügen .. _ Dadurch werden ALLE Änderungen im äußeren Container sichtbar.

$(document).ready(function(){
    applyStyling(object);
    var node = $("div#body div.centerContainer form div.centerHorizontal").parent().parent();
    var content = node.html();
    node.html("");
    node.html(content);
}
0
WiR3D

Ich habe versucht, eine neue Antwort zu finden, aber es funktioniert nicht für mich ... Ich hatte Probleme, dieselbe clientWidth mit Safari zu haben, die mit anderen Browsern verglichen wurde, und dieser Code hat das Problem gelöst:

var get_safe_value = function(Elm,callback){
    var sty = Elm.style
    sty.transform = "translateZ(1px)";
    var ret = callback(Elm)//you can get here the value you want
    sty.transform = "";
    return ret
}
// for safari to have the good clientWidth
var $fBody = document.body //the element you need to fix
var clientW = get_safe_value($fBody,function(Elm){return $fBody.clientWidth})

Es ist wirklich seltsam, denn wenn ich erneut versuche, die clientWidth nach get_safe_value zu bekommen, erhalte ich mit safari einen schlechten Wert. Der Getter muss zwischen sty.transform = "translateZ(1px)";.__ liegen. und sty.transform = "";

Die andere Lösung, die definitiv funktioniert, ist

var $fBody = document.body //the element you need to fix
$fBody.style.display = 'none';
var temp = $.body.offsetHeight;
$fBody.style.display = ""
temp = $.body.offsetHeight;

var clientW = $fBody.clientWidth

Das Problem ist, dass Sie den Fokus- und Bildlaufstatus verlieren.

0
bormat

der "display/offsetHeight" -Hack funktionierte in meinem Fall nicht, zumindest wenn er auf das animierte Element angewendet wurde.

ich hatte ein Dropdown-Menü, das über den Seiteninhalt geöffnet/geschlossen wurde. Die Artefakte wurden nach dem Schließen des Menüs auf dem Seiteninhalt verbleiben (nur in Webkit-Browsern). Der "display/offsetHeight" -Hack funktionierte nur, wenn ich ihn auf den Körper anwendete, was unangenehm erscheint.

ich habe jedoch eine andere Lösung gefunden:

  1. bevor das Element animiert wird, fügen Sie eine Klasse hinzu, die "-webkit-backface-visible: hidden" definiert. auf dem Element (Sie könnten auch Inline-Style verwenden, würde ich vermuten)
  2. wenn Sie mit der Animation fertig sind, entfernen Sie die Klasse (oder den Stil).

dies ist immer noch ziemlich hackig (es verwendet eine CSS3-Eigenschaft, um das Hardware-Rendering zu erzwingen), aber es wirkt sich immer nur auf das betreffende Element aus und arbeitete für mich sowohl auf Safari als auch auf Chrome auf PC und Mac.

0
tedtedson

Ich würde eine weniger hackhafte und formalere Methode empfehlen, um einen Reflow zu erzwingen: use forceDOMReflowJS . In Ihrem Fall würde Ihr Code folgendermaßen aussehen.

sel = document.getElementById('my_id');
forceReflowJS( sel );
return false;
0
Jack Giffin