2

我想我们只是想当然地认为,每当我们更改 DOM 元素的属性或其 CSSStyleDeclation 对象的属性时,页面都会被重绘:

http://jsfiddle.net/peqtL/

document.getElementById("foo").innerHTML = "bar";
document.getElementById("foo").style.padding = "30px";

但是想一想,.style返回一个CSSStyleDeclaration对象,padding返回一个属性。现在我们只需设置一个属性,它是如何触发并导致页面看起来不同的呢?

我在想,这会是经典的“观察者模式”吗,页面渲染器注册每个 DOM 元素和CSSStyleDeclaration对象,每当属性更改时,通知渲染器重绘页面的一部分?

或者,是不是即使我们改变了一些东西,也会影响到整个页面:比如z-index会影响自己和所有兄弟节点的“覆盖”顺序,所以我们需要重绘父节点并向下树,或者如果元素有position: relativeposition: absolute,它可以影响页面上的任何元素,所以需要重绘整个页面。所以换句话说,渲染器可能不需要注册每个 DOM 元素及其CSSStyleDeclaration对象。渲染器只需要注册顶级 DOM 对象(或者document标签<html>元素,即document.documentElement一个简单的实现是,对其属性或其后代的属性进行任何更改,然后通知渲染器重绘整个页面。每当我们简单地获取属性的值时,都不需要通知渲染器来重绘页面。

而这种“观察者模式”是 DOM 和 JavaScript 引擎内部的——也就是说,我们无法真正接触或知道它是如何在底层完成的?

我知道实现应该是隐藏的,并且对于 HTML 和 JavaScript 的用户来说是未知的,但是从编程的角度来看,我希望知道作为软件系统的一部分可以实现它的实用方法是什么。

4

2 回答 2

0

实现这种模式的一个好方法是使用 css 类。您应该避免修改每个 HTML 元素的 DOM 特性。修改 HTML 元素集的视觉方面或添加新类。在下面,您可以看到一个简单的示例:

    //html
    First name: <input type="text" id="fmame" class="mandatory">
    Last name:  <input type="text" id="lname" class="mandatory" value="Blablabla">

    //javascript
    var inputs = $("input");
    for (var i = 0, j = inputs.length; i < j; i++) {
        if (inputs[i].className === 'mandatory' && inputs[i].value === '')
            inputs[i].className += ' error';
    }

    //css
    .error {
        background-color: red;
    }
于 2013-05-21T15:26:09.513 回答
0

观察者模式

似乎所有布局引擎(Gecko、Trident 和 WebKit)都在 DOM 上实现了侦听器(根据此文档),这意味着它们实现了观察者模式。实际上,每个节点都附加了一个侦听器。

根据某个 Lindsey Simon 的说法,更新可以触发任何方向的回流,直到树上的任何端点,但布局引擎会尽量减少回流和重绘计算。我会解释...

www-archive.mozilla.org中,Gecko 出于多种原因调用回流,每个原因都有不同的计算结果,包括InitialResize(相当明显)。这里相关的是增量

增量回流

...当脚本操作 DOM 或资源已完成异步加载(如图像)时发生。Gecko 将此称为“增量”,因为它试图“尽可能多地重用现有状态”来源)。当您修改 DOM 节点时,侦听器会自动将回流请求添加到异步队列。为了优化队列的评估,Gecko 将“合并”,或在共享目标的队列中合并类似的回流请求。不再相关的重排请求将被删除(例如,如果目标帧已被删除)。

高效的 DOM 操作

有一些方法可以显式避免从脚本中触发不必要的重排。

  • 在将文档片段添加到 DOM 节点之前,对文档片段的操作不会触发重排。
  • display:none即使您通过脚本对对象执行样式更改,在更改其显示类型之前不会触发重排的节点。
  • 如果要执行高效的动画,position:absolute则不会向其父级发出回流,因为它已从流程中取出。

请注意,从 DOM 读取属性也可以强制进行队列评估,因此信息是准确的。

我希望这很有用。

于 2015-02-19T02:10:12.097 回答