web-dev-qa-db-ja.com

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

スタイルを変更するための簡単な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>タグが含まれています。

Chromeの「開発者ツール」ウィンドウで(たとえば、チェックマークを外してチェックする)any属性any要素の場合、2番目の兄弟が正しいスタイルに更新されます。

簡単にそしてプログラム的にWebKitを正しく動かして正しいことをさせるための回避策はありますか?

241
danorton

私はいくつかの複雑な提案とうまくいかなかった多くの単純な提案を見つけましたが、 Vasil Dinkov によってそれらの1つへのコメントはちょうどうまく働く再描画/再描画を強制する簡単な解決策を提供しました:

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

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

ありがとう、Vasil!

295
danorton

私たちは最近これに遭遇し、CSSで composite layer with translateZ に影響を受ける要素をプロモートすることで余分なJavaScriptを必要とせずに問題が解決することを発見しました。

.willnotrender { 
   transform: translateZ(0); 
}

これらの描画の問題は主にWebkit/Blinkで発生し、この修正は主にWebkit/Blinkを対象としているので、場合によってはそれが望ましいです。特に、受け入れられた答えはほぼ確実に リフローと再描画 を引き起こしますので、はそうではありません再塗装するだけです。

WebkitとBlinkはレンダリングパフォーマンスに懸命に取り組んできました、そしてこれらの種類のグリッチは不必要な流れと絵の具を減らすことを目的とする最適化の不幸な副作用です。 CSS will-change または他の連続した仕様が将来の解決策になるでしょう。

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

87
morewry

danortonの解決策は私にはうまくいきませんでした。 WebKitが要素をまったく描画しないという非常に奇妙な問題がいくつかありました。入力内のテキストがonblurまで更新されなかった場所。 classNameを変更しても再描画されません。

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

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

それはそれを修正しました。それはどれほど変ですか?これが誰かに役立つことを願っています。

51
Gerben

Display + offsetトリガーは私にはうまくいかなかったので、私はここで解決策を見つけました:

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

すなわち.

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

私は同じ問題に苦しんでいました。 danortonの 'toggling display'修正は私のアニメーションのstep関数に追加されたとき私にはうまくいきましたが、私はパフォーマンスを心配していたので私は他のオプションを探しました。

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

これが万能薬ではないかと思います、それは依存していると思いますなぜ Chromeは要素を再描画しないことを選択しましたが、私はそれが誰かを願ってこの手助けをします。

乾杯、ロブ

tl; dr >> zインデックスを要素またはその親に追加してみてください。

22
Rob Murphy

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

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

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

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

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

12
sowasred2012

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

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

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

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

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

6
Toni Almeida

私は今日これにつまずいた: 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()を直接呼び出さなければならないことに気付きました。親要素を再描画しても、子が経験している問題を解決できないことがあります。

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

6
Adam Eberlin

スクロール中にページの左下にある固定位置のキャプションボックスを更新すると、キャプションが画面いっぱいに「ゴースト」してしまいます。上記のすべてを試してみたが、DOMの再レイアウトが非常に短くなったり、スクロールが多少不自然になったりするなど、遅くなったり問題が発生したりすることが多くありました。

pointer-events: noneでフルサイズのdivを固定し、その要素に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';
4
waffl

私はposition: absoluteで別のdivに挿入されたdivのいくつかでこの問題を抱えていました、挿入されたdivはposition属性を持っていませんでした。これをposition:relativeに変更したときはうまくいきました。 (問題を特定するのは本当に難しかったです)

私の場合は、要素はAngularでng-repeatで挿入されています。

4
JustGoscha

この質問に別の答えが必要なわけではありませんが、私は特定の状況で色を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番目のスタイルの変更を現在のイベントループの外側に移動するのに重要です。

3
TMan

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

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

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

2
sviriden

誰もが彼ら自身の問題と解決策を持っているように思われるので、私は私のために働く何かを追加したいと考えました。現在のChromeを搭載したAndroid 4.1では、オーバーフロー付きで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
Eric Ruck

私はイオン性のhtml5アプリに取り組んでいます、私はIOSデバイス(iPhone 4、5、6、6 +)で上下にスクロールするとき、私はabsolutename__の位置の要素を持っています私はバグを塗り直しました。

これが私の問題を解決することを除いてそれらのどれも働いていなかった多くの解決策を試しました。

これらの絶対位置要素にCSSクラス.fixRepaintを使用しました。

.fixRepaint{
    transform: translateZ(0);
}

これで私の問題は解決しました。助けになるかもしれません

1
Anjum....

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

.selector {
    content: '1'
}
0
Steve

特定の状況で向きが変更されたときに、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日半後に、ここで提供されているhacky解決策に満足していませんでしたが、この問題は新しい要素を描くときに要素をメモリに保持しているように見えることが原因です。解決策は、SVG上のlinearGradientのIDを一意にすることでした。ただし、これは1ページに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}})"/>

私はこの答えが他の誰かが彼らのキーボードを顔を壊すことの価値を節約することを願っています。

0
Jamie Barker

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

アンカー内のテキストを動的に変更したい。 「検索」という言葉。 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
ganesh

私はtransform: translateZ(0);メソッドを使いますが、場合によってはそれでは不十分です。

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

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

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

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

最初の回答で提案した解決策は、これらのシナリオでは私にはうまくいきました。

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

これはサファリに再描画を強制します。

0
steve

私はこのメソッドがトランジションを扱うときに便利だと思った

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

これは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
WiR3D

私はもっ​​と答えを試みましたが、それは私のために働きません。私は他のブラウザと比較してサファリと同じ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で間違った値を取得するので、getterはsty.transform = "translateZ(1px)";sty.transform = "";の間になければならないので、本当に奇妙です。

最も確実に機能するもう1つの解決策は、

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
bormat

私の場合は "display/offsetHeight"ハックはうまくいきませんでした。少なくともそれがアニメ化されている要素に適用されたときです。

私はページのコンテンツ上で開閉されていたドロップダウンメニューを持っていました。メニューが閉じられた後にアーティファクトがページコンテンツに残っていました(WebKitブラウザでのみ)。 "display/offsetHeight"ハックが機能する唯一の方法は、私がそれをボディに適用した場合です。

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

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

これはまだかなり厄介です(ハードウェアレンダリングを強制するためにCSS3プロパティを使用します)、しかし少なくともそれは問題となっている要素にのみ影響を及ぼし、そして私にとってサファリとクロムの両方でPCとMacの上で働きました。

0
tedtedson

私は、リフローを強制するための、よりハックが少なく、より正式な方法をお勧めします。 forceDOMReflowJS を使用します。あなたの場合、あなたのコードは次のようになります。

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