New issue
Advanced search Search tips

Issue 668263 link

Starred by 1 user

Issue metadata

Status: WontFix
Owner: ----
Closed: Nov 2016
EstimatedDays: ----
NextAction: ----
OS: Mac
Pri: 2
Type: Bug



Sign in to add a comment

Need a way to detect v0 vs v1 shadow roots.

Reported by trusktr@gmail.com, Nov 23 2016

Issue description

UserAgent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36

Steps to reproduce the problem:
1. Try to detect if a shadow root is a v0 root or a v1 root.

What is the expected behavior?
It should be possible, since Chrome supports both versions of the ShadowDOM API currently.

What went wrong?
I haven't found a way to do it yet. Maybe there can be a `version` property on shadow roots? f.e. `root.version`

Did this work before? No 

Does this work in other browsers? No
 Chrome is the only browser with both v0 and v1 natively.

Chrome version: 54.0.2840.71  Channel: n/a
OS Version: OS X 10.12.1
Flash Version: Shockwave Flash 23.0 r0

I'm making a library, and I find the need to detect v0 vs v1 roots in order to react differently (f.e. knowing when to observe <content> elements vs <slot> elements).

Related:
http://stackoverflow.com/questions/40004064
 
Labels: Needs-Feedback Needs-Triage-M54 M-54
Could you please provide us with a sample testcase for a repro?

Comment 2 by trusktr@gmail.com, Nov 23 2016

I basically need to do something like this:

  if (getShadowRootVersion(root) == 0) {
    // observe <content> elements
  }
  else if (getShadowRootVersion(root) == 1) {
    // observe <slot> elements
  }

So far the only suggested way to do it is at http://stackoverflow.com/a/40078261/454780, but it only works in Chrome 54 (Stable) when the light tree has no child Nodes, and doesn't work in Chrome 56 (Canary) when the light tree has no child Nodes. It always fails when the light tree has more than one child Node (assignedNodes.length will be > 1, so the return value is always false).

Comment 3 by trusktr@gmail.com, Nov 23 2016

To provide more context, my library has this in it:

    ```js
    const hasShadowDomV0 =
        typeof Element.prototype.createShadowRoot == 'function'
        && typeof HTMLContentElement == 'function'
        ? true : false

    const hasShadowDomV1 =
        typeof Element.prototype.attachShadow == 'function'
        && typeof HTMLSlotElement == 'function'
        ? true : false

    function getShadowRootVersion(shadowRoot) {
        if (!shadowRoot) return null
        const slot = document.createElement('slot')
        shadowRoot.appendChild(slot)
        slot.appendChild(document.createElement('div'))
        const assignedNodes = slot.assignedNodes({ flatten: true })
        slot.remove()
        return assignedNodes.length === 1 ? 'v1' : 'v0'
    }

    class MyElement extends WebComponent(HTMLElement) {
        childConnectedCallback(child) {
            if (
                hasShadowDomV0
                && child instanceof HTMLContentElement
                && getShadowRootVersion(getAncestorShadowRootIfAny(this)) == 'v0'
            ) {
                console.log(' -- a v0 content child connected!')
            }

            else if (
                hasShadowDomV1
                && child instanceof HTMLSlotElement
                && getShadowRootVersion(getAncestorShadowRootIfAny(this)) == 'v1'
            ) {
                console.log(' -- a v1 slot child connected!')
            }
        }

        childDisconnectedCallback() {
            // similar conditional checks here.
        }
    }
    ```

where `getAncestorShadowRootIfAny` just traverses up a tree until it finds a ShadowRoot Node and returns it, or returns null if not found, and where `getShadowRootVersion` is a slightly modified version of the function on StackOverflow, but I've realized it doesn't work except for when the shadow root's host has no child nodes, which is not the case most of the time.

Comment 4 by trusktr@gmail.com, Nov 23 2016

I meant my lib has something *very similar* to the above code, and I also forgot to mention that `WebComponent` is a mixin that extends the given base class and implements the childConnectedCallback/childDisconnectedCallback methods using `MutationObserver`.

Comment 5 by trusktr@gmail.com, Nov 23 2016

Here's a little more context. My conditional check currently looks like this:

    ```js
            if (
                hasShadowDomV0
                && child instanceof HTMLContentElement
                && getAncestorShadowRootIfAny(this)
            ) {
                // work with v0 root's <content> element
            }

            else if (
                hasShadowDomV1
                && child instanceof HTMLSlotElement
                && getAncestorShadowRootIfAny(this)
            ) {
                // work with v1 root's <slot> element
            }
    ```

It works most of the time. For example, if a user has a v0 shadow root, they are likely to also use <content> elements. It will also work fine when a user uses v1 roots with <slot> elements.

However, the conditional checks can fail. For example, if the user has ShadowDOM v1 available and has placed a <slot> element inside of a v0 shadow root, then the second conditional check will pass and will do unnecessary work on the <slot> element for no reason, since nothing will ever be distributed into the slot.

Another example: imagine that someone was given the task to write the HTML that will ultimately be the DOM of a ShadowRoot, and that the person doesn't know which version ShadowRoot the HTML will be used in. That person can defensively write HTML like the following, so that it works in either version of shadow root, sothat content is distributed no matter what:

    ```html
    <div>
        <slot></slot>
        <content></content>
    </div>
    ```

As you can imagine, when my library is performing those conditional checks, it will do unnecessary work on one of the slot/content elements, and possibly introduce errors.

This is why I want to detect the version of the ShadowRoots that are used with my lib, because I'm trying to code defensively in such a way that end users won't have unexpected trouble, and in such a way that promotes ShadowDOM without any hitches.

Comment 6 by trusktr@gmail.com, Nov 23 2016

In that last HTML snippet, I meant to put:

    ```html
    <my-element>
        <slot></slot>
        <content></content>
    </my-element>
    ```

where `my-element` is defined from the hypothetical `MyElement` class of my library.

Comment 7 by hayato@chromium.org, Nov 24 2016

Status: WontFix (was: Unconfirmed)
Closing. This is not a bug report for Blink. This is a basically question which should be done in stackoverflow.

trusktr@,
Why don't you modify the function by yourself? You can use "assignedNodes.length > 0".





Comment 8 by trusktr@gmail.com, Nov 24 2016

Here's a fiddle not working in 56.0.2919.0.
https://jsfiddle.net/trusktr/p8pbdzrx

Is the return of an empty array while the slot is inside a v0 root something that I can rely on? The v1 spec doesn't mention that.

Here's a fiddle showing that `slot.assignedNodes({flatten:true})` returns a populated array when the slot isn't inside any shadow root at all.
https://jsfiddle.net/trusktr/pgxfavfy

Shouldn't that also return an empty array?

Comment 9 by hayato@chromium.org, Nov 24 2016

This should be considered as an undefined behavior, basically.
Please do not rely on any behavior where v0 and v1 are used in mixture.

I am sorry, but I could not guarantee anything. Anything could change. 

I am afraid that I can not spend much time on *fixing* an undefined behavior's *regression*. :(

Comment 10 by trusktr@gmail.com, Dec 2 2016

What about adding a simple (non-spec) property to shadow roots, something
like `root.shadowDomVersion`?
This is not a place to discuss the spec.
You should file an issue for the spec.

Comment 12 by trusktr@gmail.com, Dec 8 2016

A simple (non-spec) property isn't a spec discussion. Doesn't seem too hard. What's the chance of it getting merged if I give it a shot?
We do not add a spec non-compliant property.

Sign in to add a comment