Hiding Native HTML5 Video Controls in Full-Screen Mode

Avatar of Sara Soueidan
Sara Soueidan on (Updated on )

The following is a guest post by Sara Soueidan. I know Sara through all her excellent work on CodePen. She was working on some custom HTML5 video controls and noticed that the customizations were lost when the video went into full screen mode (example of that happening). Digging into the shadow DOM, Sara finds a solution…

If you’ve ever worked with HTML5 video then you have probably wondered how you get a bunch of control buttons, sliders, and slider thumbs on your page, when you’ve only added a single <video> tag to the DOM.

Where did all these control elements come from?

Browsers add these controls as a “sub-tree” of the video tag into the rendering of the document. These elements (buttons, sliders, etc.) are part of the DOM, but you can’t actually see them in the main DOM tree, you only see them rendered onto the page. More on this shortly.

The problem of HTML5 video controls in full-screen mode

While working on a custom HTML5 video framework lately, I stumbled upon an issue which a lot of designers and developers stumble upon in this area. Instead of displaying the custom controls I was working on, native browser controls appeared on the video when it entered the full-screen mode. Like so many others, I searched around for answers to this problem but had no luck finding any. (Update: this bug is filed in the Chrome bug tracker).

After some inspection in the dev tools, I found that:

  1. The native controls were still there. By setting the controls attribute of the video element to false, we are able to hide the controls but for some reason, when entering full-screen mode they reappear, despite being hidden in normal screen mode. (Why?)
  2. The custom controls were hidden below the video in the full-screen mode. Inspecting the controls with the dev tools showed that the reason the controls were hidden below is because the user agent’s style sheet (in this case Chrome’s style sheet) was overriding the styles applied to the controls, with a very weird z-index value, I must say!
The high z-index applied by Chrome’s user agent stylesheet

How do we make this work? How do we prevent the native controls from appearing in full-screen mode and show our own custom-styled controls instead?

The second point is easy: just override the user agent stylesheet. We do that all the time in our stylesheets. We’ll get to this shortly. But what about the first point? How can we hide elements that the browser adds but we can’t see in the DOM tree we’re working with?

Note that the technique explained in this article to hide the native controls works only in browsers that support the Shadow DOM.

Quick Introduction to Shadow DOM

The subtree of DOM elements generated by the browser is what we call the “Shadow DOM”. Plainly speaking, it’s a bunch of DOM elements, the same as the ones you’re already familiar with, like <div>s and <span>s, which are added by the browser as a document fragment, and are rendered on the page just like the main DOM tree.

James Edwards summarizes the function of the shadow DOM perfectly in his article for SitePoint:

The Shadow DOM encapsulates content by creating document fragments. Effectively, the content of a Shadow DOM is a different document, which is merged with the main document to create the overall rendered output.

In fact some browsers already use this to render some of their native widgets.

The reason why browsers do this is because browser developers decided to encapsulate some DOM elements to hide them from us developers so we’re not concerned with the implementation details of these elements, in an attempt to make things easier for us to work with. Also, as James Edwards also said in his article:

Because it’s isolated, users can’t accidentally break it, there’s no possibility of naming conflicts with any classes or IDs you use, and the CSS on the main page won’t affect it at all.

So we know now that the controls added to the video tag are just part of the shadow DOM sub-tree generated for this tag by the browser.

Hiding the native video controls

We need to be able to style the controls which are part of the shadow DOM, but how do we do that if the regular CSS selectors we know can’t access Shadow DOM elements?

After reading this superb intro article by Dimitri Glazkov I learned that “there’s a handy pseudo attribute capability, which allows shadow DOM subtrees to associate an arbitrary pseudo-element identifier with an element in the subtree.”

Which means that some elements inside the shadow DOM subtree can be styled by targeting them via their associated pseudo-element. This sounds great!

But how do we determine what pseudo-element is associated with the shadow DOM element we need to style? Some elements are more or less known, like the range input, which has a pseudo-element available to style its thumb in webkit browsers

::-webkit-slider-thumb

Firefox also provides two pseudo-elements to style range inputs now that it supports them starting from version 23.0:

::-moz-range-track

and

::-moz-range-thumb

But what about other less-known pseudo-elements associated with other shadow DOM elements? What pseudo-elements are associated with them? To find out, the Chrome dev tools come to the rescue!

Determining pseudo-elements associated with Shadow DOM elements

One of the great features of the Chrome dev tools is that you can inspect the shadow DOM subtrees in the Elements panel just like you would inspect the “regular” DOM tree. All you have to do is enable this feature:

  1. Go to dev tools settings (by clicking the little gear icon at the bottom right of the dev tools)
  2. In the General tab, check the “Show Shadow DOM” option
  3. Restart the dev tools (close and reopen)

