首页 文章

如何强制WebKit重绘/重绘以传播样式更改?

提问于
浏览
229

我有一些简单的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上失败 .

它会影响两个后代,这些后代恰好是兄弟姐妹 . 第一个兄弟更新,但第二个没有 . 第二个元素的子元素也具有焦点,并包含 <a> 标记,该标记包含 onclick 属性中的上述代码 .

在Chrome“开发者工具”窗口中,如果我轻推(例如取消选中并检查)任何元素的任何属性,则第二个兄弟会更新为正确的样式 .

是否有一种解决方法可以轻松地以编程方式“轻推”WebKit做正确的事情?

25 回答

  • 0

    我发现了一些复杂的建议和许多不起作用的简单建议,但是Vasil Dinkov对其中一个的评论提供了一个简单的解决方案来强制重绘/重绘工作正常:

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

    如果它适用于“阻止”以外的样式,我会让其他人评论 .

    谢谢,瓦西里!

  • 2

    对我来说唯一的解决方案类似于sowasred2012的答案:

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

    我在页面上有很多问题块,所以我改变了根元素的 display 属性 . 我使用 display: table; 而不是 display: none; ,因为 none 将重置滚动偏移 .

  • 6

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

    .willnotrender { 
       transform: translateZ(0); 
    }
    

    由于这些绘画问题主要出现在Webkit / Blink中,并且此修复主要针对Webkit / Blink,因此在某些情况下更为可取 . 特别是因为接受的答案几乎肯定会导致reflow and repaint,而不仅仅是重画 .

    Webkit和Blink一直致力于渲染性能,这些故障是优化的不幸副作用,旨在减少不必要的流量和涂料 . CSS will-change或其他后续规范将是未来的解决方案,最有可能 .

    还有其他方法可以实现复合层,但这是最常见的 .

  • 1

    danorton解决方案对我不起作用 . 我有一些非常奇怪的问题,webkit根本不会绘制一些元素;输入中的文本直到onblur才更新;并且更改className不会导致重绘 .

    我意外地发现,我的解决方案是在脚本之后向主体添加一个空样式元素 .

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

    修好了 . 这有多奇怪?希望这对某人有帮助 .

  • 0

    由于显示偏移触发对我不起作用,我在这里找到了一个解决方案:

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

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

    我遇到了同样的问题 . danorton的'切换显示'修复程序在添加到动画的步进功能时对我有效,但我关注性能而且我寻找其他选项 .

    在我的情况下,没有重新绘制的元素是在一个绝对的位置元素内,当时它没有z指数 . 在此元素中添加z-index会改变Chrome的行为并重新按照预期发生 - >动画变得平滑 .

    我怀疑这是灵丹妙药,我认为这取决于为什么Chrome选择不重绘元素,但我在这个特定的解决方案中发布了它希望有人的帮助 .

    干杯,罗布

    tl;dr >> Try adding a z-index to the element or a parent thereof.

  • 3

    以下作品 . 它只需要在纯CSS中设置一次 . 它比JS功能更可靠 . 表现似乎不受影响 .

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

    出于某种原因,我无法得到danorton的工作答案,我可以看到它应该做什么,所以我稍微调整了一下:

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

    它对我有用 .

  • 0

    我今天偶然发现了这个:Element.redraw() for prototype.js

    使用:

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

    但是,有时我注意到你必须直接在有问题的元素上调用redraw() . 有时重绘父元素不会解决孩子遇到的问题 .

    关于浏览器呈现元素的方式的好文章:Rendering: repaint, reflow/relayout, restyle

  • 0

    我来到这里是因为我需要在更改其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

    我有一个插入另一个div与 position: absolute 的div的问题,插入的div没有位置属性 . 当我将其更改为 position:relative 时,它工作正常 . (真的很难找出问题)

    在我的例子中,由Angular与 ng-repeat 插入的元素 .

  • 16

    我不敢相信这仍然是一个问题2014年 . 我在滚动时刷新页面左下角的固定位置 Headers 框时遇到了这个问题, Headers 会在屏幕上“重影” . 在尝试了以上所有内容但没有成功之后,我注意到很多事情要么是由于创建非常短的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';
    
  • 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 在当前事件循环之外移动第二个样式更改是至关重要的 .

  • 0

    由于每个人似乎都有自己的问题和解决方案,我想我会添加一些适合我的东西 . 在Android 4.1上使用当前的Chrome,试图在一个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或桌面浏览器上似乎没有任何损害 .

  • 78

    我正在研究离子html5应用程序,在几个屏幕上我有 absolute 定位元素,当在IOS设备中向上或向下滚动时(iPhone 4,5,6,6)我有重绘bug .

    试过很多解决方案他们都没有工作,除了这个解决了我的问题 .

    我在那些绝对位置元素上使用了css类 .fixRepaint

    .fixRepaint{
        transform: translateZ(0);
    }
    

    这解决了我的问题,可能对某人有所帮助

  • 288

    这对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'; 
    });
    
  • 4

    “display / offsetHeight”hack在我的情况下不起作用,至少当它应用于动画元素时 .

    我有一个在页面内容上打开/关闭的下拉菜单 . 菜单关闭后(仅在webkit浏览器中),工件被留在页面内容上 . “display / offsetHeight”hack工作的唯一方法是将它应用到身体,这看起来很讨厌 .

    但是,我找到了另一个解决方案:

    • 在元素开始动画之前,添加一个在元素上定义"-webkit-backface-visibility: hidden;"的类(你也可以使用内联样式,我猜)

    • 完成动画后,删除类(或样式)

    这仍然非常hacky(它使用CSS3属性强制硬件渲染),但至少它只影响有问题的元素,并在PC和Mac上的safari和chrome上为我工作 .

  • 0

    这似乎与此有关:jQuery style not being applied in Safari

    在这些场景中,第一个响应中建议的解决方案对我来说效果很好,即:在进行样式更改后应用并删除虚拟类:

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

    这迫使safari重绘 .

  • 51

    以上建议对我没有用 . 但下面的确如此 .

    想要动态更改锚点内的文本 . “搜索”一词 . 创建了一个带有id属性的内部标记“font” . 使用javascript管理内容(如下)

    Search
    

    脚本内容:

    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

    在某些情况下更改了方向时,我遇到了一个SVG问题,该问题在Chrome for Android上消失了 . 下面的代码不会重现它,而是我们的设置 .

    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>
    

    在戳戳和刺激后的一天半,并且对这里提供的hacky解决方案不满意,我发现问题是由于它在绘制新元素时似乎将元素保留在内存中 . 解决方案是制作linearGradient的ID在SVG上是唯一的,即使它每页只使用过一次 .

    这可以通过许多不同的方式实现,但对于我们的角度应用程序,我们使用lodash uniqueId函数向范围添加变量:

    角度指令(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}})"/>

    我希望这个答案可以为别人节省一些值得面对面打击他们键盘的日子 .

  • 32

    我会建议一种不那么强硬和更正式的方法来强制重排:使用forceDOMReflowJS . 在您的情况下,您的代码将如下所示 .

    sel = document.getElementById('my_id');
    forceReflowJS( sel );
    return false;
    
  • 4

    我发现只是向元素添加内容样式会强制它重新绘制,每次想要重绘时都应该是一个不同的值,而不需要在伪元素上 .

    .selector {
        content: '1'
    }
    
  • 0

    我尝试了更多的回答,但它对我不起作用 . 与其他浏览器相比,我在使用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获取一个错误的值,getter必须介于 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
    

    问题是你失去焦点和滚动状态 .

  • 6

    一个简单的jquery解决方案:

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

    要么

    element.innerHTML = element.innerHTML;
    

    有一个SVG在添加到html时没有显示 .

    这可以在svg元素出现在屏幕上之后添加 .

    更好的解决方案是使用:

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

    和jQuery:

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

    这将在Chrome上正确呈现 .

相关问题