在本文中,我们将深入了解 <input type='color'>
元素的内部结构、浏览器不一致性、为什么它们在特定浏览器中以特定方式显示,以及如何深入研究。深入了解此输入可以帮助我们评估是否可以实现特定的跨浏览器外观,以及如何以最少的工作量和代码来实现。
以下是我们的重点内容
但在深入研究之前,我们需要了解…
可访问性问题!
我们遇到了一个重大问题:对于完全依赖键盘的用户来说,此输入在 Safari 和 Windows 上的 Firefox 中无法正常工作,但在 Mac 和 Linux 上的 Firefox 中可以正常工作(我只在 Fedora 上进行了测试,因此如果您使用其他发行版无法正常工作,请随时在评论中告诉我)。
在 Windows 上的 Firefox 中,我们可以使用 Tab 键将焦点移至输入框,然后按 Enter 键打开一个对话框……然后我们无法使用键盘导航!
我尝试过使用 Tab 键、箭头键和键盘上的所有其他键……没有任何效果!我至少可以使用 Alt + F4 关闭对话框。后来,我在 Bugzilla 上的错误单 中发现了一个解决方法:使用 Alt + Tab 切换到另一个窗口,然后再次使用 Alt + Tab 返回,这样就可以使用键盘导航选择器对话框了。
Safari 中的情况更糟。如果未启用 VoiceOver,则输入框甚至无法获得焦点 (错误单)。即使使用 VoiceOver,也无法在输入框打开的对话框中进行 Tab 键导航。
如果您想在实际网站上使用 <input type='color'>
,请告知浏览器需要解决此问题!
如何查看内部结构
在 Chrome 中,我们需要打开开发者工具,转到 **设置**,然后在 **首选项** 部分的 **元素** 下,勾选 **显示用户代理影子 DOM** 选项。
然后,当我们返回检查元素时,就可以看到其影子 DOM 的内部结构了。
在 Firefox 中,我们需要转到 about:config
并确保 devtools.inspector.showAllAnonymousContent
标志设置为 true
。
然后,我们关闭开发者工具,再次检查输入框时,就可以看到其内部结构了。
不幸的是,在 Chromium 之前的 Edge 中,我们似乎没有此选项。
内部结构
在开发者工具中显示的结构因浏览器而异,就像范围输入一样。
在 Chrome 中,影子 DOM 的顶部有一个 <div>
包装器,我们可以使用 ::-webkit-color-swatch-wrapper
访问它。
在它里面,我们还有另一个 <div>
,我们可以使用 ::-webkit-color-swatch
访问它。

在 Firefox 中,我们只看到一个 <div>
,但它没有标签,所以我们该如何访问它呢?
凭直觉,考虑到此 <div>
的 background-color
设置为输入的 value
属性,就像 ::-webkit-color-swatch
组件一样,我尝试了 ::-moz-color-swatch
。结果它有效!

但是,我后来了解到,在 Firefox 中,我们有一个更好的方法来弄清楚这一点!
我们可以进入 Firefox 开发者工具设置,在 Inspector 部分,确保已选中“显示浏览器样式”选项。然后,我们返回 Inspector 并选择 <input type='color'>
内部的 <div>
。在用户代理样式中,我们会看到一个为 input[type='color']::-moz-color-swatch
设置的规则!

在 Chromium 之前的 Edge 中,我们甚至无法看到内部的结构。我尝试过 ::-ms-color-swatch
,但它不起作用,::-ms-swatch
也不起作用(我考虑过它,因为对于 input type='range'
,我们有 ::-webkit-slider-thumb
和 ::-moz-range thumb
,但只有 ::-ms-thumb
)。
经过一番搜索,我发现的只有 这个 2016 年提出的问题。Chromium 之前的 Edge 似乎不允许我们为此输入内部的任何内容设置样式。好吧,这很糟糕。
如何查看浏览器样式
在所有浏览器中,我们都可以选择不应用任何自己的样式,然后查看计算的样式。
在 Chrome 和 Firefox 中,我们还可以看到影响当前选定元素的用户代理样式表规则集(尽管我们需要在 Firefox 中显式启用此功能,如上一节所述)。

