首页 文章

在Chrome / Mac上强制DOM重绘/刷新

提问于
浏览
194

每隔一段时间,Chrome都会错误地呈现完全有效的HTML / CSS或根本不呈现 . 通过DOM检查器深入挖掘通常足以让它实现其方式的错误并正确地重绘,因此可以证明标记是好的 . 这种情况在我正在进行的项目中经常(并且可预测地)发生,因为我已经将代码放在适当位置以在某些情况下强制重绘 .

这适用于大多数浏览器/操作系统组合:

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

在调整一些未使用的CSS属性时,请求一些强制重绘的信息,然后取消对该属性的破坏 . 不幸的是,Chrome for Mac背后的聪明团队似乎找到了一种方法来获得offsetHeight而无需重绘 . 从而杀死一个有用的黑客 .

到目前为止,我在Chrome / Mac上获得同样效果的最佳方法就是这个丑陋:

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

实际上,强制元素跳跃一点,然后冷却一秒钟然后再跳回来 . 更糟糕的是,如果你将超时时间降低到500毫秒以下(不太明显),它往往不会产生预期的效果,因为浏览器在返回其原始状态之前不会重新绘制 .

任何人都想提供一个更好的版本的重绘/刷新黑客(最好是基于上面的第一个例子),适用于Chrome / Mac?

18 回答

  • 0

    这似乎对我有用 . 此外,它根本没有显示 .

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

    仅限CSS . 这适用于删除或添加子元素的情况 . 在这些情况下,边框和圆角可能会留下瑕疵 .

    el:after { content: " "; }
    el:before { content: " "; }
    
  • 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>
    
  • 169

    这对我有用 . Kudos go here .

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

    在IE上为我工作的方法(我无法使用显示技术,因为有一个输入不能松散焦点)

    如果你有0边距(改变填充也可以)

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

    我对IE10 IE11的修复 . 基本上会发生的是你在一个必须重新计算的包装元素中添加一个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();
        },
    
  • 60

    我们最近遇到过这种情况并发现将受影响的元素提升为composite layer并使用translateZ修复了该问题而无需额外的javascript .

    .willnotrender { 
       transform: translateZ(0); 
    }
    

    由于这些绘画问题主要出现在Webkit / Blink中,并且此修复主要针对Webkit / Blink,因此在某些情况下更为可取 . 特别是因为许多JavaScript解决方案导致reflow and repaint,而不仅仅是重新绘制 .

  • 24

    大多数答案都需要使用异步超时,这会导致恼人的眨眼 .

    但我想出了这个,因为它同步是顺利的:

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

    不确定你到底想要实现什么,但这是我过去使用的一种方法,成功迫使浏览器重绘,也许它会对你有用 .

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

    编辑:为响应ŠimeVidas,我们在这里取得的成就将是强制回流 . 你可以从主人那里了解更多信息http://paulirish.com/2011/dom-html5-css3-performance/

  • 0

    我今天在OSX El Capitan使用Chrome v51遇到了这个挑战 . 有问题的页面在Safari中运行良好 . 我在这个页面上几乎尝试了所有的建议 - 没有一个正常 - 都有副作用...我最终实现了下面的代码 - 超级简单 - 没有副作用(仍然像以前一样在Safari中工作) .

    解决方案:根据需要在有问题的元素上切换类 . 每个切换都会强制重绘 . (为方便起见,我使用了jQuery,但是vanilla 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>
    
  • 0

    示例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')重绘() . // <==糟糕的主意

    $('#parent').redraw();
    
  • 7
    function resizeWindow(){
        var evt = document.createEvent('UIEvents');
        evt.initUIEvent('resize', true, false,window,0);
        window.dispatchEvent(evt); 
    }
    

    500毫秒后调用此函数 .

  • 2

    上述答案都不适合我 . 我注意到调整窗口大小确实会导致重绘 . 所以这对我来说:

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

    我遇到了类似的问题,这个简单的JS系列帮助解决了这个问题:

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

    就我而言,在扩展程序中更改CSS后,Chrome扩展程序无法重绘页面 .

  • 1

    如果要保留声明到样式表中的样式,最好使用以下内容:

    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(出于优化目的)可能会跳过该指令 .

    更新:添加了第二个 offsetHeight 读取,有必要阻止浏览器排队/缓存以下CSS属性/类更改并恢复 display 值(这样一个CSS应该遵循可以遵循的过渡)

  • 21

    此解决方案没有超时!真的 force 重绘!适用于Android和iOS .

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

    隐藏元素然后在setTimeout 0中再次显示它将强制重绘 .

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

    调用 window.getComputedStyle() 应该强制重排

相关问题