【问题标题】:Can I use javascript to force the browser to "flush" any pending layout changes?我可以使用 javascript 强制浏览器“刷新”任何待处理的布局更改吗?
【发布时间】:2011-10-20 19:39:19
【问题描述】:

我有一个使用 Javascript 构建 UI 的库,由于涉及到动态内容,我有时想将内容输出到浏览器,检查布局如何更改以支持此功能,然后根据结果执行不同的逻辑.例如:检测某些文本是否溢出并用省略号将其截断。

通常我通过输出更改来实现这一点,然后使用 window.setTimeout(0) 等待布局更新并调用其余逻辑。这显然不是最理想的,因为不同的浏览器可能会实现一个太慢而无法防止闪烁的最小超时或使用大量 CPU 的更快。

理想情况下,我想做 DOM 更改,然后强制布局同步更新并立即内联运行“修复”逻辑。有什么想法吗?

【问题讨论】:

  • 我不明白。为什么 setTimeout(0) 和一个缓慢的最小超时会导致闪烁?
  • 如果最小超时时间太长(假设超过 100 毫秒),那么我所做的更新链会导致闪烁:第一步会做一些事情,然后第二步修复它,第三步step 做其他事情等等 - 而不是一次做所有事情。

标签: javascript dom layout browser


【解决方案1】:

我的理解是读取任何 CSS 属性都会强制重排。你根本不需要setTimeout

摘自Rendering: repaint, reflow/relayout, restyle

但有时脚本可能会阻止浏览器优化回流,并导致它刷新队列并执行所有批量更改。当您请求样式信息时会发生这种情况,例如

 offsetTop, offsetLeft, offsetWidth, offsetHeight
 scrollTop/Left/Width/Height
 clientTop/Left/Width/Height
 getComputedStyle(), or currentStyle in IE

以上所有这些本质上都是请求有关节点的样式信息,并且无论何时您这样做,浏览器都必须为您提供最新的值。为此,它需要应用所有计划的更改、刷新队列、咬紧牙关并进行重排。

Here's a list of the API calls/properties that will trigger a reflow.

(此答案用于链接到现在 404 的网站。Here's a link to it in the wayback machine。)

【讨论】:

  • 它们可能会触发回流,但我怀疑实际的回流是否立即完成(〜同步)。编辑:我在你的链接中看到了The browser will setup a queue of the changes your scripts require and perform them in batches.
  • 我正在做的是尝试读取您描述的布局属性,但在某些浏览器(尤其是 IE)中,这将无法正常工作,因为尚未更新属性以纳入说明我的更改。
  • True,clientHeightcurrentStyle 没有在 IE8 中触发立即反射
  • @PeterMajeed:谢谢;找到一个实时版本。
  • Here's a list of the API calls/properties that will trigger a reflow gist.github.com/paulirish/5d52fb081b3570c81e3a 的直播网站
【解决方案2】:

我们在 IE8 上遇到了一个疯狂的问题(Firefox、Chrome 都可以)。我们在子元素上使用 toggleClass('enoMyAddressesHide')。

.enoMyAddressesHide{display:none}

但父级 div 容器不会刷新/重新布局其高度。

setTimeout(),读取位置,读取元素的宽度和高度没有帮助。 最后我们可以找到一个可行的解决方案:

jQuery(document).ready(function ($) {
    var RefreshIE8Layout = function () {
        $('.enoAddressBook:first').css('height', 'auto');
        var height = $('.enoAddressBook:first').height();
        $('.enoAddressBook:first').css('height', height);
    };

    $(".enoRowAddressInfo .enoRowAddressInfoArea ul li img.enoMyAddresses").click(function () {
        $(this).parent().find(".enoAllInfoInAddressBox,img.enoMyAddresses").toggleClass('enoMyAddressesHide');

        RefreshIE8Layout(); // fix IE8 bug, not refresh the DOM dimension after using jQuery to manipulate DOM
    });
});

它看起来很愚蠢,但它确实有效!

【讨论】:

  • 这看起来很有趣。您是否认为在将高度设置为特定值后将其重置为auto 会保留回流行为?因为我真的希望高度仍然是自动的。
  • @Guss 在我的测试中(在 Win7 上使用 IE8 原生浏览器),我们要做的是更改 height CSS 属性。你可以改成别的东西(100%, 50px, ...),然后改回auto
  • 太棒了。我不确定什么时候可以尝试(最近忙于其他项目),但它看起来像我正在寻找的东西,所以我将继续接受你的回答。谢谢!
【解决方案3】:

我不知道这是否真的适用于您的实施,但是:

var myDiv = document.getElementById("myDiv");
myDiv.innerHTML = "".concat(myDiv.innerHTML);

我相信设置 innerHTML 会强制为子元素重新加载 DOM。我不知道如果您将此范围扩展到整个站点会发生什么。听起来你已经很好地融入了整个地方的 DOM,而且这种方法可能会强制你已经创建的任何节点引用变得未定义。换句话说:

var mySelect = document.getElementById("mySelect");// suppose this lives in myDiv.
var myDiv = document.getElementById("myDiv");
myDiv.innerHTML = "".concat(myDiv.innerHTML);

在上面的例子中,我相信你会失去对 mySelect 的引用。如果你有很多这样的事情发生并且你使用的是字段而不是 getter(即,每次访问时都没有使用 $() 或 $get 或 document.getElementById(..) 获取你关心的每个元素)然后这种冲洗方式很有可能会让你感到不适。

您也肯定会丢失任何您没有手动跟踪/设置的页面状态数据 - 带有来自用户的新文本的文本框、用户新检查的复选框等,将使用这种方法重新初始化为其默认状态冲洗。

祝你好运 - 编码愉快!

B

【讨论】:

  • 是的,这是一个半身像:我不打算重新渲染整个页面,因为这会破坏很多我无法控制的事情。
【解决方案4】:

是的,你可以!!

大多数浏览器通过将更改排队并分批执行来优化回流过程。刷新渲染树更改需要您检索一些布局信息(偏移计算、getComputedStyle() 和滚动值)。

var el = document.getElementById("my-element");
var top = el.offsetTop;

以上代码将强制浏览器在渲染队列中执行更改以返回正确的值。

简单!!

【讨论】:

  • 根本不这样做。
猜你喜欢
  • 2010-10-03
  • 1970-01-01
  • 2012-07-13
  • 2012-11-08
  • 1970-01-01
  • 2011-08-08
  • 2012-02-25
  • 2012-11-12
  • 2012-01-04
相关资源
最近更新 更多