webkit内核的浏览器为什么removeAttribute('style')会失效?

做了一些研究,应该算是理清了问题。

首先,我们在这里常说的「属性」(attributes)其实分为两种:内容属性(content attributes)以及 IDL 属性(IDL attributes)。

内容属性指那些在 HTML 中定义的属性,例如在下面这个 HTML 片段中:

<p class="lede" style="font-size: 1.2em;">

定义了 2 个内容属性 class 与 style。
IDL 指接口描述语言(Interface definition language),而描述 HTML 的接口所用的描述语言为 Web IDL(w3.org/TR/WebIDL/)。脚本语言(如 JavaScript)可以通过 HTML 定义的这些接口来进行诸如 DOM 操作的各种处理。(举个例子,HTML 的 Element 接口描述:dom.spec.whatwg.org/#)。而其中的某些 IDL 属性就是用来对应内容属性的。

HTML 规范中有如下描述(dev.w3.org/html5/spec/c):

Some IDL attributes are defined to reflect a particular content attribute. This means that on getting, the IDL attribute returns the current value of the content attribute, and on setting, the IDL attribute changes the value of the content attribute to the given value.
In general, on getting, if the content attribute is not present, the IDL attribute must act as if the content attribute's value is the empty string; and on setting, if the content attribute is not present, it must first be added.

对于 style 属性,又有这样的描述(dev.w3.org/html5/spec/g):

The style IDL attribute must return a CSSStyleDeclaration whose value represents the declarations specified in the attribute. (If the attribute is absent, the object represents an empty declaration.) Mutating the CSSStyleDeclaration object must create a style attribute on the element (if there isn't one already) and then change its value to be a value representing the serialized form of the CSSStyleDeclaration object. The same object must be returned each time.

从上面的两段内容 可以看出,浏览器在实现 HTML 的接口规范时,会区分处理内容属性和 IDL 属性,但对在读取、修改 IDL 属性时会对内容属性作同步处理(对于 style 属性,两者具体格式不同,但内容是同质的。比如 div.style.fontSize = '36px' 相当于把内容属性 style 的值设为 "font-size: 36px;")。

看了上面这一堆,我们应该可以猜测题主所述的这个问题的产生过程:
可能是由于对 "style" 这个内容属性做了一定的优化,进行了延迟创建(即在第一次运行 setAttribute('style', value) 或 getAttribute('style') 时才创建)。而最开始的 HTML 代码没有 style 属性,所以 Chrome 开始时没有创建新的内容属性。而在通过 IDL 属性修改 div.style.fontSize 后,并没有导致 Chrome 创建一个新的内容属性 "style"。然后在 removeAttribute('style') 时,因为没有找到此属性所以也没有对 IDL 属性的变化进行修改。从表现来看,最终渲染的属性值是与 IDL 属性值保持一致的,所以无法通过 removeAttribute('style') 来去掉元素的样式。

也就是说,只要我们人工保证先创建了 style 内容属性,就能够解决这个问题。所以你可以试一下以下的各个方法:

  1. 在 HTML 内容中先随便加入一点 style 比如:
    <div style="margin: 0;">测试</div>
    需要注意的是,用这种方法,在第一次删除样式后,内容属性被删除了,之后再删除又不会再生效;
  2. 在每次删除样式之前任意时间先运行一下 div.getAttribute('style');
    在这种情况下,可以发现,如果 getAttribute('style') 之后才首次修改 IDL 属性,删除也无法完成,即在 btn[0].onclick 中修改为下面代码时,也不会导致内容属性被创建:
    div.getAttribute('style');
    div.style.fontSize = '36px';
    于是我们可以进一步认为,所谓的「延迟创建」其实不是在第一次getAttribute('style') 或 setAttribute('style', value) 时总会被创建。如果在此之前对应的 IDL 属性为空,内容属性亦不会被创建;
  3. 通过 div.setAttribute('style', 'font-size: 36px;') 来设置文字大小。


所以,removeAttribute('style') 生效的前提是,在其运行之前,已经

  1. 用 IDL 属性添加了样式并调用了 getAttribute('style') 与 setAttribute('style', value) 其中之一;
  2. 或者直接用 setAttribute('style', value) 来添加样式。


至于为什么打开开发者工具就能够生效,我想也比较明显了:因为开发者工具在渲染内容的过程中,在已经通过 IDL 属性修改样式后调用了 getAttribute('style'),导致这个内容属性被创建,于是也就能被正常删除了。

我测试了一下,这个问题对于 Windows 和 Mac 下的 Chrome 与 Safari 均成立,所以应该是 WebKit 的一个 bug。

原文地址:https://www.cnblogs.com/lhgstudio/p/3358933.html