Jump to content
GreenSock

mr.x

How to select the Shadow DOM?

Recommended Posts

Can GSAP access an element on the Shadow DOM? I can't seem to find the syntax, I tried a bunch of things and no luck so far. 

 

I made a simple CodePen example that looks something like this (note that this is simplified for clarity):

 

<svg>
  <symbol id="myShape">
    <rect id="rect" width="300" height="200"/>
    <circle id="circle" cx="150" cy="100" r="90"/>
  </symbol>
</svg>

<svg id="cloneParent">
  <use xlink:href="#myShape"/>
</svg>

 

I clone an SVG with USE, which puts it in the Shadow DOM. If you open the Developer Tools, you can see it in the shadow-root, with the 2 child nodes (#rect and #circle). Great.

 

I can select the clone parent SVG easily (that's what's animating), but how do you select the children of the clone in the shadow content? In this case, I want to select just the child node #circle of #cloneParent. Is this possible? 

 

Thanks in advance.

 

 

See the Pen qVZJJJ by xgraves (@xgraves) on CodePen

  • Like 1
Link to comment
Share on other sites

Deleted incorrect answer.

 

Here is the thread that discusses limitations of use tag, see if it helps.

 

 

  • Like 2
Link to comment
Share on other sites

Thanks @Sahil, I did see that thread before I posted. While it's similar, I wasn't sure if the shadow dom was their problem specifically.  @OSUblake does suggest the shadow-root can't be accessed, and he's pretty much always right, but I'm asking in case anyone has successfully done it, or has more information. I can't seem to find much documentation about it, but I feel like I've done it before in JS/JQuery?

Link to comment
Share on other sites

Hello @mr.x and Welcome to the GreenSock Forum!

 

The issue is that SVG child elements <symbol> or <defs> element are not directly rendered in the DOM. So they are removed from the browser render tree. But that is part of the SVG spec so it is not advised to try to animate CSS with graphical elements inside a <defs> or <symbol> element, since they wont visually animate!

 

SVG <symbol> spec: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/symbol

  • Note that a symbol element itself is not rendered.

Please see this for more info on why

 

https://greensock.com/forums/topic/13681-svg-gotchas/?do=findComment&comment=57408:

 

Better to access the attributes and animate them, or use the GSAP AttrPlugin for this.

 

This is regarding animating SVG elements within an SVG <defs> tag.

  •  If you are trying to animate elements within an SVG <defs> element, then you should use the GSAP AttrPlugin.
     
  • If your SVG elements are not within a SVG <defs>, <symbol>, or <mask> element .. than you should use the GSAP CSSPlugin like your already doing! Which is the default when tweening CSS properties in GSAP.

GSAP is smart enough to know when to use what. But for attributes, wrap your attributes in the attr:{} object when using the GSAP AttrPlugin

 

If you are animating SVG elements and they are nested inside a SVG <defs> element. then you need to animate those nested graphical elements with the GSAP AttrPlugin, instead of the GSAP CSSPlugin. The reason being is that Firefox will honor the SVG spec and will follow web standards whereas webkit browsers like Google Chrome and Apple Safari will allow certain non-standards and non-spec behavior. But will later remove that non-standards and non-spec behavior to line up with the spec, further confusing users / developers.

 

SVG Graphics elements are considered the folllowing:
 

<circle>, <ellipse>, <image>, <line>, <path>, <polygon>, <polyline>, <rect>, <text>, <use>

 

That is why i always debug and test in Firefox first knowing the expected behavior will line up with the spec, in this case the SVG spec. And then i debug webkit (Chrome and Safari), followed by debugging IE. Doing it that way I guarantee that HTML, DOM and SVG elements will behave according to the web standards, the spec, and cross browser.

 

CSS is not directly rendered inside a SVG <defs> or <symbol> element. That is why using the GSAP AttrPlugin works, since it animates the attribute values directly.

 

Taken from SVG spec on the MDN (Mozilla Developer Network) website.

 

https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs

  • <defs>
    SVG allows graphical objects to be defined for later reuse. It is recommended that, wherever possible, referenced elements be defined inside of a defs element. Defining these elements inside of a defs element promotes understandability of the SVG content and thus promotes accessibility. Graphical elements defined in a defs will not be directly rendered. You can use a <use> element to render those elements wherever you want on the viewport.

The same goes for the nested elements within an SVG <symbol> element. So as rule of thumb when animating SVG, if the element your animating is within a SVG <defs> or <symbol> element , then please use the GSAP AttrPlugin. But if it is not nested inside a <defs> or <symbol> element, then you can use GSAP as usual, knowing that it will use the CSSPlugin instead.

 

You could access the Shadow DOM with web components but its not really ready for prime time.

 

Happy Tweening :)

  • Like 4
