Jump to content

Search In
  • More options...
Find results that contain...
Find results in...

CSSPlugin Error on Class Change to HTML Element

Warning: Please note

This thread was started before GSAP 3 was released. Some information, especially the syntax, may be out of date for GSAP 3. Please see the GSAP 3 migration guide and release notes for more information about how to update the code to GSAP 3's syntax. 

Recommended Posts

The update to 2.1.x started throwing errors in several spots in my project so I rolled back until I could get a chance to debug what was different. I finally worked out what the problem is: you can no longer change a class on the HTML element as part of a timeline.


This seems to be a change in CSSPlugin.js that pushes every call to the set() function in TimelineLite through the "_getMatrix" function. That function performs a trick when the element isn't visible by appending the element to the DOM. Unfortunately, if the target is the HTML element that causes a "DOMException: Failed to execute 'appendChild' on 'Node': The new child element contains the parent" error. Line 1366 ends up trying to append the HTML element to the HTML element. I can see that v.2.0.2 didn't call "_getMatrix" at all on set() and definitely not for a simple class addition or removal.


Here's an example that will break every time:


const waitContainer = $('.waiting_container');
const waitEnd_tl = new TimelineLite()
	.to(waitContainer, 0.2, {opacity: 0, ease: 'easeOut'})
	.set($('html'), {className: '-=waiting'});


If you run the CodePen in debug view, you'll get the error after 2 seconds.

See the Pen vPVGjm by jonathansousa (@jonathansousa) on CodePen

Link to comment
Share on other sites

Thanks for reporting this. I'm traveling at the moment, but I'll look into this as soon as I return. 


I'm curious - are you saying this is only a problem for animations that target the root "html" node and are class tweens? I've actually never heard of anyone even attempting that, so I'm also curious what your use case is. 


For the record, we generally recommend avoiding class tweens if possible because they require ripping through all the properties and doing a comparison (before & after), isolating the changed properties, and going from there. They also can be confusing for developers because the actual values are stored in a completely different place (in the CSS), making it cumbersome. It's certainly not "wrong" or anything - I'm just saying that typically we recommend avoiding those if at all possible. Perhaps you could share a practical use case that sheds light on what's going on and why it'd be important to accommodate class-based tweens on the root HTML element. 



Link to comment
Share on other sites

Hi Jack,


Yes, this is only a problem when changing a class on the html element as part of a timeline. I admit it's a rare case and I can go through my code and work around it now that I've identified the issue, but it is an unintentional breaking change from 2.0.x to 2.1.x and after a good part of a day trying to track down what was going on it might be something you either want to patch or that other people could encounter and need to know to avoid.


I'm surprised to hear you don't recommend changing classes in timelines since I hadn't read anything about it being a performance problem or a discouraged feature. I use that technique fairly frequently (although not on the html element) to keep changes to the DOM display in sync with animations. For example, switching tabs where a single timeline has a fade out animation, a class change to adjust layer stacking, hidden elements, pointer-events, etc., then another animation to fade in the new content. Are class changes in a timeline something to actively avoid?


As for changing classes on the html element, as I said, it's rare but I have a couple of cases where I need to make some global change to all elements on a page in sync with an animation. For example, I have a waiting indicator that appears when changes are being saved to the backend. I animate it in while setting a class on the HTML tag that fades and locks all buttons and form fields on the page. When the API returns successfully, I animate out the waiting indicator and remove the "waiting" class to restore editing. I have something similar for animated popup boxes that require a modal state preventing interactions and animations from running beneath the popup. Setting the class within the timeline in those cases is just a convenience and I can easily move the action to a separate line outside of TimelineLite, but since I've been used to doing things like "+=waiting" in set() if I'm already running a timeline, I just kept up with the practice.



  • Like 1
Link to comment
Share on other sites

Sorry about the hassle you faced with the 2.1.x update. I'm sure it sucked to burn the better part of a day chasing that down. Ugh. I had no idea about the root HTML effect, but thanks for letting us know. 


41 minutes ago, BaskingShark said:

I hadn't read anything about it being a performance problem or a discouraged feature.


It's not something we've added to the docs or anything - we've said it quite a bit in the forums but we're also not trying to actively scare people about. Honestly, in the vast majority of cases nobody is gonna notice a performance difference. If it helps your workflow, feel free to keep doing it. I'm just a performance nut and className animations bug me (personally). Perhaps I'm making too big a deal of it :)


Anyway, thanks again for the input and sorry about the hassle. 

Link to comment
Share on other sites

Thanks for the info! That's good detail to know and I'll keep it in mind as I'm building animations.


Should I look for a patch in a future version or is this too much of an edge case and I should modify my existing timelines that touch the html element?

Link to comment
Share on other sites

  • 2 weeks later...

Since I'm importing the NPM package it's hard for me to switch over to test that beta, but looks to me like making sure "e !== _docElement" should do it.


Thanks for patching that up!

  • Like 1
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.