HTML 为什么认为 “chucknorris” 是一种颜色?

在 HTML 中作为背景色输入时,某些随机字符串如何产生颜色?例如:

<body bgcolor="chucknorris"> test </body>

... 在所有浏览器和平台上产生背景红色的文档。

有趣的是,虽然chucknorri产生红色背景,但chucknorr产生黄色背景。

这里发生了什么?

答案

这是 Netscape 时代的遗留物:

丢失的数字被视为 0 [...]。不正确的数字将被简单地解释为 0。例如,值#F0F0F0,F0F0F0,F0F0F,#FxFxFx 和 FxFxFx 都相同。

它来自博客文章关于 Microsoft Internet Explorer 的颜色解析的一点怨言,其中涵盖了非常详细的内容,包括不同长度的颜色值等。

如果我们从博客文章中依次应用规则,则会得到以下信息:

  1. 将所有无效的十六进制字符替换为 0

    chucknorris becomes c00c0000000
  2. 填充到下一个可被 3 整除的字符总数(11-> 12)

    c00c 0000 0000
  3. 分为三个相等的组,每个分量代表 RGB 颜色的相应颜色分量:

    RGB (c00c, 0000, 0000)
  4. 将每个参数从右向下截断为两个字符

得到以下结果:

RGB (c0, 00, 00) = #C00000 or RGB(192, 0, 0)

这是一个演示bgcolor属性的示例,以产生此 “惊人的” 颜色样本:

<table>
  <tr>
    <td bgcolor="chucknorris" cellpadding="8" width="100" align="center">chuck norris</td>
    <td bgcolor="mrt"         cellpadding="8" width="100" align="center" style="color:#ffffff">Mr T</td>
    <td bgcolor="ninjaturtle" cellpadding="8" width="100" align="center" style="color:#ffffff">ninjaturtle</td>
  </tr>
  <tr>
    <td bgcolor="sick"  cellpadding="8" width="100" align="center">sick</td>
    <td bgcolor="crap"  cellpadding="8" width="100" align="center">crap</td>
    <td bgcolor="grass" cellpadding="8" width="100" align="center">grass</td>
  </tr>
</table>

这也回答了问题的另一部分。为什么bgcolor="chucknorr"产生黄色?好吧,如果我们应用规则,则字符串为:

c00c00000 => c00 c00 000 => c0 c0 00 [RGB(192, 192, 0)]

呈浅金黄色。由于字符串以 9 个字符开头,因此我们这次保留了第二个 C,因此最终以最终的颜色值结束。

我最初遇到此问题的原因是有人指出您可以进行color="crap" ,结果它变成棕色。

抱歉,不同意,但是根据解析@Yuhong Bao发布的旧色值的规则, chucknorris不等于#CC0000 ,而是等于#C00000 ,这是一个非常相似但略有不同的红色。我使用Firefox ColorZilla 加载项进行了验证。

规则规定:

  • 通过添加 0 将字符串的长度chucknorris0 3 的倍数: chucknorris0
  • 将字符串分成 3 个相等长度的字符串: chuc knor ris0
  • 将每个字符串截断为 2 个字符: ch kn ri
  • 保留十六进制值,并在必要时添加 0: C0 00 00

我能够使用这些规则正确解释以下字符串:

  • LuckyCharms
  • Luck
  • LuckBeALady
  • LuckBeALadyTonight
  • GangnamStyle

更新: 原来说颜色为#CC0000的原始答复者已经编辑了他们的答案以包括更正。

大多数浏览器将简单地忽略颜色字符串中的任何非十六进制值,而将非十六进制数字替换为零。

ChuCknorris转换为c00c0000000 。此时,浏览器会将字符串分为三个相等的部分,分别指示RedGreenBlue值: c00c 0000 0000 。每个部分中的多余位都将被忽略,这将使最终结果#c00000是带红色的颜色。

请注意,这并不适用于 CSS 颜色解析,它遵循 CSS 标准。

