WebKitを強制的に再描画/再描画してスタイルの変更を反映させるにはどうすればよいですか?


247

スタイルの変更を行うための簡単なJavaScriptがあります。

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

これは、FF、Opera、IEの最新バージョンでは正常に動作しますが、ChromeとSafariの最新バージョンでは失敗します。

これは、たまたま兄弟である2つの子孫に影響します。最初の兄弟は更新されますが、2番目の兄弟は更新されません。2番目の要素の子にもフォーカスがあり、onclick属性に上記のコードを含む<a>タグが含まれています。

クロームの「開発ツール」ウィンドウでは、私は(例えばチェックを外し&チェック)微調整する場合の任意の属性の任意の要素、正しいスタイルに二兄弟の更新を。

WebKitを簡単かつプログラム的に「ナッジ」して正しいことを実行するための回避策はありますか?


WebViewでキャンバスアプリをラップすると、Android 4.2.2(Samsung I9500)でこの問題が発生していると思います。ばかげた!
Josh

3
what-forces-layout.mdは非常に優れた読み物
vsync

回答:


312

私はいくつかの複雑な提案と機能しない単純な提案を見つけましたが、Vasil Dinkovによるそれらの1 つへのコメントは、正常に機能する再描画/再描画を強制するための簡単な解決策を提供しました:

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

「ブロック」以外のスタイルで機能する場合は、他の人にコメントしてもらいます。

ありがとう、Vasil!


32
ちらつきを回避するために'inline-block''table'またはの'run-in'代わりにを試すことができますが'none'、これには副作用がある場合があります。また、単にクエリのような0トリガーのタイムアウトリフローをoffsetHeight行いますsel.style.display = 'run-in'; setTimeout(function () { sel.style.display = 'block'; }, 0);
user123444555621

15
この回答は2年後も役に立ちます。ブラウザを特定の方法でサイズ変更した後、CSSボックスの影が画面に残るChromeのみの問題を修正するために使用しました。ありがとう!
rkulla

5
@danortonあなたは2010年に回答しました。2013年ですが、バグはまだ残っています。ありがとう。ちなみに、webkitトラッカーで登録されている問題があるかどうか誰でも知っていますか?
RaphaelDDL 2013

13
PS .:私にとって、上記の解決策はもう機能しませんでした。代わりにこれを使用しました(jQuery):sel.hide()。show(0); 出典:stackoverflow.com/questions/8840580/…–
ProblemsOfSumit

7
あなたがする必要があるのはサイズのプロパティを読むことだけであり、あなたはそれを前後に変更する必要はありません。
Andrew Bullock

93

最近この問題に遭遇し、CSS でtranslateZを使用して影響を受ける要素を複合レイヤーに昇格させると、追加のJavaScriptを必要とせずに問題が修正されることがわかりました。

.willnotrender { 
   transform: translateZ(0); 
}

これらの描画の問題は主にWebkit / Blinkで発生し、この修正は主にWebkit / Blinkを対象としているため、場合によってはそれが望ましい方法です。特に、受け入れられた回答はほぼ確実に、リペイントだけでなくリフローとリペイントを引き起こします。

WebkitとBlinkはレンダリングパフォーマンスに懸命に取り組んできました。この種の不具合は、不必要なフローやペイントを減らすことを目的とした最適化の不幸な副作用です。 CSSは変更されるか、別の後続の仕様が将来のソリューションになる可能性が高いです。

複合レイヤーを実現する方法は他にもありますが、これが最も一般的です。


1
角度カルーセルを使用するときに私のために働いた!
gustavohenke 2014年

1
スライドパネル内の要素でborder-radiusが失われる問題を修正しました
Timo

1
コルドバプロジェクトでも動作します!
Quispie

1
更新されない-webkit-line-clampのために私のために働いた
Adam Marshall

1
-webkit-transform:translate(-50%、-50%)を使用してipad(6.0)で動作しました。
Lain

51

danortonソリューションが機能しませんでした。Webkitがいくつかの要素をまったく描画しないという、本当に奇妙な問題がありました。入力のテキストはonblurまで更新されませんでした。また、classNameを変更しても、再描画は行われません。

私が誤って発見した解決策は、スクリプトの後に空のスタイル要素を本文に追加することでした。

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

それで直った。それはどのように奇妙ですか?これが誰かに役立つことを願っています。


3
できれば1,000,000回賛成票を投じます。これは、私がドラッグしていた愚かなdivをクロムで再描画できる唯一の方法です。ちなみに、スタイル要素を追加する必要はありません(少なくとも私はしませんでした)要素は機能します。私はdivを作成し、それにIDを付けて、各ステップで要素を削除して追加できるようにしました。
hobberwickey

