Chrome / MacでDOMの再描画/更新を強制する


215

時々、Chromeは完全に有効なHTML / CSSを正しく表示しないか、まったく表示しません。多くの場合、DOMインスペクタを掘り下げるだけで、その方法のエラーを認識して正しく再描画できます。そのため、マークアップが適切である可能性があります。これは私が取り組んでいるプロジェクトで頻繁に(そして予想通り)十分に発生し、特定の状況でコードを強制的に再描画するように配置しました。

これは、ほとんどのブラウザーとOSの組み合わせで機能します。

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

のように、未使用のCSSプロパティを調整し、再描画を強制する情報を要求してから、プロパティの調整を解除します。残念ながら、Mac向けChromeの背後にいる明るいチームは、再描画せずにそのoffsetHeightを取得する方法を見つけたようです。したがって、他の点では有用なハックを殺します。

これまでのところ、Chrome / Macで同じ効果を得るために思いついた最善の方法は、次の醜さです。

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

のように、実際に要素を少しジャンプさせ、次に1秒間冷やしてからジャンプし直します。さらに悪いことに、そのタイムアウトを500ミリ秒未満に(目立たない程度に)落とすと、ブラウザーが元の状態に戻る前に再描画に取り掛からなくなるため、期待した効果が得られないことがよくあります。

誰かがこの再描画/更新ハックのより良いバージョン(できれば上記の最初の例に基づいたもの)をChrome / Macで動作するように提供したいと思いませんか?


数分前に同じ問題に遭遇しました。divの要素(それはスパンでした)を変更すると、ブラウザーは変更を正しく再描画します。私はこれが少し古いことを知っていますが、これは私が考えるいくつかの仲間を助けることができます。
アンドレス・トーレス

2
不透明度0.99に関する以下の回答をご覧ください。ここでの最良の回答ですが、ページの奥深くにあるため、簡単に見つけることができません。
Grouchal 2015年

1
これはLinuxでも起こります:-(
schlingel '9/09/15

1
タイムアウトをrequestAnimationFrameに置き換えることができます。その場合、同じことを達成できますが、ラグは1000ミリ秒ではなく16ミリ秒になります。
trusktr 2016

3
無効なレンダリングについては、Chromiumの問題提出してください。これは4年前であるにもかかわらず、誰もそうしなかったようです。
Bardi Harbourow 2016年

回答:


183

あなたが何を達成しようとしているのか正確にはわかりませんが、これは過去に私がブラウザを強制的に再描画させることに成功した方法です。おそらくそれはあなたのために働くでしょう。

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

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

この単純な再描画が機能しない場合は、これを試すことができます。再描画を保証する要素に空のテキストノードを挿入します。

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
}

編集:私たちがここで達成しているのは、チメ・ビダスへの対応として、強制的なリフローです。あなたはマスター自身からもっと知ることができますhttp://paulirish.com/2011/dom-html5-css3-performance/


3
Afaik、ビューポートの一部だけを再描画することはできません。ブラウザーは常にWebページ全体を再描画します。
–ŠimeVidas、2012

38
$( '#parentOfElementToBeRedrawn')。hide()。show();の代わりに Chromeで適切に動作するために.hide.show(0)が必要でした
l.poellabauer

1
@ l.poellabauerここでも同じ-これは意味がありません...しかし、ありがとう。一体どうやって見つけたの??? :)
JohnIdol

6
ちなみに、私が取り組んでいた関連領域は、無限のスクロールリストでした。UL全体を非表示/表示することは少し実行不可能でしたので、私は遊んでみましたが.hide().show(0)任意の要素(ページフッターを選択した)で操作すると、ページが更新されることがわかりました。
treeface

1
ビューポートの一部のみを再描画することが可能です。そのためのAPIはありませんが、ブラウザーはそうすることを選択できます。ビューポートは、最初はタイルでペイントされています。また、複合レイヤーは個別にペイントされます。
morewry 2015年

62

上記の答えはどれもうまくいきませんでした。ウィンドウのサイズ変更すると再描画が発生することに気づきました。だからこれは私のためにそれをしました:

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

11
ヒント:これは完全なウィンドウを再描画します。これはパフォーマンスにコストがかかり、おそらくOPが望んでいないものです
hereandnow78 '30