这通常比计算的样式更有用,但也有例外,我们仍然应该始终检查计算的值。
在 Firefox 中,我们还可以查看 form
元素的 CSS 文件,位于 view-source:resource://gre-resources/forms.css
。

输入元素本身
现在我们将查看不同浏览器中一些属性的默认值,以清楚了解我们真正需要显式设置哪些内容才能获得自定义的跨浏览器结果。
在处理 <input>
元素时,我总是首先想到要检查的属性是 box-sizing
。此属性的初始值在 Firefox 中为 border-box
,但在 Chrome 和 Edge 中为 content-box
。

<input type='color'>
的 box-sizing
值在 Chrome、Firefox 和 Edge 中的比较(从上到下)。我们可以看到 Firefox 在 <input type='color'>
上将其设置为 border-box
,但 Chrome 似乎根本没有设置它,因此它保留了 content-box
的初始值(我怀疑 Edge 也一样)。
无论如何,这意味着如果我们要为此元素设置 border
或 padding
,我们还需要显式设置 box-sizing
,以便在所有这些浏览器中获得一致的结果。
font
属性的值在每个浏览器中都不同,但由于此输入框中没有文本,因此我们真正关心的是 font-size
,它在我检查过的所有浏览器中都是一致的:13.33(33)px
。至少在 Chrome 中,这个值看起来确实像是从 40px
除以 3
得到的。

<input type='color'>
的 font
值在 Chrome、Firefox 和 Edge 中的比较(从上到下)。在这种情况下,计算的样式对 Firefox 更有用,因为如果我们查看浏览器样式,我们不会获得太多有用的信息。

margin
在所有这些浏览器中也是一致的,计算结果为 0
。

<input type='color'>
的 margin
值在 Chrome、Firefox 和 Edge 中的比较(从上到下)。每个浏览器对`border`的处理都不一样。在 Chrome 和 Edge 中,我们都有`solid 1px`的边框,但`border-color`不同(Chrome 为`rgb(169, 169, 169)`,Edge 为`rgb(112, 112, 112)`)。在 Firefox 中,`border`为`outset 2px`,`border-color`为… `ThreeDLightShadow`?!

`ThreeDLightShadow`是怎么回事?如果你没听说过,别担心!它是一个(现在已过时的)CSS2 系统值,在 Windows 上的 Firefox 中,它在**计算**样式选项卡中显示为`rgb(227, 227, 227)`。

请注意,在 Firefox 中(至少在 Windows 上),操作系统缩放级别(设置 → 系统 → 显示 → 缩放和布局 → 更改文本、应用程序和其他项目的尺寸)会影响`border-width`的计算值,即使在我检查过的其他属性中似乎没有发生这种情况,并且似乎与`border-style`有关。

最奇怪的是,对于不同的缩放级别,计算出的`border-width`值似乎毫无意义。如果我们保留初始的`border-style: outset`,我们会得到
125%
时为`1.6px`150%
时为`2px`175%
时为`1.7px`200%
时为`1.5px`225%
时为`1.8px`250%
时为`1.6px`300%
时为`1.66667px`
如果我们设置`border-style: solid`,对于`50%`的倍数的缩放值,我们会得到一个计算出的`border-width`为`2px`,与设置的一样,而对于其他所有缩放级别,则得到与`border-style: outset`相同的计算值。
Chrome 和 Edge 的`padding`相同(`1px 2px`),而 Firefox 再次不同。

