以下是 Sara Soueidan 的客座文章。 我通过她在 CodePen 上的所有出色作品认识了 Sara。 她正在开发一些自定义的 HTML5 视频控件,并注意到当视频进入全屏模式时,自定义内容会丢失(示例)。 通过深入研究 Shadow DOM,Sara 找到了一个解决方案……
如果您曾经使用过 HTML5 视频,那么您可能想知道,当您只向 DOM 添加了一个 <video>
标签时,页面上是如何出现一堆控制按钮、滑块和滑块滑块的。

浏览器将这些控件作为视频标签的“子树”添加到文档的渲染中。 这些元素(按钮、滑块等)是 DOM 的一部分,但您实际上无法在主 DOM 树中看到它们,您只能看到它们渲染到页面上。 稍后将详细介绍。
全屏模式下 HTML5 视频控件的问题
最近在开发一个自定义 HTML5 视频框架时,我遇到了很多设计师和开发人员在这个领域遇到的一个问题。 当视频进入全屏模式时,它会显示浏览器的原生控件,而不是我正在开发的自定义控件。 和许多其他人一样,我四处寻找这个问题的答案,但没有找到任何有用的信息。(更新:此错误已在 Chrome 错误跟踪器中提交)。
经过一些检查,我发现
- 原生控件仍然存在。 通过将视频元素的
controls
属性设置为false
,我们可以隐藏控件,但出于某种原因,在进入全屏模式时,它们会重新出现,尽管在普通屏幕模式下它们是隐藏的。(为什么?) - 自定义控件在全屏模式下隐藏在视频下方。 使用开发工具检查控件显示,控件隐藏在下方的原因是,用户代理的样式表(在本例中为 Chrome 的样式表)正在覆盖应用于控件的样式,并且使用了非常奇怪的
z-index
值,我必须说!

我们如何才能使其正常工作? 我们如何防止原生控件在全屏模式下出现,而是显示我们自己的自定义样式控件?
第二点很容易:只需覆盖用户代理样式表即可。 我们一直在样式表中这样做。 我们很快就会讲到这一点。 但是第一点呢? 我们如何隐藏浏览器添加但我们在使用的 DOM 树中看不到的元素?
请注意,本文中解释的隐藏原生控件的技术仅适用于支持 Shadow DOM 的浏览器。
快速了解 Shadow DOM
浏览器生成的 DOM 元素子树就是我们所说的“Shadow DOM”。 简单来说,它是一堆 DOM 元素,与您已经熟悉的元素(如 <div>
和 <span>
)相同,它们由浏览器作为文档片段添加,并像主 DOM 树一样渲染到页面上。
James Edwards 在他为 SitePoint 撰写的一篇文章中完美地总结了 Shadow DOM 的功能
Shadow DOM 通过创建文档片段来封装内容。 有效地,Shadow DOM 的内容是一个不同的文档,它与主文档合并以创建最终的渲染输出。
事实上,一些浏览器已经使用它来渲染一些原生部件。
浏览器这样做的原因是,浏览器开发人员决定封装一些 DOM 元素以将其隐藏在我们开发人员面前,这样我们就不用担心这些元素的实现细节,从而试图使我们更容易使用。 此外,正如 James Edwards 在他的文章中所说
由于它是隔离的,用户无法意外地破坏它,也不可能与您使用的任何类或 ID 发生命名冲突,并且主页面上的 CSS 根本不会影响它。
所以我们现在知道,添加到视频标签的控件只是浏览器为此标签生成的 Shadow DOM 子树的一部分。
隐藏原生视频控件
我们需要能够为 Shadow DOM 中的控件设置样式,但是如果我们知道的常规 CSS 选择器无法访问 Shadow DOM 元素,我们该怎么做呢?
在阅读了 Dimitri Glazkov 撰写的这篇精彩的介绍文章后,我了解到“有一个方便的伪属性功能,它允许 Shadow DOM 子树将任意伪元素标识符与子树中的元素关联起来。”
这意味着可以通过其关联的伪元素来定位 Shadow DOM 子树中的一些元素,从而为其设置样式。 这听起来很棒!
但是我们如何确定与我们需要设置样式的 Shadow DOM 元素关联的伪元素是什么呢? 一些元素或多或少是已知的,例如范围输入,它有一个可用于在 WebKit 浏览器中为其滑块设置样式的伪元素
::-webkit-slider-thumb
Firefox 还提供了两个伪元素来设置范围输入的样式,因为它从版本 23.0 开始支持它们
::-moz-range-track
和
::-moz-range-thumb
但是其他不太为人知的与其他 Shadow DOM 元素关联的伪元素呢? 与它们关联的伪元素是什么? 要找出答案,Chrome 开发工具可以帮上忙!
确定与 Shadow DOM 元素关联的伪元素
Chrome 开发工具的一项很棒的功能是,您可以在“元素”面板中检查 Shadow DOM 子树,就像检查“常规”DOM 树一样。 您只需启用此功能即可
- 转到开发工具设置(单击开发工具右下角的小齿轮图标)
- 在“常规”选项卡中,选中“显示 Shadow DOM”选项
- 重新启动开发工具(关闭并重新打开)
现在,当您检查 <video>
元素时,您会看到类似于以下屏幕截图的内容