<p><font color='chucknorris'>Redish</font></p>
<p><font color='#c00000'>Same as above</font></p>
<p><span style="color: chucknorris">Black</span></p>

浏览器试图将chucknorris转换为十六进制颜色代码,因为它不是有效值。

  1. chucknorris ,除c外的所有内容都不是有效的十六进制值。
  2. 因此将其转换为c00c00000000
  3. 变成#c00000 ,红色阴影。

这似乎主要是Internet ExplorerOpera (12)的问题,因为 Chrome(31)和 Firefox(26)都忽略了这一点。

PS 括号中的数字是我测试过的浏览器版本。

轻一点

Chuck Norris 不符合网络标准。 Web 标准符合他的要求。 #BADA55

原因是浏览器无法理解它,并尝试以某种方式将其转换为它可以理解的值,在这种情况下,将其转换为十六进制值!

chucknorrisc开头, c是十六进制的可识别字符,它还将所有无法识别的字符转换为0

因此, chucknorris以十六进制格式变为: c00c00000000 ,所有其他人物变成0c遗体他们在哪里?

现在它们被RGB (红色,绿色,蓝色)除以 3 ... R: c00c, G: 0000, B:0000 ...

但是我们知道 RGB 的有效十六进制只有 2 个字符,表示R: c0, G: 00, B:00

因此,实际结果是:

bgcolor="#c00000";

我还将图像中的步骤添加为您的快速参考:

HTML为什么认为“ chucknorris”是一种颜色?

WHATWG HTML 规范具有解析旧版颜色值的确切算法: https : //html.spec.whatwg.org/multipage/infrastructure.html#rules-for-parsing-a-legacy-colour-value

用于解析颜色字符串的 Netscape Classic 代码是开源的: https//dxr.mozilla.org/classic/source/lib/layout/layimage.c#155

例如,请注意,每个字符都被解析为十六进制数字,然后在不检查 overflow 的情况下被转换为 32 位整数。 32 位整数只能容纳 8 个十六进制数字,这就是为什么只考虑最后 8 个字符的原因。将十六进制数字解析为 32 位整数后,然后将它们除以 16,直到它们适合 8 位,然后将其截断为 8 位整数,这就是为什么忽略前导零的原因。

更新:此代码与规范中定义的代码不完全匹配,但唯一的区别是几行代码。我认为是添加了以下几行(在 Netscape 4 中):

if (bytes_per_val > 4)
{
      bytes_per_val = 4;
}

回答:

  • 浏览器将尝试将chucknorris转换为十六进制值。
  • 由于cchucknorris 中唯一有效的十六进制字符, 因此该值变为: c00c00000000对于所有无效的值均为 0 )。
  • 然后,浏览器将结果分为 3 组: Red = c00cGreen = 0000Blue = 0000
  • 由于 html 背景的有效十六进制值对于每种颜色类型( rgb )仅包含 2 位数字,因此每组都将截断最后 2 位数字,从而使 rgb 值c00000为砖红色调的颜色。

chucknorrisc开头,浏览器将其读取为十六进制值。

因为 A,B,C,D,E 和 F 是十六进制字符

浏览器将chucknorris转换为十六进制值C00C00000000

然后将C00C00000000十六进制值转换为RGB格式(除以 3):

C00C00000000 => R:C00C, G:0000, B:0000

浏览器仅需要两位数字来指示颜色:

R:C00C, G:0000, B:0000 => R:C0, G:00, B:00 => C00000

最后,在网络浏览器中显示bgcolor = C00000

这是一个演示它的示例:

<table>
  <tr>
    <td bgcolor="chucknorris" cellpadding="10" width="150" align="center">chucknorris</td>
    <td bgcolor="c00c00000000" cellpadding="10" width="150" align="center">c00c00000000</td>
    <td bgcolor="c00000" cellpadding="10" width="150" align="center">c00000</td>
  </tr>