Firefox 的`padding`看起来像是`1px`。这正是它被设置为的值,没有任何迹象表明有任何东西覆盖它——如果一个属性被覆盖,那么它将显示为灰色并带有删除线。
![Screenshot of Firefox DevTools highlighting how the border set on input[type='color'] overrides the one set on input and the look (grey + strike-through) of overridden properties.](https://i0.wp.com/css-tricks.com/wp-content/uploads/2019/05/override_firefox.png?ssl=1)
但计算出的值为`0 8px`!此外,这个值不依赖于操作系统的缩放级别。所以,到底发生了什么?!

现在,如果你真的尝试过检查颜色输入,仔细查看了它的样式,而且你的大脑和我的工作方式不同(这意味着你会阅读你面前的内容,而不是仅仅扫描你感兴趣的一件事,完全忽略其他所有事情……)那么你可能已经注意到有一些东西覆盖了`1px`的`padding`(而且应该被标记为这样)——流相关填充!
![Screenshot of Firefox DevTools showing the flow-relative padding overriding the old padding due to higher specificity of selector (input[type='color'] vs. input).](https://i0.wp.com/css-tricks.com/wp-content/uploads/2019/05/default_l0_padding_firefox.png?ssl=1)
天啊,谁知道那些带有很多字母的属性实际上是相关的?感谢Zoltan 发现并让我知道。否则,我可能还需要两天才能弄清楚这一点。
这就引出了一个问题,即其他浏览器和/或其他属性是否也可能发生相同类型的覆盖。
Edge 不支持 CSS 逻辑属性,所以在那个方面答案是“否”。
在 Chrome 中,`<input type='color'>`没有显式设置`margin`、`border` 或`padding`的任何逻辑属性,因此没有覆盖。
在 Firefox 中,我们可能在`margin`或`border`上遇到了相同的情况,但对于这两个属性,恰好流相关的属性没有为我们的输入显式设置,因此同样没有覆盖。
即使这样,它绝对是将来需要关注的事情!
接下来是尺寸,我们的输入在 Chrome 和 Edge 中的`width`为`44px`,在 Firefox 中为`64px`。

它的`height`在所有三个浏览器中都是`23px`。

请注意,由于 Chrome 和 Edge 的`box-sizing`为`content-box`,它们的`width`和`height`值不包括`padding`或`border`。但是,由于 Firefox 的`box-sizing`设置为`border-box`,它的尺寸包括`padding`和`border`。

这意味着 Chrome 和 Edge 中的`content-box`为`44px`x`23px`,Firefox 中为`44xpx`x`19px`;Chrome 和 Edge 中的`padding-box`为`48px`x`25`,Firefox 中为`60px`x`19px`;Chrome 和 Edge 中的`border-box`为`50px`x`27px`,Firefox 中为`64px`x`23`。
我们可以清楚地看到 Chrome 中是如何设置尺寸的,我假设 Edge 中也是以同样的直接方式设置的,即使 Edge 不允许我们追踪这些东西。Firefox 没有显示这些尺寸是被显式设置的,甚至不允许我们在计算选项卡中追踪它们来自哪里(例如,它在`border`等其他属性上是这样做的)。但是,如果我们查看在`input[type='color']`上设置的所有样式,我们会发现尺寸被设置为流相关的(`inline-size`和`block-size`)。
![Screenshot of the Firefox user agent styles showing flow relative dimensions being set on input[type='color'].](https://i0.wp.com/css-tricks.com/wp-content/uploads/2019/05/default_l0_dim_firefox_logical.png?ssl=1)
我们检查的最后一个属性是实际输入的正常状态的`background`。在这里,Edge 是唯一一个有`background-image`(设置为从上到下的渐变)的浏览器,而 Chrome 和 Firefox 都有一个`background-color`设置为`ButtonFace`(另一个已过时的 CSS2 系统值)。奇怪的是,这应该是`rgb(240, 240, 240)`(根据这个资源),但它在 Chrome 中的计算值为`rgb(221, 221, 221)`。

更奇怪的是,如果我们真的在 Chrome 中查看我们的输入,它看起来确实有一个渐变的`background`!如果我们截图,然后使用一个颜色拾取器,我们会发现它有一个从`#f8f8f8`到`#ddd`的从上到下的渐变。

此外,请注意,在 Edge 中,只更改`background-color`(或其他与尺寸无关的属性,如`border-radius`)也会更改`background-image`、`background-origin`、`border-color`或`border-style`。

其他状态
我们可以通过单击 Chrome 和 Firefox 中`样式`面板中的**`:hov`**按钮,以及 Edge 中同一`样式`面板中的**`a:`**按钮,查看应用于元素的一系列其他状态的样式。这将显示一个区域,我们可以在其中检查所需的状态。

请注意,在 Firefox 中,检查一个类只会将用户样式视觉上应用到选定的元素,而不是浏览器的样式。因此,例如,如果我们检查`:hover`,我们将看不到`:hover`样式应用到我们的元素。但是,我们可以看到 DevTools 中显示的匹配选中状态的选定元素的用户代理样式。
此外,我们不能这样测试所有状态,让我们从这样一个状态开始。
:disabled
为了查看样式在这个状态下的变化,我们需要手动将`disabled`属性添加到我们的`<input type='color'>`元素中。
嗯… 任何浏览器都没有太多变化!
在 Chrome 中,我们看到`background-color`略有不同(在`:disabled`状态下为`rgb(235, 235, 228)`,而在正常状态下为`rgb(221, 221, 221)`)。

但只有查看 DevTools 中的信息才能清楚地看到这种差异。从视觉上,我可以看到一个`:disabled`的输入和一个没有`:disabled`的输入之间有轻微的差异,如果它们并排放置的话,但我事先不知道的话,光看它们是无法区分哪个是哪个的,如果我只看到一个,我无法仅通过观察来判断它是否已启用,除非点击它。

在 Firefox 中,我们为`:disabled`状态设置的与正常状态设置的值完全相同(好吧,除了光标,实际上,除了特殊情况外,它不会产生不同的结果)。怎么回事,Firefox?!

在 Edge 中,`border-color`和`background`渐变都不同。

我们有以下正常状态的样式
border-color: rgb(112, 112, 112);
background-image: linear-gradient(rgb(236, 236, 236), rgb(213, 213, 213));
以及`:disabled`状态的样式
border-color: rgb(186, 186, 186);
background-image: linear-gradient(rgb(237, 237, 237), rgb(229, 229, 229));
如果查看代码,则差异很明显,并且从视觉上来说比 Chrome 更好,尽管它可能仍然不够好

:focus
这是我们可以通过切换 DevTools 伪类来测试的一种状态。理论上是这样。实际上,它并不能在所有浏览器中真正帮助我们。
从 Chrome 开始,我们可以看到在这种状态下有一个 outline
,并且 outline-color
计算为 rgb(77, 144, 254)
,这是一种蓝色。

:focus
样式。非常直观且易于识别。
继续转向 Firefox,事情开始变得复杂!与 Chrome 不同,从 DevTools 切换 :focus
伪类对输入元素没有任何作用,但通过聚焦它(通过 Tab 键点击),border
变成了蓝色,并且我们得到了一个点状的矩形——但 DevTools 中没有显示任何正在发生的事情。

:focus
它时,Firefox 中会发生什么。如果我们检查 Firefox 的 forms.css
,它会为点状矩形提供解释。这是伪元素的点状 border
,::-moz-focus-inner
(一个伪元素,出于某种原因,在 DevTools 中未显示在我们的输入内部,因为 ::-moz-color-swatch
是)。这个 border
最初是 transparent
,然后在输入聚焦时变得可见——这里使用的伪类 (:-moz-focusring
) 基本上是新标准 (:focus-visible
) 的旧版 Firefox 版本,目前仅在 Chrome 中通过 **Experimental Web Platform features** 标志支持。

:focus
上的内部点状矩形来自哪里。蓝色 border
怎么样呢?好吧,看起来它不是由样式表设置的,而是由 OS 级别设置的。好消息是,如果我们选择这样做,我们可以覆盖所有这些样式。
在 Edge 中,我们面临着类似的情况。从 DevTools 切换 :focus
伪类时不会发生任何事情,但如果我们实际上用 Tab 键切换到我们的输入以聚焦它,我们可以看到一个内部点状矩形。

:focus
它时,Edge 中会发生什么。虽然我不知道确切的原因,但我怀疑,就像在 Firefox 中一样,这个内部矩形是由于一个伪元素在 :focus
时变得可见。
:hover
在 Chrome 中,切换此伪类不会在 DevTools 中显示任何特定于 :hover
的样式。此外,实际上将鼠标悬停在输入上似乎不会在视觉上改变任何东西。所以看起来 Chrome 真的没有任何特定于 :hover
的样式吗?
在 Firefox 中,从 DevTools 切换 :hover
伪类会在样式面板中显示一条新规则

:hover
样式,如 DevTools 所示。当实际将鼠标悬停在输入上时,我们看到 background
变成了浅蓝色,border
变成了蓝色,所以第一个想法是浅蓝色是 -moz-buttonhoverface
的值,并且蓝色 border
再次在 OS 级别设置,就像在 :focus
的情况下一样。

:hover
上实际发生的事情。但是,如果我们查看计算后的样式,我们会看到与正常状态下相同的 background
,因此,尽管在 forms.css
样式表中具有该规则,但该蓝色 background
也可能是在 OS 级别设置的。

:hover
上 <input type='color'>
的计算 background-color
。在 Edge 中,从 DevTools 切换 :hover
伪类会为我们的输入提供一个浅蓝色 (rgb(166, 244, 255)
) 的 background
和一个蓝色 (rgb(38, 160, 218)
) 的 border
,其确切值可以在 **Computed** 选项卡中找到

:hover
上 <input type='color'>
的计算 background-color
和 border-color
。:active
在 Chrome DevTools 中检查 :active
状态在视觉上没有任何作用,并且在 Styles 面板中没有显示任何特定规则。但是,如果我们实际点击我们的输入,我们会看到,即使在 DevTools 中没有显示正常状态下的渐变,它也会反转。

:active
状态下实际输入的外观。它似乎有一个渐变(与正常状态相反),尽管我们从 DevTools 中获取的信息告诉我们它没有。在 Firefox DevTools 中,在 DevTools 中切换 :active
状态没有任何作用,但如果我们也切换 :hover
状态,那么我们会得到一个规则集,该规则集会更改内联 padding
(块 padding
设置为与所有其他状态中相同的 0
值),border-style
并且将 background-color
设置回我们老朋友 ButtonFace
。

:active
样式,如 DevTools 所示。然而,在实践中,唯一与我们从 DevTools 获取的信息匹配的是逻辑 padding
更改带来的内联偏移。background
变得比 :hover
状态更浅的蓝色,border
为蓝色。这两项更改也可能是在 OS 级别发生的。

:active
状态下实际发生的事情。在 Edge 中,从 DevTools 激活 :active
类会为我们提供与 :hover
状态完全相同的样式。但是,如果我们同时启用了 :hover
和 :active
状态,事情会发生一些变化。我们仍然有一个浅蓝色的 background
和一个蓝色的 border
,但两者现在都更暗 (rgb(52, 180, 227)
用于 background-color
和 rgb(0, 137, 180)
用于 border-color
)

:active
上 <input type='color'>
的计算 background-color
和 border-color
。这是重点:如果我们想要 <input type='color'>
的一致的跨浏览器结果,我们应该自己为所有这些状态定义我们自己的清晰可辨的样式,因为幸运的是,几乎所有浏览器默认值——除了我们在 Edge 的 :focus
上获得的内部矩形——都可以被覆盖。
色板包装器
这是一个我们只在 Chrome 中看到的组件,因此,如果我们想要跨浏览器的结果,我们应该确保它不会影响内部的色板——这意味着确保它没有 margin
、border
、padding
或 background
,并且其尺寸等于实际输入的 content-box
。
为了知道我们是否需要修改这些属性(以及可能由此产生的其他属性),让我们看看浏览器默认值。
幸运的是,我们没有 margin
或 border
,所以我们不用担心这些。

margin
和 border
值。但是,我们确实有一个非零的 padding
(4px 2px
),因此,如果我们想要实现一致的跨浏览器结果,我们需要将其清零。

padding
值。尺寸都方便地设置为 100%
,这意味着我们不需要修改它们。

这里需要注意的是,我们已将 box-sizing
设置为 border-box
,因此 padding
会从此包装器上设置的尺寸中减去。

box-sizing
值。这意味着,虽然我们包装器的 padding-box
、border-box
和 margin-box
(由于没有 margin
或 border
,因此都相等)与实际 <input type='color'>
的 content-box
相同(在 Chrome 中为 44px
x23px
),获得包装器的 content-box
需要从这些尺寸中减去 padding
。结果是该框为 40px
x15px
。

background
设置为 transparent
,因此我们无需担心重置该属性。

background
值。此元素上还有一个属性引起了我的注意:display
。它的值为 flex
,这意味着它的子元素是 flex 项目。

display
值。颜色选取器
这是一个可以在 Chrome 和 Firefox 中进行样式设置的组件。遗憾的是,Edge 并没有将其公开以允许我们进行样式设置,因此我们无法更改可能需要的属性,例如 border
、border-radius
或 box-shadow
。
如果我们计划为颜色选取器设置 border
或 padding
,则需要显式设置 box-sizing
属性,因为其值在 Chrome 中为 content-box
,但在 Firefox 中为 border-box
。

box-sizing
值。幸运的是,font-size
从输入本身继承而来,因此它是一样的。

font-size
值。margin
在 Chrome 和 Firefox 中都计算为 0
。

margin
值。这是因为大多数边距都没有设置,因此它们最终都为 0
,这是 <div>
元素的默认值。但是,Firefox 将内联边距设置为 auto
,我们将在稍后解释为什么它会计算为 0
。

margin
设置为 auto
。border
在两个浏览器中都是 solid 1px
。唯一不同的是 border-color
,Chrome 中为 rgb(119, 119, 119)
,Firefox 中为 grey
(或 rgb(128, 128, 128)
,因此略微浅一些)。

border
值。请注意,在 Firefox 中(至少在 Windows 上),计算出的 border-width
取决于 OS 缩放级别,就像实际输入一样。
幸运的是,padding
在 Chrome 和 Firefox 中都是 0
。

padding
值。尺寸最终与我们预期的一样,假设颜色选取器覆盖了其父元素的整个 content-box
。

在 Chrome 中,颜色选取器的父元素是我们之前看到的 <div>
容器,其 content-box
为 4px
x15px
。这等于颜色选取器的 margin-box
和 border-box
(它们重合,因为我们没有 margin
)。由于 padding
为 0
,因此颜色选取器的 content-box
和 padding-box
相同,减去 1px
边框后,我们得到的尺寸为 38px
x13px
。
在 Firefox 中,颜色选取器的父元素是实际的输入,其 content-box
为 44px
x19px
。这等于颜色选取器的 margin-box
和 border-box
(它们重合,因为我们没有 margin
)。由于 padding
为 0
,因此颜色选取器的 content-box
和 padding-box
相同,减去 1px
边框后,我们得到的尺寸为 42px
x17px
。
在 Firefox 中,我们看到颜色选取器通过将两个尺寸都设置为 100%
来覆盖其父元素的 content-box
。

这就是为什么内联 margin
的 auto
值计算为 0
的原因。
但是 Chrome 呢?我们看不到任何实际设置的尺寸。好吧,这个结果是由于 flex
布局以及颜色选取器是一个 flex 项目,它被设置为拉伸以覆盖其父元素的 content-box
。

flex
值。最后的想法
哇,我们在这里涵盖了很多内容!虽然深入研究一个特定元素似乎很全面,但这正是说明跨浏览器支持有多困难的练习。我们有自己的样式、用户代理样式和操作系统样式要遍历,其中一些总是会保持原样。但是,正如我们在最上面讨论的那样,这最终成为一个可访问性问题,并且在实现颜色输入的实用、功能性应用程序时需要认真考虑。
请记住,其中很多内容都是可以向浏览器供应商寻求帮助的,让他们根据您报告的用例更新其实现。以下是我之前提到的三个问题,您可以参与其中或参考它们来创建新问题。
哇,这是噩梦般的领域
颜色选择器应该被火烧掉。没有人应该在真正的网站上使用它。
这是一个深入的探讨!
我不知道你可以如此轻松地检查浏览器的 shadow DOM。
这将成为一个很棒的系列文章;)
Edge Canary(我目前使用的是 77.0.223.0)具有与 Chrome 完全相同的开发者工具设置,检查 shadow DOM 会得到与 Chrome 相同的结果。
Firefox 的 padding 开发者工具“不一致”是由于后面的规则没有覆盖声明的 input 规则,覆盖它的实际上是浏览器本身,它在处理 RTL 逻辑时会进行操作,而开发者工具无法访问浏览器实时处理后的样式,而是显示并关联了渲染引擎的“原始输入”。
太激烈了!我认为我们需要对实际的颜色选择器本身进行后续跟进,除了输入之外。对于文件选择器来说,这样做是有道理的,因为它应该是原生的,但对于颜色选择器来说,就毫无意义了。
颜色选择器可以说是打破了第四堵墙……吸管能够遍历系统窗口来获取颜色,这是该功能成为原生功能的一个很好的理由。