7
ウィンドウのサイズを変更すると常にリフローがトリガーされますが、コードで$(window).trigger('resize')はトリガーされません。割り当てられている場合、関連するハンドラーをトリガーする「サイズ変更」イベントのみがスローされます。
グアリ

1
あなたは私を丸一週間救います!私の問題は、<body style = "zoom:67%">を設定した後、ページが期待したレベルにズームされましたが、ページがフリーズしてスクロールできない!です。あなたの答えがこの問題をすぐに解決しました
user1143669

30

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

.willnotrender { 
   transform: translateZ(0); 
}

これらの描画の問題は主にWebkit / Blinkで発生し、この修正は主にWebkit / Blinkを対象としているため、場合によってはそれが望ましい方法です。特に、多くのJavaScriptソリューションでは、単なるリペイントではなく、リフローとリペイントが発生します。


1
これは、キャンバスが継続的にアニメーション化されているにもかかわらず、問題がキャンバスに描画されている削除されたHTML要素である場合、私にとってはうまくいきました。
Knaģis

これにより、で発生していたレイアウトの問題が修正されましたneon-animated-pages。ありがとうございました:+1:
coffeesaga

1
これはうまくいきました。Safariは、div内で何も表示せず、表示され、選択できない要素を残していました。ウィンドウのサイズを変更すると、ウィンドウが非表示になりました。この変換を追加すると、「アーティファクト」が予想どおりに消えました。
ジェフマットソン

28

タイムアウトなしのこのソリューション!リアルフォース再描画!AndroidおよびiOS用。

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

1
これは、Chromeでは私には機能せず、LinuxではFFでも機能しません。ただし、element.offsetHeightにアクセスするだけで(表示プロパティを変更せずに)機能します。要素が表示されていない場合、ブラウザがoffsetHeightの計算をスキップするのと同じです。
グロドリゲス2014年

このソリューションは、「overwolf」で使用されているクロムベースのブラウザーで発生したバグを回避するのに最適です。私はこれを行う方法を理解しようとしていました、そしてあなたは私に努力を救いました。
nacitar sevaht 2015年

13

これは私にとってはうまくいきます。功績はここに行く

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

$(el).redraw();

1
しかし、「display:block」などの「残り」が残って、ページ構造が破壊されることがあります。
pie6k 2014

これは本質的に同じである必要があります$().hide().show(0)
Mahn

@Mahnやってみましたか?私の推測では、.hide()は非ブロッキングであるため、ブラウザでの再レンダリングの実行、つまりメソッドの全体的なポイントはスキップされます。(そして、私が正しく覚えていれば、当時それをテストしました。)
zupa 2014年

2
@zupaそれは試験だとChrome 38に取り組んでトリックすなわちshow()異なるshow(0)後者における持続時間を指定することで、すぐに時間を与えるのと同じ方法でレンダリングするためにかなりことがタイムアウトにjQueryのアニメーションの方法によってトリガということでhide(0, function() { $(this).show(); })ありません。
Mahn 2014年

@マーン大丈夫いいキャッチします。クロスプラットフォームで動作し、最近のjQuery機能ではない場合、私は間違いなくあなたのソリューションです。テストする時間がないので、「可能」と追加します。上記が当てはまると思われる場合は、回答を編集してください。
zupa 2014年

9

要素を非表示にしてから、setTimeoutが0以内に再度表示すると、強制的に再描画されます。

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

1
これは、SVGフィルターが再描画されないことがあるChromeのバグで動作しました、bugs.chromium.org / p / chromium / issues / detail?id = 231560。タイムアウトは重要でした。
Jason D

これはmyElement.getBoundingClientRect()、おそらく最適化やパフォーマンスの目的で、キャッシュされていたの値をChromeに再計算させようとしているときに私に役立ちました。以前の(キャッシュされた)値を持つ古い要素がDOMから削除された後、新しい要素をDOMに追加し、新しい値を取得する関数に0ミリ秒のタイムアウトを追加しただけです。
TheDarkIn1978 2017

6

これは私のトリックを行うようです。さらに、実際にはまったく表示されません。

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

このバグに関しては、Chromiumバージョン60.0.3112.113で問題なく機能しました:問題727076。効率的で微妙です。どうもありがとう。
ロニーベスト