Now, when u inspect the <video> element, you get something similar to the screenshot below:

The shadow DOM sub-tree added to the <video> element as inspected with the Chrome dev tools reveals a new #document-fragment

Then, just like you can get the style rules for any HTML element if you click on it in the Elements panel, you can see the pseudo-elements associated with the subtree of the shadow DOM too. Pretty neat, huh? :)

The CSS panel in the Chrome dev tools shows the pseudo-element name associated with the <div> inside the shadow DOM sub-tree

So for the video controls we can see from the screenshot that there’s a pseudo-element called ::-webkit-media-controls, which as the name clearly indicates, is associated with tags containing media controls.

Setting a display:none !important; on this pseudo-element hides the element completely in normal and full-screen mode.

::-webkit-media-controls {
  display:none !important;
}

But bear in mind that this pseudo-element is associated with the outermost <div> in the subtree, which will contain media controls, all kinds of media controls, which means that if you have an <audio> tag somewhere on the page, you’ll be hiding the controls for that media element as well.

So, unless you want to hide all browser native media controls, you’ll need to specify which type of media controls to hide, which are those associated with the video element, and you’d target them by specifying the “scope” for this pseudo-element:

video::-webkit-media-controls {
  display:none !important;
}

This will hide the native controls completely.

Another options is that you could go deeper in the sub-tree to find the pseudo-element associated with the inner and more specific div containing the actual controls: buttons, sliders, etc., and hide that.

So going one level deeper we get the pseudo-element associated with the inner div:

video::-webkit-media-controls-enclosure {
  display:none !important;
}

Setting the display property of this pseudo-element to none will hide the native controls completely, in both normal and full-screen mode. You can also see that this pseudo-element is more specific in terms of scope (the video element) and the div it’s associated with: the div “enclosing” the actual control elements.

And that’s pretty much all you need to do to hide the native controls in full-screen mode. Simple, right?

Showing the custom video controls

As for the custom controls, they still didn’t appear in my demo after hiding the native ones, because, as you’ve seen in the screenshot above, the user agent style sheet had a value for z-index set to the full-screen mode:

The high z-index applied by Chrome’s user agent style sheet

I have no idea why the developers chose this value in particular, but it could be because, as Nate Volker pointed out in his comment below that this number is the maximum value for a 32-bit signed integer, which could be the datatype the browser developers use to represent values for z-index. So in order to make sure the custom controls are visible you need to set the z-index for the custom controls to be equal to or higher than this value.

Setting it to 2147483646 made the controls disappear in full-screen mode on Firefox and, sometimes (a bug?), in Chrome, so the z-index for the controls container should be >= 2147483647.

.custom-video-controls {
  z-index: 2147483647;
}

Summary

In order to hide the native controls in full-screen mode in browsers supporting the Shadow DOM you need to:

  1. target the pseudo-element associated with them: video::-webkit-media-controls-enclosure, which you can find by using Chrome’s dev tools and inspecting the shadow DOM, and set it’s display to none, and then to show your custom-styled controls
  2. set the z-index of your custom controls to a value higher than the z-index supplied by the user agent style sheet.

And that’s pretty much it!

Demo

See the Pen Custom HTML5 Video Controls in Full Screen by Chris Coyier (@chriscoyier) on CodePen.

What about other browsers that don’t support the shadow DOM?

While working I also tested the results in Firefox. Setting the z-index to the value mentioned above makes the custom controls also appear in Firefox’s full-screen mode, so showing the controls is easy, but the shadow DOM solution doesn’t work because Firefox does not yet support it, so the native controls also still appear in full-screen mode.

Surely enough, you can expect similar behavior in other browsers that don’t support the shadow DOM.

The only way that I found to hide the native controls in these browsers is to cover the native controls with the custom ones, by simply positioning the custom controls on top of them using simple and basic CSS styles targeting the full-screen mode.
This hides the native controls, but also has a limitation: the custom controls can’t have a transparent background, otherwise the native controls will show through.

There is another possible solution here. Mr. James Edwards noted in his comment below that the native controls can be hidden in full-screen mode in another way too, without having to use the shadow DOM, by applying the full-screen mode to an element, for example, a

, containing the video element. I tested his technique and it worked in Chrome, and it also worked in latest version of Firefox on Windows 7, and according to Mr. Edwards it should also work in other browsers supporting the full-screen mode

Further Reading

And if you’re interested you can also see how you can use the shadow DOM API and HTML <template>s to create your own “encapsulated” code and HTML templates by watching this presentation by Peter Gasston about Web Components that he gave at this year’s CSSConfEU.