<video>
元素的 Shadow DOM 子树会显示一个新的 #document-fragment然后,就像您可以获取任何 HTML 元素的样式规则(如果在“元素”面板中单击它),您也可以看到与 Shadow DOM 的子树关联的伪元素。 非常棒,对吧? :)

<div>
关联的伪元素名称因此,对于视频控件,我们可以从屏幕截图中看到,有一个名为 ::-webkit-media-controls
的伪元素,顾名思义,它与包含媒体控件的标签关联。
在该伪元素上设置 display:none !important;
会在普通和全屏模式下完全隐藏该元素。
::-webkit-media-controls {
display:none !important;
}
但请记住,此伪元素与子树中最外层的 <div>
关联,该 <div>
将包含媒体控件,所有类型的媒体控件,这意味着如果您在页面上的某个位置有一个 <audio>
标签,您也将隐藏该媒体元素的控件。
因此,除非您要隐藏所有浏览器原生媒体控件,否则您需要指定要隐藏的媒体控件类型,即与视频元素关联的那些控件,并且您可以通过指定此伪元素的“范围”来定位它们
video::-webkit-media-controls {
display:none !important;
}
这将完全隐藏原生控件。
另一种选择是,您可以深入子树以查找与包含实际控件(按钮、滑块等)的内部和更具体的 div
关联的伪元素,并将其隐藏。
因此,深入一层,我们得到了与内部 div
关联的伪元素
video::-webkit-media-controls-enclosure {
display:none !important;
}
将此伪元素的 display
属性设置为 none
将完全隐藏原生控件,无论是在普通模式还是全屏模式下。 您还可以看到,此伪元素在范围(视频元素)和与其关联的 div
方面更具体:包含实际控件元素的 div
。
这几乎就是隐藏全屏模式下原生控件所需做的全部操作。 很简单,对吧?
显示自定义视频控件
至于自定义控件,在隐藏原生控件后,它们仍然没有出现在我的演示中,因为,正如你在上面的屏幕截图中看到的,用户代理样式表为全屏模式设置了 z-index 值。