Link to comment
Share on other sites

If you console log children of clone parent, you will see it only returns one child element. It basically confirms that you can't access shadow root, there maybe way but I don't know.

 

See the Pen LOZoWO?editors=0011 by Sahil89 (@Sahil89) on CodePen

 

  • Like 2
Link to comment
Share on other sites

Thanks @Jonathan

 

The "SVG Gotchas section" is such a helpful resource, thank you for all your help there. I've read through it numerous times and I did try the AttrPlugin, but I couldn't figure out how to first access the cloned node's children individually, to apply the animation to, lol. 

 

After more research I've come to the conclusion that we can't reliably access the shadow dom. A couple of years ago I was messing around with the webkit pseudo elements which are in the shadow dom, but they are specifically earmarked so it's not the same thing. The shadowroot spec is interesting, but only Chrome seems to support it so far. 

 

It's unrelated, but at one point I found an interesting side effect where you can control all the <use> clones simultaneously by animating the <symbol> directly, as demonstrated in this pen:

 

See the Pen NwRKRV by xgraves (@xgraves) on CodePen

 

Anyway thanks for all your help! 

 

  • Like 2
Link to comment
Share on other sites

No worries @mr.x

 

The reason that works is because your telling GSAP to animate the <use> element directly via their ID selector. So in your tween it's animating the <use> element via CSS ( GSAP CSSPlugin), which in turn animate the graphical element (<circle>) inside the <symbol> element. That is the nature of the SVG <use> element. ;)

 

But you could still animate the graphical element (<circle>) inside the <symbol> element with the GSAP AttrPlugin. Since the CSSPlugin can't be used due to the SVG spec not directly rendering graphical elements inside <symbol> or <defs> elements.

 

If you were testing in Google Chrome browser, it would allow you to see the shadow-DOM in the Dev Tools inspector.

 

Glad you got it sorted, Happy Tweening :)

  • Like 3
Link to comment
Share on other sites

Thanks @Jonathan!

 

In that codepen I'm afraid the ID names may have obscured my point: 

#c0 is the <symbol><circle>, and when I GSAP CSS animate it, every <use> instance follows like an army of puppets! (in this case, all circles moving horizontally)

#c2 is a <use>  instance, and it can be animated on its own, independently and in addition to the puppet master (in this case, the middle circle moving vertically, but we could also move along the x and it won't conflict)

 

This is shown in the inspector (screenshot attached), where you can see the horizontal matrix transform happening on the <symbol><circle> and the subsequent clones in the shadow dom (#c1, #c2, #c3). You can also see the vertical matrix transform on the #c2 <use> element (and not on the other <use> clone parents, as expected).

 

What's great about this is that we can control the <use> child nodes in the shadow dom by animating the <symbol> children directly, but the caveat is that every <use> clone of that <symbol> will be affected simultaneously. 

 

Interestingly, I have the {attr:{}} commented out in both the #c0 <symbol> Tweenmax and the #c2 <use> TweenMax, and if you swap them instead of the CSSPlugin, then the <symbol><circle> doesn't move, but the #c2 <use> clone still can (albeit in a different coordinate space). That means the attr plugin is working, just not on #c0. However if we set the #c0 attr to something other than x/y it will animate (such as fill:), so there must be some conflict on the x/y. 

 

Hope that makes sense! Thanks.

 

devtools.png

Link to comment
Share on other sites

16 minutes ago, mr.x said:

What's great about this is that we can control the <use> child nodes in the shadow dom by animating the <symbol> children directly, but the caveat is that every <use> clone of that <symbol> will be affected simultaneously. 

 

 

That's exactly why using the <use> element can lead to poor performance. I briefly touch on that here.

 

If you want to understand the shadow-root a little better, try playing around with Custom Elements in Chrome.

https://developer.mozilla.org/en-US/docs/Web/Web_Components/Custom_Elements

 

 

  • Like 1
Link to comment
Share on other sites

Thanks @OSUblake!

 

I think I'll switch from <use> to .cloneNode() for my project, since it will fix the shadow dom problem and improve performance as a bonus.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×