</table>

解析旧属性上的颜色规则涉及到比现有答案中提到的步骤更多的步骤。将截断部分转换为 2 位数部分描述为:

  1. 丢弃除最后 8 个字符以外的所有字符
  2. 只要所有组件都具有前导零 ,则一一丢弃前导零。
  3. 丢弃除前 2 个字符外的所有字符

一些例子:

oooFoooFoooF
000F 000F 000F                <- replace, pad and chunk
0F 0F 0F                      <- leading zeros truncated
0F 0F 0F                      <- truncated to 2 characters from right

oooFooFFoFFF
000F 00FF 0FFF                <- replace, pad and chunk
00F 0FF FFF                   <- leading zeros truncated
00 0F FF                      <- truncated to 2 characters from right

ABCooooooABCooooooABCoooooo
ABC000000 ABC000000 ABC000000 <- replace, pad and chunk
BC000000 BC000000 BC000000    <- truncated to 8 characters from left
BC BC BC                      <- truncated to 2 characters from right

AoCooooooAoCooooooAoCoooooo
A0C000000 A0C000000 A0C000000 <- replace, pad and chunk
0C000000 0C000000 0C000000    <- truncated to 8 characters from left
C000000 C000000 C000000       <- leading zeros truncated
C0 C0 C0                      <- truncated to 2 characters from right

以下是该算法的部分实现。它不处理错误或用户输入有效颜色的情况。

function parseColor(input) {
  // todo: return error if input is ""
  input = input.trim();
  // todo: return error if input is "transparent"
  // todo: return corresponding #rrggbb if input is a named color
  // todo: return #rrggbb if input matches #rgb
  // todo: replace unicode code points greater than U+FFFF with 00
  if (input.length > 128) {
    input = input.slice(0, 128);
  }
  if (input.charAt(0) === "#") {
    input = input.slice(1);
  }
  input = input.replace(/[^0-9A-Fa-f]/g, "0");
  while (input.length === 0 || input.length % 3 > 0) {
    input += "0";
  }
  var r = input.slice(0, input.length / 3);
  var g = input.slice(input.length / 3, input.length * 2 / 3);
  var b = input.slice(input.length * 2 / 3);
  if (r.length > 8) {
    r = r.slice(-8);
    g = g.slice(-8);
    b = b.slice(-8);
  }
  while (r.length > 2 && r.charAt(0) === "0" && g.charAt(0) === "0" && b.charAt(0) === "0") {
    r = r.slice(1);
    g = g.slice(1);
    b = b.slice(1);
  }
  if (r.length > 2) {
    r = r.slice(0, 2);
    g = g.slice(0, 2);
    b = b.slice(0, 2);
  }
  return "#" + r.padStart(2, "0") + g.padStart(2, "0") + b.padStart(2, "0");
}

$(function() {
  $("#input").on("change", function() {
    var input = $(this).val();
    var color = parseColor(input);
    var $cells = $("#result tbody td");
    $cells.eq(0).attr("bgcolor", input);
    $cells.eq(1).attr("bgcolor", color);

    var color1 = $cells.eq(0).css("background-color");
    var color2 = $cells.eq(1).css("background-color");
    $cells.eq(2).empty().append("bgcolor: " + input, "<br>", "getComputedStyle: " + color1);
    $cells.eq(3).empty().append("bgcolor: " + color, "<br>", "getComputedStyle: " + color2);
  });
});
body { font: medium monospace; }
input { width: 20em; }
table { table-layout: fixed; width: 100%; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>

<p><input id="input" placeholder="Enter color e.g. chucknorris"></p>
<table id="result">
  <thead>
    <tr>
      <th>Left Color</th>
      <th>Right Color</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>&nbsp;</td>
      <td>&nbsp;</td>
    </tr>
    <tr>
      <td>&nbsp;</td>
      <td>&nbsp;</td>
    </tr>
  </tbody>
</table>