我不知道开发人员为什么选择这个特定的值,但可能是因为,正如 Nate Volker 在他下面的评论中指出的那样,这个数字是 32 位有符号整数的最大值,这可能是浏览器开发人员用来表示 z-index 值的数据类型。因此,为了确保自定义控件可见,您需要**将自定义控件的 z-index 设置为等于或高于此值**。
将其设置为 2147483646 会导致控件在 Firefox 的全屏模式下消失,有时(一个 bug?),也会在 Chrome 中消失,因此控件容器的 z-index 应 >= 2147483647。
.custom-video-controls {
z-index: 2147483647;
}
总结
为了在支持 Shadow DOM 的浏览器中隐藏全屏模式下的原生控件,您需要
- 定位与之关联的伪元素:
video::-webkit-media-controls-enclosure
,您可以使用 Chrome 的开发者工具并检查 Shadow DOM 来找到它,并将其 display 设置为 none,然后显示您自定义样式的控件。 - 将自定义控件的 z-index 设置为高于用户代理样式表提供的 z-index 的值。
就是这样!
演示
查看 Chris Coyier 在 CodePen 上的笔 Custom HTML5 Video Controls in Full Screen(@chriscoyier)。
不支持 Shadow DOM 的其他浏览器呢?
在工作过程中,我还测试了 Firefox 中的结果。将z-index
设置为上面提到的值也会使自定义控件出现在 Firefox 的全屏模式中,因此显示控件很容易,但 Shadow DOM 解决方案不起作用,因为 Firefox 尚未支持它,因此原生控件在全屏模式下仍然出现。
当然,您可以在其他不支持 Shadow DOM 的浏览器中期待类似的行为。
我发现隐藏这些浏览器中原生控件的唯一方法是使用自定义控件覆盖原生控件,方法是简单地使用针对全屏模式的基本 CSS 样式将自定义控件定位在其顶部。
这会隐藏原生控件,但也存在一个限制:自定义控件不能具有透明背景,否则原生控件将显示出来。
这里还有另一种可能的解决方案。James Edwards 先生在他的下面的评论中指出,原生控件也可以通过另一种方式隐藏在全屏模式下,而无需使用 Shadow DOM,方法是将全屏模式应用于一个元素,例如,一个
进一步阅读
- W3C 的 Shadow DOM 工作草案
- Shadow DOM 101 在 HTML5Rocks 上
- DOM 的黑暗阴影 在 SitePoint 上
- Shadow DOM 是什么鬼 由 Dimitri Glazkov 撰写
如果您有兴趣,还可以了解如何使用 Shadow DOM API 和 HTML <template>
创建您自己的“封装”代码和 HTML 模板,请观看 Peter Gasston 关于 Web Components 的此演示,他在今年的 CSSConfEU 上发表了该演示。
我非常喜欢它,Sara!你真是个天才。
我喜欢演示中的那个视频 :)
确实 - 非常感谢!
2147483647 是 32 位有符号整数的最大值,这很可能是浏览器开发人员用来表示 z-index 值的数据类型。
这是一个很好的提示!谢谢,Nate!我对此很好奇。:)
我构建了一个支持全屏模式的 HTML5 视频播放器,并且从未遇到过任何这些问题。
应用全屏模式时,您是将其应用于 VIDEO 元素吗?如果是这样,那就是问题所在 - 如果您将 VIDEO 包裹在一个容器 DIV 中,然后将全屏应用于该容器,那么它将在所有支持全屏模式的浏览器(即 Chrome、Safari、Firefox 和 Opera 的最新版本)中按预期工作。
这里:http://www.accessibilityoz.com.au/products/ozplayer/
感谢您的评论,James!
在本文提供的演示中,我必须将全屏模式应用于视频的容器才能将自定义控件定位到我想要的位置(在 Firefox 中),但这仍然没有解决原生控件的问题。原生控件在 Firefox 的全屏模式下仍然存在。
将全屏应用于视频容器在 Chrome 中确实有效,我刚刚测试过,但它在我的 Firefox 中不起作用。:/
编辑我之前的评论
我的错!它在 Firefox 中也有效!感谢您的提示,James。:)
我可以确认您的发现,James。但在我的情况下,我将视频放在一个容器中,其他所有内容放在后面的容器中。然后这两个容器再次被包装。此包装器是“全屏的”。并行容器解决了 z-index 问题,而我从未注意到这个问题 - 此外,它不需要 Shadow DOM 支持,也不需要我了解所有浏览器特定的伪选择器。
干杯
2147483647 = 2^31 – 1
感谢您的帖子……我充其量只是一个黑客……而且,现在我在 WordPress 中完成了我所有的工作……我的圣杯是如何将控件推到视频帧下方。这完全是愚蠢的想法吗?还是可以做到?
通过在底部为控件提供负边距,应该可以做到这一点。我还没有尝试过,但它可以做到。
伟大的 Sara 再次出手 :D
一如既往的很棒。
http://www.nilnakliyat.com/ 感谢您的评论,James!
仅供参考,IE11确实支持全屏 API,带前缀(根据 Can I use…)。
酷,这意味着应用于容器的全屏在 IE11 中可以/应该工作,但 Shadow DOM 技术可能有效也可能无效,因为 IE11 中对 Shadow DOM 的支持仍然未知(同样根据 https://caniuse.cn/shadowdom)。
不错的帖子!谢谢!!!
我不认为这是一个问题,如果您继续将容器元素设置为全屏。
包含控件和视频的那个。这实际上可能使在全屏模式下设置视频和控件的样式更容易。
因为我正在制作一个自定义视频播放器,所以我现在正在这样做。
这绝对是一种有趣的方法,但它不是一个非常强大的方法。演示在 Chrome 中运行良好,但在最新的 Safari 测试版中,自定义控件与原生控件一起存在。
正如其他一些评论者所说,更好的解决方案是将包装元素置于全屏状态。它还有一个好处,就是可以在更多浏览器中得到支持。 我编写了一个库,我们在 Vimeo 播放器中使用它,以便更容易处理所有变化和错误(有一些错误会特别影响 iframe)。
哇哦…!萨拉做得很好,我真的很欣赏。
终于解决了我的视频控件宽度问题,感谢贡献者! :)
很棒的文章..感谢萨拉和您!!
很棒的文章 :)
我已经将此解决方案应用到我的播放器中。但不幸的是,Firefix 在全宽下不显示自定义控件 :(
演示 http://www.athimannil.com/player/