6
これを、別の回答に対するPumbaa80のコメントと組み合わせて使用​​しました。私が最終的に行った修正はvar div = $('<div>').appendTo(element); setTimeout(function(){ div.remove(); }, 0);
ベンドマン、2013年

33
この単一行は私にとって素晴らしい仕事です!$('<style></style>').appendTo($(document.body)).remove();
pdelanauze 2013

1
@pdelanauzeの回答は完璧に機能します。iOSでcss3 translateとsingを歌って再描画の問題があります。
Marcel Falliere、2013年

11
これはページ全体の再描画を強制することを知っているだけですが、ほとんどの場合、ページの特定のセクションのみを再描画したいのです...
Ryan Wheale 2013

33

ディスプレイ+オフセットトリガーが機能しなかったので、ここで解決策を見つけました:

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

すなわち

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

3
私が試していたハックでこれを使用しましたが、の代わりにscale(1)として割り当てましたelement.style.webkitTransform = element.style.webkitTransform。これは、前者に設定すると、absolute配置が適切な要素のためにページが少し歪むためです。
bPratik 2012

これは私にとってはうまくいき、displayソリューションが行ったスクロール位置のちらつきやリセットの問題もありませんでした。
davidtbernal 2014

多くの動的SVGを使用するCordovaアプリがありました。起動時にSVG要素を表示しない場合、iOS7を除くすべての場所で問題なく動作しました。単純な$( 'body')[0] .style.webkitTransform = 'scale(1)';を追加しました。初期化コードと問題が消えました!
ハーク

これは現在、最新のSafariとiOSでは機能していません。
setholopolus

@setholopolusどのversoin(s)でしょうか?
EricG

23

私も同じ問題を抱えていました。danortonの「表示の切り替え」の修正は、アニメーションのステップ関数に追加するとうまくいきましたが、パフォーマンスが心配で他のオプションを探しました。

私の状況では、再描画されなかった要素は、その時点ではZインデックスを持っていなかった絶対位置要素内にありました。この要素にZインデックスを追加すると、Chromeの動作が変更され、期待どおりに再描画が行われました->アニメーションがスムーズになりました。

これが万能薬であるとは思えません。Chromeが要素を再描画しないことを選択した理由に依存すると思いますが、この特定の解決策を誰かが望む助けとしてここに投稿しています。

乾杯、ロブ

tl; dr >>要素またはその親にz-indexを追加してみてください。


8
鮮やかさ。これは素晴らしく、私にとっては問題を修正しました。A +++は再びZインデックスを作成します。
trisweb 2013年

スクロールバーと変換を扱う私の状況では機能しませんでした。
リッキー・ボイス、

16

以下の作品。純粋なCSSで一度設定するだけです。また、JS関数よりも確実に機能します。パフォーマンスには影響がないようです。

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

それも私のために働いているようです。これを使用してMobile Safariのレンダリングの問題を修正しました
Chris

これは私の問題を修正し、Safariを強制的に再レイアウトします。しかし、私が使用zoom代わりにパディングの:@-webkit-keyframes safariBugFix {from { zoom: 100%; } to { zoom: 99.99999%; }}
アハメドMusallam

13

どういうわけか私はdanortonの答えを仕事に得ることができませんでした、私はそれが何をすべきかを見ることができたので私はこれを少し微調整しました:

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

そしてそれは私のために働いた。


7
私はこれ以上のことをすべて試しましたが、これだけがうまくいきました。WTF Webkit!これはコンピュータサイエンスではありません。これは黒魔術です!
チャーリーマーティン

7

CSSを変更した後、Chromeでスクロールバーを再描画する必要があったため、ここに来ました。

誰かが同じ問題を抱えている場合、私はこの関数を呼び出すことで解決しました:

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

この方法は最善の解決策ではありませんが、すべてで機能し、再描画が必要な要素を非表示にして表示すると、すべての問題を解決できる場合があります。

これが私が使ったフィドルです:http : //jsfiddle.net/promatik/wZwJz/18/


1
すごい!私もスクロールバーをアニメーション化しようとしていましたが、それが必要でした!ところでオーバーフローの使用を改善:アニメーションスクロールバーのオーバーレイ
ekalchev '

6

今日これに遭遇しました:prototype.jsのElement.redraw()

使用:

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

