关于CSS:为什么HTML认为”chucknorris”是一种颜色?

Why does HTML think “chucknorris” is a color?

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

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

…在所有浏览器和平台上生成具有红色背景的文档。

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

这是怎么回事?


这是网景时代的遗留物:

Missing digits are treated as 0[...]. An incorrect digit is simply interpreted as 0. For example the values #F0F0F0, F0F0F0, F0F0F, #FxFxFx and FxFxFx are all the same.

这是来自于博客文章对微软Internet Explorer的颜色分析的一点抱怨,它详细地介绍了它,包括不同长度的颜色值等。

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

  • 用0替换所有无效的十六进制字符

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

    1
    c00c 0000 0000
  • 分成三个相等的组,每个组分代表一种RGB颜色的相应颜色组分:

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

  • 结果如下:

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

    下面是一个示例,演示了bgcolor属性的实际应用,以生成这种"令人惊异的"色样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <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"会产生黄色?如果我们应用规则,字符串是:

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

    它是浅黄色的金黄色。当字符串以9个字符开始时,我们将保留第二个C,因此它将以最终的颜色值结束。

    我最初遇到这种情况的时候,有人指出你可以做color="crap",然后,好吧,结果是棕色的。


    不好意思,我不同意,但是根据@yuhong bao发布的分析传统颜色值的规则,ChuCknorris并不等同于#CC0000,而是等同于#c00000,一种非常相似但略有不同的红色色调。我使用了firefox colorzilla插件来验证这一点。

    规则规定:

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

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

    • LuckyCharms
    • Luck
    • LuckBeALady
    • LuckBeALadyTonight
    • GangnamStyle

    最新消息:最初说颜色是#CC0000的回答者已经编辑了他们的答案以包括更正。


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

    ChuCknorris翻译成c00c0000000。此时,浏览器将把字符串分成三个相等的部分,分别表示红色、绿色和蓝色值:c00c 0000 0000。每个部分中的额外位将被忽略,这将导致最终结果#c00000,这是一种红色。

    注意,这不适用于遵循CSS标准的CSS颜色分析。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <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转换为十六进制颜色代码,因为它不是有效值。

  • chucknorris中,除c以外的所有内容都不是有效的十六进制值。
  • 所以它被转换成c00c00000000
  • 变成了红色的阴影。
  • 这似乎主要是Internet Explorer和Opera(12)的问题,因为Chrome(31)和Firefox(26)都忽略了这一点。

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

    .

    轻一点的音符

    Chuck Norris doesn't conform to web standards. Web standards conform
    to him. #BADA55


    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

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

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

    1
    2
    3
    4
    if (bytes_per_val > 4)
    {
          bytes_per_val = 4;
    }

    原因是浏览器无法理解它,并试图以某种方式将其转换为它可以理解的内容,在本例中转换为十六进制值!…

    chucknorrisc开始,它是十六进制中可识别的字符,它还将所有未识别的字符转换为0

    所以十六进制格式的chucknorris变为:c00c00000000,所有其它字符变为0c保留在原来的位置…

    现在他们被RGB除以3(红、绿、蓝)。R: c00c, G: 0000, B:0000

    但是我们知道有效的十六进制对于rgb只有2个字符,意味着R: c0, G: 00, B:00

    所以真正的结果是:

    1
    bgcolor="#c00000";

    我还添加了图片中的步骤作为快速参考:

    Why does HTML think


    答:

    • 浏览器将尝试将chucknorris转换为十六进制值。
    • 由于c是chucknorris中唯一有效的十六进制字符,因此该值变为:c00c00000000(0表示所有无效的值)。
    • 然后,浏览器将结果分成3组:Red = c00cGreen = 0000Blue = 0000
    • 由于HTML背景的有效十六进制值仅包含每种颜色类型(R、G、B)的2位数字,因此每个组中最后2位数字都会被截断,剩下的rgb值为c00000,这是砖红色调的颜色。

    chucknorris是用C来统计的,浏览器读取十六进制值。

    because a,b,c,d,e,f are characters in hexadecimal

    浏览器chucknorris转换为c00c00000000十六进制值。

    然后,c00c00000000十六进制值转换为rgb格式(除以3)

    c00c00000000 => R:c00c,G:0000,B:0000

    浏览器只需要2位数字来指示颜色。

    R:c00c,G:0000,B:0000 => R:c0,G:00,B:00 => c00000

    最后,在Web浏览器中显示bgcolor = c00000

    下面是一个例子

    1
    2
    3
    4
    5
    6
    7
    <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位部分描述如下:

  • 放弃除最后8个字符以外的所有字符
  • 只要所有组件都有前导零,就逐个丢弃前导零
  • 放弃前2个字符以外的所有字符
  • 一些例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    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,"","getComputedStyle:" + color1);
        $cells.eq(3).empty().append("bgcolor:" + color,"","getComputedStyle:" + color2);
      });
    });
    1
    2
    3
    body { font: medium monospace; }
    input { width: 20em; }
    table { table-layout: fixed; width: 100%; }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js">

    <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>