ChatGPT解决这个技术问题 Extra ChatGPT

如何强制 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 上失败。

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

在 Chrome 的“开发者工具”窗口中,如果我轻推(例如取消选中和选中)任何元素的任何属性,第二个兄弟姐妹将更新为正确的样式。

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


J
Jack

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

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

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

谢谢,瓦西尔!


为避免闪烁,您可以尝试使用 'inline-block''table''run-in' 而不是 'none',但这可能会产生副作用。此外,超时 0 会触发重排,就像查询 offsetHeight 所做的那样:sel.style.display = 'run-in'; setTimeout(function () { sel.style.display = 'block'; }, 0);
2年后这个答案仍然有用。我只是用它来解决一个仅限 Chrome 的问题,即在以某些方式调整浏览器大小后 CSS 框阴影仍保留在屏幕上。谢谢!
@danorton您在2010年回答。现在是2013年,错误仍然存在。谢谢。顺便说一句,有人知道在 webkit 跟踪器上是否注册了任何问题?
PS .:对我来说,上述解决方案不再起作用。相反,我使用了这个(jQuery): sel.hide().show(0);来源:stackoverflow.com/questions/8840580/…
您需要做的就是读取一个 size 属性,您不需要来回更改它。
m
morewry

我们最近遇到了这个问题,发现在 CSS 中使用 translateZ 将受影响的元素提升为 composite layer 可以解决问题,而无需额外的 JavaScript。

.willnotrender { 
   transform: translateZ(0); 
}

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

Webkit 和 Blink 一直在努力提高渲染性能,而这些故障是旨在减少不必要的流量和绘制的优化的不幸副作用。 CSS will-change 或其他后续规范很可能是未来的解决方案。

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


使用 angular-carousel 时为我工作!
修复了滑动面板内的元素中边框半径丢失的问题
也适用于科尔多瓦项目!
最初的问题是在 2010 年提出的。这个答案是在 2014 年写的,是一个很好的解决方案。现在是 2019 年,这仍然是一个问题(尽管仅在 Safari 中)...... 玩得很好,WebKit。
野生动物园很烂,新的ie6
G
Gerben

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

我偶然发现,我的解决方案是在脚本之后在正文中添加一个空样式元素。

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

那解决了它。这有多奇怪?希望这对某人有帮助。


如果可以的话,我会投票 1,000,000 次。这是我可以让 chrome 重新绘制一个我拖着的愚蠢 div 的唯一方法。顺便说一句,您不必添加样式元素(至少我没有)任何元素都可以工作。我制作了一个 div 并给它一个 id,这样我就可以在每一步删除然后添加元素,以免用无用的标签填充 DOM。
只是将其与 Pumbaa80 对另一个答案的评论混合使用。我最终得到的修复是 var div = $('<div>').appendTo(element); setTimeout(function(){ div.remove(); }, 0);
这条单行对我来说非常有用! $('<style></style>').appendTo($(document.body)).remove();
@pdelanauze 答案完美。我在 ios 中唱 css3 translate 和 transform 时遇到重绘问题。
只要知道这会强制重新绘制整个页面,而大多数时候您只想重新绘制页面的某个部分......
E
EricG

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

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

IE

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

我在尝试的 hack 中使用了它,但我没有将 scale(1) 分配给自己,而是将其分配给自己作为 element.style.webkitTransform = element.style.webkitTransform。这样做的原因是,将其设置为前者会使页面略微扭曲absolute定位元素!
这对我有用,并且没有像 display 解决方案那样闪烁或重置滚动位置的问题。
我有一个使用大量动态 SVG 的 Cordova 应用程序。除了 iOS7 之外的任何地方都可以正常工作,如果在启动时不会显示 SVG 元素。添加了一个简单的 $('body')[0].style.webkitTransform = 'scale(1)';到初始化代码,问题就消失了!
目前,在最新的 Safari 和 iOS 上,这不起作用。
@setholopolus 那会是哪个版本?
R
Rob Murphy

我遇到了同样的问题。 danorton 的“切换显示”修复在添加到我的动画的步进函数时确实对我有用,但我担心性能,我寻找其他选项。

在我的情况下,未重新绘制的元素位于绝对位置元素中,当时该元素没有 z-index。向该元素添加 z-index 改变了 Chrome 的行为,并且重绘按预期发生 -> 动画变得流畅。