ただし、問題のある要素でredraw()を直接呼び出す必要があることに気づきました。親要素を再描画しても、子供が経験している問題が解決しない場合があります。

ブラウザーが要素をレンダリングする方法についての良い記事:レンダリング:再描画、リフロー/再レイアウト、スタイル変更


1
appendChildDOMメソッドであり、jQueryメソッドではありません。
ChrisW、2015年

4

で別のdivに挿入された多数のdivでこの問題が発生しました。挿入されたdivにposition: absoluteは位置属性がありませんでした。これを変更したposition:relativeところ、うまくいきました。(問題を特定するのは本当に困難でした)

私の場合、Angular withで挿入された要素ng-repeat


1
ありがとう!これは私の問題でも機能し、上記の多くのJavaScriptソリューションよりもはるかに軽量です。
Dsyko 2014

4

これが2014年にまだ問題であるとは思えません。スクロール中にページの左下にある固定位置のキャプションボックスを更新すると、キャプションが画面上に「ゴースト」するだけでこの問題が発生しました。上記のすべてを成功せずに試した後、非常に短いDOM再レイアウトなどの作成が原因で、多くのことが遅くなったり問題を引き起こしたりして、スクロールが多少不自然に感じられるなどに気づきました。

私は最終的に固定位置のフルサイズのdivを作成しpointer-events: none、その要素にdanortonの回答を適用しました。これは、DOMに干渉することなく画面全体に再描画を強制するようです。

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';

私のために働いた唯一の方法であるあなたの回避策に感謝することはできません。はい、それは2017年であり、問​​題は残っています。私の問題は、Androidモバイルデバイスで手動で更新すると空白になるRTL言語ページに関連していました。z-indexそしてpointer-eventsルールはここに不要です。
アミンモザファリ2017

私は以前にdanortonの修正を使用したことがありますが、表示の設定からなしへのコンテンツのフラッシュに本当に苦労していました。あなたの解決策はコンテンツフラッシュなしで私にとってうまくいきました!
ジャスティンノエル

3

この質問には別の答えが必要なわけではありませんが、特定の状況では、色を1ビットだけ変更すると、強制的に再描画されることがわかりました。

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

setTimeout現在のイベントループ外側の第2スタイルの変更を移動することが重要であることが判明しました。


1
タイムアウトを0に設定すると、別の似たような状況で役立ちます。大きなフォームを解析しているときにjqueryの控えめな検証で画面がフリーズする前に、再描画が行われていませんでした。誰かが同じ状況にいる場合に備えてコメントしたいと思いました。他のソリューションはこのシナリオでは機能しませんでした。
k29の2015

2

私のために働く唯一の解決策はsowasred2012の答えに似ています:

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

ページに問題のあるブロックがたくさんあるのでdisplay、ルート要素のプロパティを変更します。スクロールオフセットをリセットするため、のdisplay: table;代わりに使用します。display: none;none


2

私はこのtransform: translateZ(0);方法を使用していますが、場合によっては十分ではありません。

私はクラスを追加したり削除したりするのが好きではないので、これを解決する方法を見つけようとしましたが、うまく機能する新しいハックになりました:

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

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

1

誰もが独自の問題と解決策を持っているようなので、私は私のために働く何かを追加したいと思いました。現在のChromeがインストールされているAndroid 4.1で、overflow:hiddenを使用してdiv内でキャンバスをドラッグしようとすると、親divに要素を追加しない限り、再描画を取得できませんでした(害を及ぼさない場合)。

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

画面のちらつきや実際の更新はなく、自分で片付けます。グローバルまたはクラスのスコープ変数ではなく、ローカルのみ。Mobile Safari / iPadまたはデスクトップブラウザーで何も害を与えていないようです。


1

absoluteIOSデバイス(iPhone 4、5、6、6+)で上下にスクロールすると、要素を配置したいくつかの画面で、イオンhtml5アプリに取り組んでいます。再描画のバグがありました。

多くの解決策を試してみましたが、この解決策が私の問題を解決することを除いて、どれも機能していませんでした。

私は.fixRepaintそれらの絶対位置要素にCSSクラスを使用しています

.fixRepaint{
    transform: translateZ(0);
}

これは私の問題を解決しました、それは誰かを助けるかもしれません


1
Opssいくつか見たことがない。@morewry指摘してくれてありがとう
...

0

これはJSには問題ありません

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

ただし、Jqueryでは、特に$(document).readyしか使用できず、特定の理由でオブジェクトの.loadイベントにバインドできない場合は、次のように機能します。