@ wiktus239 20ミリ秒は速すぎるかもしれませんし、ブラウザが1.0に戻る前に.99に変える時間がないのでしょうか。—この不透明度のトリックは私にはとにかく機能し、iframeのコンテンツをiPhone iOS 12 Safariで表示できるようにします。私は1000ミリ秒ごとに切り替えます。これは、上記のコメント(1000ミリ秒)のリンク先の問題727076にもあります。
KajMagnus

4

呼び出しwindow.getComputedStyle()は強制的にリフローする必要があります


2
私にとってはうまくいきoffsetHeightませんでしたが、それはおそらくCSSではないプロパティが必要だったためです。
Sebas

3

今日、OSX El CapitanとChrome v51でこの問題に遭遇しました。問題のページはSafariでは問題なく動作しました。私はこのページのほぼすべての提案を試みました-どれも正しく機能しませんでした-すべてに副作用がありました...私は最終的に以下のコードを実装しました-超単純-副作用なし(まだSafariで以前と同じように機能します)。

解決策:必要に応じて、問題のある要素のクラスを切り替えます。トグルごとに強制的に再描画されます。(私は便宜上jQueryを使用しましたが、JavaScriptは問題ありません...)

jQueryクラス切り替え

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

CSSクラス

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

以上です...

注:効果を確認するには、「全ページ」の下のスニペットを実行する必要があります。

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


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

500ミリ秒後にこの関数を呼び出します。


2

スタイルシートに宣言されたスタイルを保持したい場合は、次のようなものを使用することをお勧めします。

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

$('.layer-to-repaint').redraw();

注意:最新のwebkit / blinkを使用して、 offsetHeight、再描画をトリガーするためにのが必須です。そうでない場合、VM(最適化目的)はおそらくその命令をスキップします。

更新:2番目のoffsetHeight読み取り値を追加しました。display値の復元により、ブラウザーが次のCSSプロパティ/クラスの変更をキューに入れたりキャッシュしたりしないようにする必要があります(このようにして、CSS遷移をレンダリングできます)。


1

サンプル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

    };

関数を呼び出す:

$('article.child').redraw(); // <==悪い考え

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

私の.fadeIn(1)代わりにでも動作し.show(300)ます-要素を押し付けません。だから、それがトリガーされdisplay:noneて戻るblock
バナナ酸2015年

0

IEで私に役立つアプローチ(フォーカスを失うことのない入力があったため、表示テクニックを使用できませんでした)

マージンが0の場合に機能します(パディングを変更しても機能します)

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

0

CSSのみ。これは、子要素が削除または追加される状況で機能します。このような状況では、境界線と丸みを帯びた角にアーチファクトが残る可能性があります。

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

0

IE10 + IE11に対する私の修正。基本的に何が起こるかは、再計算する必要があるwrapping-element内にDIVを追加することです。次に、それを削除して出来上がり。魅力のように動作します:)

    _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

ほとんどの回答では、非同期のタイムアウトを使用する必要があるため、迷惑な点滅が発生します。

しかし、私はこれを思いつきましたが、同期しているのでスムーズに動作します:

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

これらの要素に関連付けられたイベントはまだ機能しますか?
ケリージョンソン

0

これはコンテンツが消えるのに役立つ私の解決策です...

<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

私は同様の問題に遭遇し、JSのこのシンプルなラインがそれを修正するのに役立ちました:

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

私の場合は、拡張機能内からCSSを変更した後、Chrome拡張機能がページを再描画しないバグでした。


4
これはキーボードのアクセシビリティを壊します。
Pangamma

0

jqueryによって追加された要素を含め、すべての状態を(リロードせずに)前の状態に戻したいと思っていました。上記の実装は機能しません。そして私は次のようにしました。

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

スクロールして別のページを開き、戻るときにリストがページがスクロールされるまでSafari iOSでレンダリングされない反応コンポーネントリストがありました。これが修正です。

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

0

以下のcssは、IE 11とEdgeで動作し、JSは必要ありません。 scaleY(1)ここでトリックを行います。最も簡単な解決策のようです。

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


0

2020:軽くて強い

以前の解決策は私にとってもう機能しません。

ブラウザはますます「役に立たない」変更を検出することで描画プロセスを最適化していると思います。

このソリューションは、ブラウザーにクローンを描画させ、元の要素を置き換えます。それは機能し、おそらくより持続可能です:

const element = document.querySelector('selector');
if (element ) {
  const clone = element.cloneNode(true);
  element.replaceWith(clone);
}

Chrome 80 / Edge 80 / Firefox 75でテスト済み

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.