我怀疑这是灵丹妙药,我想这取决于 Chrome 选择不重绘元素的原因,但我在这里发布这个特定的解决方案是为了希望有人帮助。

干杯,罗伯

tl;dr >> 尝试向元素或其父元素添加 z-index。


杰出的。这太棒了,为我解决了这个问题。 A+++ 将再次进行 z-index。
在我处理滚动条和转换的情况下不起作用。
s
sweeds

以下作品。它只需要在纯 CSS 中设置一次。而且它比 JS 函数更可靠。性能似乎不受影响。

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

看起来它也对我有用。使用它来修复 Mobile Safari 的渲染问题
这解决了我的问题,迫使 safari 重新布局。但是,我使用 zoom 而不是填充:@-webkit-keyframes safariBugFix {from { zoom: 100%; } to { zoom: 99.99999%; }}
s
sowasred2012

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

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

它对我有用。


我尝试了上面的一切,但只有这个对我有用。 WTF 网络套件!这不是计算机科学!这是黑魔法!
A
António Almeida

我来到这里是因为我需要在更改其 css 后在 Chrome 中重绘滚动条。

如果有人遇到同样的问题,我通过调用这个函数来解决它:

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

这种方法不是最好的解决方案,但它可能适用于所有问题,隐藏和显示需要重绘的元素可能会解决所有问题。

这是我使用它的小提琴:http://jsfiddle.net/promatik/wZwJz/18/


伟大的!我也试图为滚动条设置动画,这就是我所需要的!顺便说一句,更好地使用溢出:动画滚动条的覆盖
A
Adam Eberlin

我今天偶然发现了这个: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


appendChild 是 DOM 方法,而不是 jQuery 方法。
J
JustGoscha

我在使用 position: absolute 插入另一个 div 的多个 div 中遇到了这个问题,插入的 div 没有位置属性。当我将其更改为 position:relative 时,它工作正常。 (真的很难查明问题)

在我的例子中,Angular 使用 ng-repeat 插入的元素。


谢谢!这也适用于我的问题,并且比上面的许多 javascript 解决方案要轻得多。
w
waffl

我不敢相信这在 2014 年仍然是一个问题。我刚刚在滚动时刷新页面左下角的固定位置标题框时遇到了这个问题,标题会在屏幕上“重影”。在尝试上述所有操作均未成功后,我注意到很多事情要么缓慢/导致问题,因为创建非常短的 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';

我非常感谢您的解决方法,这是唯一对我有用的方法。是的,现在是 2017 年,问题仍然存在。我的问题与 RTL 语言页面有关,如果在 Android 移动设备上手动刷新,该页面将呈现空白。 z-indexpointer-events 规则在这里是多余的。
我以前使用过 danorton 的修复程序,但在将显示设置为无时,我真的很难处理内容的闪烁。您的解决方案对我来说非常棒,没有内容闪存!
试试2020大声笑,它会帮助我吗? stackoverflow.com/questions/63690664/…
T
TMan

并不是说这个问题需要另一个答案,但我发现在我的特定情况下只需改变一点颜色就会强制重新绘制。

//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 对我很有用。在 jquery 不显眼的验证在解析大型表单时冻结屏幕之前没有发生重绘。以为我会发表评论,以防有人遇到同样的情况。其他解决方案在这种情况下不起作用。
G
Guillaume Gautier

我使用 transform: translateZ(0); 方法,但在某些情况下它还不够。

我不喜欢添加和删除一个类,所以我试图找到解决这个问题的方法,最终得到了一个效果很好的新技巧:

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

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

I
Ilya Sviridenko

唯一对我有用的解决方案类似于 sowasred2012 的回答:

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

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


E
Eric Ruck

由于每个人似乎都有自己的问题和解决方案,我想我会添加一些对我有用的东西。在具有当前 Chrome 的 Android 4.1 上,尝试在具有溢出:隐藏的 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);
}

没有屏幕闪烁或真正的更新,并自行清理。没有全局或类范围的变量,只有局部变量。似乎对移动 Safari/iPad 或桌面浏览器没有任何影响。


A
Anjum....

我正在开发 ionic html5 应用程序,在几个屏幕上我有 absolute 定位元素,当在 IOS 设备(iPhone 4、5、6、6+)中向上或向下滚动时,我有重绘错误。

尝试了很多解决方案,除了这个解决了我的问题,没有一个有效。

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

.fixRepaint{
    transform: translateZ(0);
}

