为什么设置 element.className = null 会导致 class="null"?

Why does setting element.className = null result in class="null"?

显然,尝试将 className 属性 设置为 null 不会删除 class 属性——相反,它会用 class 名称替换它称为 "null":

document.querySelector('p:nth-of-type(2)').className = null;
p[class]::before {
  content: '<p class="' attr(class) '">';
}

p:not([class])::before, p[class=""]::before {
  content: '<p>';
}

p.null {
  color: red;
}
<p class="a b c">
<p class="x y z">

除此之外,还有完整的互操作性。

我本以为 className = null 会擦除 class 属性,或者 null 会出于 className 的目的转换为空字符串,或者在至少它会产生类型错误。

什么给了?

在JavaScript中,null不转换为空字符串。它转换为字符串 "null"。手动转换就可以看到这个:

var x = String(null);
console.log(x);

这个在MDN的文档中也有提到DOMString:

Passing null to a method or parameter accepting a DOMString typically stringifies to "null".

className属性(以及id等相关属性)属于DOMString类型,在JS中直接映射到String。正是这种转换导致 class 名称字面上称为 "null",然后反映在 class 属性中并与 .null[class~=null] 等选择器匹配.

如果您期望 null 转换为空字符串,因为两者都是假值,那么它不会那样工作。仅仅因为两个值是假的,即两个值在转换为布尔值时都会导致 false,不会使这两个值等于 彼此 ,甚至不会松散。特别是,大多数对象,包括字符串,永远不会松散地等于 null,因为某些东西不能等于无(由于遗留原因,有一些例外)。有一个方便的比较 table here in MDN 可以参考。

要删除 class 属性并从元素中删除所有 class 名称,请将 className 设置为空字符串而不是 null:

document.querySelector('p:nth-of-type(2)').className = '';
p[class]::before {
  content: '<p class="' attr(class) '">';
}

p:not([class])::before, p[class=""]::before {
  content: '<p>';
}

p.null {
  color: red;
}
<p class="a b c">
<p class="x y z">

(如果browser compatibility is not a concern for you,你也可以迭代classList并单独删除每个class。有趣的是,对于性能迷来说,修改classList实际上比设置更快className — 请参阅下面的评论。据推测,设置 className 具有同时更新 classList 的副作用,这可能是它变慢的原因。)