40

This question is more aimed at user-created shadow DOM elements, but for accessibility I'll use the date input type for this question:

Say for example I have a date input on my page. With a couple of bits edited out, the shadow DOM markup for this (using Chrome) looks something like:

<input type="date">
    #document-fragment
        <div pseudo="-webkit-datetime-edit">
            <div pseudo="-webkit-datetime-edit-fields-wrapper">
                <span role="spinbutton">dd</span>
                <div pseudo="-webkit-datetime-edit-text">/</div>
                <span role="spinbutton">mm</span>
                <div pseudo="-webkit-datetime-edit-text">/</div>
                <span role="spinbutton">yyyy</span>
            </div>
        </div>
        <div></div>
        <div pseudo="-webkit-calendar-picker-indicator"></div>

The methods and properties associated with the date input do not appear to reference the shadow DOM at all (JSFiddle), so I was wondering how (if at all) can these shadow DOM elements be accessed?

Supersharp
  • 29,002
  • 9
  • 92
  • 134
James Donnelly
  • 126,410
  • 34
  • 208
  • 218

4 Answers4

23

@int32_t is right in that Shadow DOM, by definition, is a way to fill a node with DOM that you want to hide from external sources (Encapsulation). The point is that you as the component author get to choose exactly which parts will be exposed to outside CSS or JavaScript and which will not.

Unfortunately, you cannot create a public JavaScript interface to your Shadow DOM without using another bleeding-edge spec called Custom Elements. If you choose to do that, it's as simple as adding custom public methods to your element's prototype. From these you can access the internals of your Shadow DOM (see the third example here).

You can, however, expose hooks for CSS to access the internals of your Shadow DOM without using Custom Elements. There are two ways to do that:

  1. Pseudo-elements
  2. CSS Variables

Pseudo-elements

Chrome and Firefox expose certain parts of their Shadow DOM to CSS through special pseudo-elements. Here's your example of the date input with the addition of a CSS rule that only applies to the numerical part of the date field through use of the Chrome-provided -webkit-datetime-edit pseudo-element.

Here's a partial list of the available WebKit pseudo-elements. You can also just enable the Show Shadow DOM option in DevTools and look for attributes named pseudo.

Component authors can also create their own pseudo-elements to expose parts of their Shadow DOM (see the 2nd example here).

CSS Variables

An even better way is to use CSS Variables, which you can enable with Enable experimental WebKit features in about:flags in Chrome. Then check out this fiddle which uses CSS Variables to communicate to the Shadow DOM what color it should use for its "theme."

CletusW
  • 3,890
  • 1
  • 27
  • 42
  • 1
    It makes sense that you shouldn't be able to access the Shadow DOM from Javascript without some tricks. But is there a way to run Javascript in there at all? – Marcy Sutton Nov 20 '13 at 20:10
  • I'm not the best person to ask as far as vanilla Shadow DOM goes, but you certainly can when you're using Custom Elements. Check out [Polymer](http://www.polymer-project.org/getting-started.html#add-propertiesmethods-to-your-component), for example. – CletusW Nov 23 '13 at 23:36
  • 2
    An updated fiddle of the one provided above: http://jsfiddle.net/r3httdhs/ – Ally Jun 12 '17 at 02:41
  • I like this answer... however, had a question comment about the last part which revealed the answer or direction of using CSS Variables... This assumes the user has provided a style sheet within their scoped shadow-dom. What if they did not? For example what if they just styled something without providing a variable. How would one access a class within the SD w/ no css variables and or Pseudo-elements? By the way the jsfiddle you have seems to have since been deprecated – Christian Matthew Aug 10 '17 at 17:27
22

Now (2016) you can access open user-created shadow DOM elements (but no user agent-created shadow DOM!) using the method querySelector on the Shadow DOM root:

<body>
    <div id="container"></div>
    <script>
        //Shadow Root
        ̶v̶a̶r̶ ̶r̶o̶o̶t̶ ̶=̶ ̶c̶o̶n̶t̶a̶i̶n̶e̶r̶.̶c̶r̶e̶a̶t̶e̶S̶h̶a̶d̶o̶w̶R̶o̶o̶t̶(̶)̶
        //new syntax:
        var root = container.attachShadow( { mode: "open" } )

        //Inside element
        var span = document.createElement( "span" )
        span.textContent = "i'm inside the Shadow DOM"
        span.id = "inside"
        root.appendChild( span )

        //Access inside element
        console.log( container.shadowRoot.querySelector( "#inside" ) )
    </script>
</body>

//Shadow Root
var root = container.createShadowRoot()

//Inside element
var span = document.createElement( "span" )
span.textContent = "i'm inside the Shadow DOM"
span.id = "inside"
root.appendChild( span )

//Access inside element
function get() 
{
  alert( container.shadowRoot.querySelector( "#inside" ).id )
}
<!DOCTYPE html>
<html>
<head>
    <title></title>
 <meta charset="utf-8" />
</head>
<body>
 <div id="container"></div>
    <button onclick="get()">Get</button>
 <script>
 </script>
</body>
</html>
georgeawg
  • 48,608
  • 13
  • 72
  • 95
Supersharp
  • 29,002
  • 9
  • 92
  • 134
8

Yes. You just have to call .root or .shadowRoot . Here's an example,

document.getElementById('itemId').root 

You will not get the shadow dom element without the innerText or innerHTML on the parent dom element.

pravchuk
  • 903
  • 10
  • 14
4

You can't access content of Shadow DOM from scripts outside of the Shadow DOM. Encapsulation is the purpose of Shadow DOM.

int32_t
  • 5,774
  • 1
  • 22
  • 20