オブジェクト/ divのOUTER(MOST)コンテナーを取得し、その内容をすべて変数に削除してから、再度追加する必要があります。外側のコンテナ内で行われたすべての変更が表示されます。

$(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

トランジションを操作するときにこの方法が役立つことがわかりました

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

0

「display / offsetHeight」ハックは、少なくともアニメーション化されている要素に適用された場合、私の場合は機能しませんでした。

ページのコンテンツ上で開いたり閉じたりするドロップダウンメニューがありました。メニューが閉じた後、アーティファクトがページコンテンツに残っていました(Webkitブラウザーのみ)。「display / offsetHeight」ハックが機能する唯一の方法は、ボディに適用した場合で、これは厄介なようです。

しかし、私は別の解決策を見つけました:

  1. 要素がアニメーションを開始する前に、「-webkit-backface-visibility:hidden;」を定義するクラスを追加します。要素上(インラインスタイルを使用することもできると思います)
  2. アニメーションが完了したら、クラス(またはスタイル)を削除します

これはまだかなりハック(CSS3プロパティを使用してハードウェアレンダリングを強制する)ですが、少なくとも問題の要素にのみ影響し、PCとMacのサファリとクロムの両方で機能しました。


0

これはこれに関連しているようです:SafariでjQueryスタイルが適用されていません

最初の応答で提案された解決策は、これらのシナリオで私にとってうまく機能しました。つまり、スタイルの変更を行った後、ボディにダミークラスを適用および削除します。

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

これにより、サファリは強制的に再描画されます。


これをChromeで機能させるには、次のように追加する必要があります。$( 'body')。addClass( 'dummyclass')。delay(0).removeClass( 'dummyclass');
mbokil 2014年

0

上記の提案は私にとってはうまくいきませんでした。しかし、下のものはそうです。

アンカー内のテキストを動的に変更したい。「検索」という言葉。id属性で内部タグ「font」を作成しました。JavaScriptを使用してコンテンツを管理しました(下記)

探す

スクリプトの内容:

    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

特定の状況で向きを変更すると、Chrome for AndroidでSVGが表示されなくなるという問題がありました。以下のコードはそれを再現しませんが、私たちが持っていた設定です。

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>

突っ込んで突進してから1日半後、ここで提供されるハッキーソリューションに満足できなかったので、新しい要素を描画するときに要素をメモリに保持しているように見えることが原因であることがわかりました。解決策は、SVGのlinearGradientのIDを一意にすることでしたが、ページごとに1回しか使用されていませんでした。

これはさまざまな方法で実現できますが、角度アプリではlodash uniqueId関数を使用してスコープに変数を追加しました。

Angular Directive(JS):

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

HTMLの更新:

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

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

この回答により、他の誰かが1日分のキーボードの面倒を解消できることを願っています。



0

要素にコンテンツスタイルを追加するだけで強制的に再描画されることがわかりました。これは、再描画するたびに異なる値になるはずであり、疑似要素上にある必要はありません。

.selector {
    content: '1'
}

0

morewryの回答を試しましたが、うまくいきません。他のブラウザーと比較して、safariで同じclientWidthを使用するのに問題があり、このコードで問題が解決しました。

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})

get_safe_valueの後にclientWidthをもう一度取得しようとすると、safariで不正な値を取得するため、ゲッターはsty.transform = "translateZ(1px)";sty.transform = "";

決定的に機能する他のソリューションは

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

問題は、フォーカスとスクロールの状態を失うことです。


0

これにより、ちらつき、既存の要素の改ざん、レイアウトの問題を回避しながら、強制的に再描画します...

function forceRepaint() {
    requestAnimationFrame(()=>{
      const e=document.createElement('DIV');
      e.style='position:fixed;top:0;left:0;bottom:0;right:0;background:#80808001;\
               pointer-events:none;z-index:9999999';
      document.body.appendChild(e);
      requestAnimationFrame(()=>e.remove());  
    });
}

-1

jqueryを使用した簡単なソリューション:

$el.html($el.html());

または

element.innerHTML = element.innerHTML;

HTMLに追加されたときに表示されなかったSVGがあった。

これは、svg要素が画面に表示された後に追加できます。

より良い解決策は以下を使用することです:

document.createElementNS('http://www.w3.org/2000/svg', 'svg');

そしてjQueryで:

$(svgDiv).append($(document.createElementNS('http://www.w3.org/2000/svg', 'g'));

これはChromeで正しくレンダリングされます。


このアプローチの問題は、オブジェクトまたはその子孫に登録したイベントハンドラーがすべて失われることです。
2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.