这已经解决了我的问题,它可能会帮助一些人


操作一些我怎么没看到。 @morewry 感谢您指出
D
Dharman

这个答案适用于那些使用 Angular 10 为@danorton 的解决方案而苦苦挣扎的人。这个解决方案

sel.style.display='none';
sel.offsetHeight;
sel.style.display='';

仅适用于 angular.json 文件中的 "buildOptimizer": false。看起来优化器以某种方式打破了这一点。


W
WiR3D

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

j
jschr

我发现这种方法在处理转换时很有用

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

t
tedtedson

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

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

但是,我确实找到了另一种解决方案:

在元素开始动画之前,添加一个定义“-webkit-backface-visibility: hidden;”的类在元素上(你也可以使用内联样式,我猜)当它完成动画时,删除类(或样式)

这仍然很老套(它使用 CSS3 属性来强制硬件渲染),但至少它只影响有问题的元素,并且在 PC 和 Mac 上的 safari 和 chrome 上都为我工作。


C
Community

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

第一个响应中建议的解决方案在这些情况下对我来说效果很好,即:在进行样式更改后将虚拟类应用和删除到正文:

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

这迫使 safari 重新绘制。


为了让它在 Chrome 中工作,我必须添加: $('body').addClass('dummyclass').delay(0).removeClass('dummyclass');
g
ganesh

以上建议对我不起作用。但下面的一个。

想要动态更改锚点内的文本。 “搜索”一词。创建了一个带有 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);
    }

J
Jamie Barker

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

正文 {字体系列:tahoma,无衬线;字体大小:12px;边距:10px; } 文章 { 显示:弹性; } 一边 { flex: 0 1 10px;右边距:10px;最小宽度:10px;位置:相对; } SVG { 底部:0;左:0;立场:绝对;右:0;顶部:0; } .backgroundStop1 { 停止颜色:#5bb79e; } .backgroundStop2 { 停止颜色:#ddcb3f; } .backgroundStop3 { 停止颜色:#cf6b19; }

Donec et eros nibh。没有门,精英如箭,pulvinar 湖,augue loborti mauris,但没有医疗保健精英。酱汁在餐厅里。只要是环保的,航空公司就得付费。但骨灰盒的元素是未来的利益。 Curabitur 悲伤的 orci 和 ligula 有时,eu 酱恐惧 eleifend。对于免费宣传,最大的颤抖,是兽人的华丽投资。他还需要孩子,那是成员。脸上露出灿烂的笑容,但粉丝和妆容紧随其后的是仇恨。但互联网不限制时间或欢笑。

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

这可以通过许多不同的方式实现,但是对于我们的 Angular 应用程序,我们使用 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}})"/>

我希望这个答案可以为其他人节省一天的时间来砸他们的键盘。


J
Jack G

我会推荐一种不那么生硬和更正式的方式来强制回流:使用 forceDOMReflowJS。在您的情况下,您的代码如下所示。

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

S
Steve

我发现只是向元素添加内容样式会强制它重新绘制,每次你希望它重新绘制时这应该是一个不同的值,并且不需要在伪元素上。

.selector {
    content: '1'
}

b
bormat

我尝试了更多的答案,但它对我不起作用。与其他浏览器相比,我在 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

问题是您失去焦点和滚动状态。


A
Athelstone

这将强制重绘,同时避免闪烁、现有元素修补和任何布局问题......

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

N
Nagibaba

此代码将重新渲染 css

 document.body.style.display = 'flex';
 setTimeout(() => (document.body.style.display = ''), 0);

A
Ardika Rifqi

将转换 CSS 设置为 scale(0.9999) 显然适用于最新的 chrome。

function redraw(node){
     // Adjust the 200 as fastest as you can
     // or, change the setTimeout to requestAnimationFrame as soon as the element
     // is drawn
     setTimeout(() => (node.style.transform = "scale(0.9999)"), 200);
}

p
ptim

我发现 @morewry 的 excellent answer 解决了我的问题,而且 will-change 属性从那以后就到了。

CSS will-change 或其他后续规范很可能是未来的解决方案。

就我而言,仅 will-change: transform; 的值在 Safari 14 中有效。

.wont-update {
    will-change: transform;

    /* for Safari < 9.1 */
    transform: translateZ(0);
}

https://developer.mozilla.org/en-US/docs/Web/CSS/will-change

https://caniuse.com/will-change