Jump to content
Search Community

gsap 3.2.1 override of the svg API method getBBox removes angular dblclick binding from DOM element.

Gary C
Moderator Tag

Recommended Posts

Posted

Hello, we have been using GSAP to handle drag/drop, panning and other animation functionality within a complex svg interface in our angular application for some time (since GSAP v2).

 

Since we updated to v3 (currently v3.11.4 but I have checked the latest version also) we have observed an issue with some DOM elements that have picked up a gsap override of the svg API method 'getBBox'. It looks like the gsap override method is there to cope with a Firefox issue to do with how it reports the bounding box of an svg element.

 

Our application has code to work around an issue with a Firefox where getBoundaryRect does not return the expected element boundary so we make an additional call to  the svg API method 'getBBox' to work out the correct location.

 

Unfortunately when we make the call to 'getBBox' if the gsap version is present on the element the call appears to do some DOM manipulation, which, as far as I can tell removes the current element and replaces it with an equivalent however the replaced element no longer has the angular binding to our dblclick event. The click event handler which is bound to the same element is still active in the replaced element.

 

The net effect is that some of our UI elements no longer respond to double click events.

 

We use a tab control to display multiple 'maps' containing 'nodes' that the user can interact with (move, drag/drop cut/paste etc). We add our nodes to each map and then use gsap.to() to position them all once added to the DOM to position information stored on disk. On the initial active tab I can see that the nodes contain a reference to gsap, on the other tabs I can see that in addition to a reference to gsap they also have the 'getBBox' override, it is these nodes that lose the dblclick event handler when we select them (by selecting a node we call getBBox to work out the bounding rect of the node to determine if we are clicking on a node or panning the background).

 

As far as I can tell I think the gsap override is introduced by your CSSPlugin - it may be timing of the loading of this plugin that explains why our initial tab of nodes don't get the override of getBBox but the others do.

 

If I downgrade to gsap v3.2.0 the issue is not present. I noticed some changes to your overridden getBBox method between 3.2.0 and 3.2.1 - the newer version will always call through to your method that manipulates the DOM, on the earlier version it will only be called if the browser raises an error on the native call.

 

During the process of creating a simplified example I observed that your code actually backs up the native 'getBBox' implementation to a property named '_gsapBBox' .

This allows me to work around the issue in our code where we make a call to getBBox, I can check the element for your backed up method '_gsapBBox' and call that  in preference to 'getBBox' - this covers both nodes with and without the gsap content.

 

I thought it was still worth raising the issue though, it took me a while to track it down and if you change the name of the backed up native implementation in a future release our work around will fail. The issue isn't easy to spot in our application - we upgraded to 3.11.4 some time ago and the issue has only recently been spotted.

 

If anyone would like to investigate further I have created a public repository of a sample application that demonstrates the issue:

'https://github.com/garycuthbert/angular_and_gsap.git'

 

If you pull the default branch (named 'simplified') - The example application is using the latest stable angular version  (17), I have node 18.13.0 installed but this or anything newer should be compatible.

If you clone the repo and run 'npm ci' followed by ng serve (i run from a bash terminal in vs code) the example app will be available on 'localhost:4200'.

 

I have added a toaster message popup to this branch that displays when the dblclick event on a node is fired, if you run the app in Chrome you will observe that the nodes on the 'map 1' tab will display the toaster message when double clicked but if you swap to the 'map 2' tab they don't, the browser console will show that on all nodes the 'click' event is fired - both the click and double click event handlers are bound on the same element in our template html.

 

I did wonder, given that the single click handler is still active in the replaced element, if there was a way to copy all event handlers to the replaced element in your overriden getBBox method?

 

Thanks for reading.

Posted

Further to this, unfortunately the work around I have applied to my sample app is not applicable to our main app.

 

In my example the one only call to getBBox is made externally to gsap which allows me to call _gsapBBox - in our main application our use of gsap to enrich our UI interaction inevitably leads to gsap calling the overridden getBBox method, it is the manipulation of the DOM within this method that removes our dblclick binding.

 

If i downgrade to gsap 3.2.0 all of our nodes are decorated with a '_gsap' objects and a '_gsDragID' property e.g:

image.png.d03b2bbc960522a241eaa10849052361.pngThis decoration is added when we make a call to gsap.to() to position our nodes once they have been added to the DOM.

In gsap v3.2.1 onwards our initial tab view has the above decoration however nodes on our other tabs (initially hidden) pick up '_gsapBBox' and 'getBBox' override (_getBBoxHack) for example:

image.png.1c248a1a64c984ab53c2560c7c9607d7.pngThe override appears to be loaded from the CSSPlugin, we are not loading in this plugin explicitly in our code.

It may help if I understood under what circumstances this plugin is loaded? presumably there is something in our code that is causing this.

 

Is there anything that can be done to the 'getBBoxHack' implementation to restore the DOM back to its original state following the manipulation to work around the Firefox issue?

 

Many thanks

Posted

Sorry to hear about the hassle, @Gary C - I've literally never heard of anyone having an issue like this in all my years as the author of GSAP. I don't have time to dig into your whole repository (a simple CodePen would SIGNIFICANTLY increase your chances of getting a quicker answer), but let me clarify a few things: 

  1. CSSPlugin is included as part of GSAP by default. It's not as if you're doing something in your app that's forcing it to load. It's just that during development, we separated out that code to make it more manageable. Your goal shouldn't be to eliminate that (at least in 99.99% of cases that'd not be appropriate)
  2. It only applies the "hack" when necessary, meaning the browser threw an error with the default implementation. It's GSAP saving you from the error essentially. 
  3. You should be able to restore the original getBBox function by using the one that's stored as element._gsapBBox. In other words, element.getBBox = element._gsapBBox; 

Again, if you've got a very simple CodePen that clearly illustrates the problem, that'd be SUPER helpful.  

Posted

Hi, thanks very much for responding.

 

Taking on board what you have clarified I have had a look at GSAP 3.2.0 c/w 3.2.1 and I can see that it is in the method '_getBBox' in CSSPlugin that is adding the '_getBBoxHack' method to our node elements that are not visible initially.

 

In both versions the 'target.getBBox()' call returns without error, for the offscreen nodes the bounds rect returned is of 0 size, in 3.2.0 it returns a 0 dimension rect  for these offscreen nodes but does not make the call to getBBoxHack:

image.png.295e34f91c57a58c183d848334620fe1.pngIn 3.2.1 however there is a check for a 0 width bounds rect which then calls getBBoxHack which replaces the getBBox method with getBBoxHack:

image.thumb.png.1e27230f5593e639627dd62e2e304d1e.pngThe call to getBBoxHack does recover the bounding rectangle of the offscreen node(s) but it looks like the DOM manipulation performed in getBBoxHack to get this information loses the binding for the dblclick event (although the click event persists) somewhere in this process.

 

So this explains why I see the replacement getBBox implementation on the offscreen nodes in 3.2.1 onwards.

 

I don't think I will be able to get an example working in codepen with the angular framework, I have had a look at stackblitz though which has an angular template, I will try and put something simple together in that and share it here.

 

Will try and get this together this week.

Many thanks

 

  • 2 weeks later...
Posted

Hello, apologies for the delay, caught up in other work related issues.

 

I have created the sample project in stackblitz, hopefully this is easier to look at than my git repo. The link is here:

https://stackblitz.com/edit/garyc-angular-gsap-dblclick-issue-aamvn8?file=src%2Fcomponents%2Fmap-container%2Fmap-container.component.ts

 

I am running this in Chrome (I don't think it will function properly in Firefox as my example code does not account for the hit test issues with Firefox).

 

The project is currently using gsap 3.2.1 where the problem first appeared for me.

 

Once the project is running you will see 2 nodes on the 'map 1' page if you double click on either you will see 'hit test' messages appear on the relevant nodes (this hit test is triggered from the 'proxyDraggable' 'onProxyPress' method which sequentially hit tests against all nodes on the map until a node is hit - in our main app this logic decides whether or not we are interacting with a node or the background). Double clicking on a node will popup a toaster message with the node id.

 

If you switch to 'map2' you will see that the nodes carry the message 'getBBoxHack', this is controlled by the presence of the method '_gsapBBox' on the node element, the 3.2.1 version of gsap adds this to the offscreen nodes when they are initially positioned - if you downgrade to v3.2.0 you will see that the nodes do not get the overridden version.

image.thumb.png.04af5ddade483f95631a6ffa72081ef9.png

If you double click on these nodes the handler is not called (and so no toaster message), looking at the element view in the browser debug tools you can observe the node element collapsing when the _getBBoxHack method operates on the DOM.

 

As has been explained in other posts I understand that the '_getBBoxHack' method has not changed in a long time it is just that from 3.2.1 onwards we more readily get this replacement - to that end we probably had similar issues before just not often showing up as it would only be added following an error with getBBox (prevalent in firefox).

 

Although in my example it is our code making the call to 'getBBox' (from 'MainContainerComponent.elementRectHitTest()') once '_getBBoxHack' is present that triggers the loss of our dblclick handler, in our main application once '_getBBoxHack' is present on the node subsequent gsap interactions will also call this method  which breaks our dblclick event handler.

 

I will continue to try to find a workaround in the angular space (may be able to trigger an angular compilation cycle at a timely point) but ideally if there was a way to preserve the  event handlers after the DOM manipulation in the overridden method this would avoid the issue - the single click handler persists which leads me to believe it may be possible.

 

Any insight/suggestions welcome,

 

Many thanks

Gary

 

  • 2 weeks later...
Posted

Apologies once again for the delay, finally got back to spending some time on this.

 

I updated my stackblittz project:

https://stackblitz.com/edit/garyc-angular-gsap-dblclick-issue-wytgvg?file=src%2Fcomponents%2Fmap-container%2Fmap-container.component.ts

 

This version includes my attempt at having a local implementation of the equivalent to 'getBBoxHack' which I call in preference to getBBox when I know getBBoxHack has replaced getBBox on the gsap modified element.

With this in place I was seeing the same DOM behavior where the manipulation performed in 'localGetBBoxHack' doesn't appear to recover the DOM to its original state and loses the angular binding to the double click event on the original nested 'map node' svg element.

 

I then added 'localGetBBoxHackNew' which I am calling from the above version of my project, in this method I clone the target element and append the clone to the temporary svg element and call the getBBox native method of the clone to retrieve the bbox, I then remove the clone and the temporary svg element from the DOM leaving the original untouched. Using this version the original target element is not moved and the double click handler remains intact.

 

Would it be possible to use a cloned element in the gsap implementation in order to retrieve the bbox instead of moving the target element?

 

Many thanks
Gary

Posted

I have studied the element view in the browser tools using my 'localGetBBoxHack' which emulates the gsap 'getBBoxHack' and I cannot see any difference in the dom elements that are moved out to the temporary svg element and then moved back, the surrounding elements decorated with angular attributes remain unchanged so it looks identical by the time the method returns but the dblclick no longer responds so there is some unseen issue with angular and the dom appendChild/insertBefore api calls.

 

Using 'cloneNode' ('localGetBBoxHackNew') to take a temporary 'deep' duplicate copy of the element to add to the temporary svg element used to get the required bbox dimensions leaves the original content unmodified and so the integrity from an angular perspective remains unchanged and so the dblclick handler continues to respond.

 

I will raise the behavior seen in the angular community in an attempt to understand the issue better but would it be possible to consider adapting the '_getBBoxHack' implementation in your library to clone the target element for use in the temporary svg element used for size determination?

 

Many thanks

Gary

Posted

I recreated a stackblitz without gsap to investigate the behavior of the same angular structure to see if it copes with direct DOM manipulation with 'appendChild'/'insertBefore' and it was OK which suggests it is a clash between gsap (onPressProxy callback ultimately calls getBBoxHack) and angular operations.

 

I can mitigate the issue we are experiencing with this by looking for the gsap replacement when our offscreen nodes are initially positioned using 'gsap.to', I do this by setting an 'onComplete' callback method and use 'onCompleteParams' to pass in the target element.

 

If _gsapGetBBox is valid I update getBBox (pointing to getBBoxHack) to the backed up original implementation in '_gsapGetBBox'.

 

Although this solves the immediate issue it looks like it could still appear given the right circumstances, the stackblitz demonstrates the issue which appears to be resolved when cloneNode is used instead of appendChild/insertBefore.

 

Can you consider changing the getBBoxHack implementation to use cloneNode? or revert to the 3.2.0 library behavior where the method substitution is not made when the target rect has zero size (i.e. not visible)?

 

Many thanks

Gary

Posted

Yeah, I revamped some of that functionality in the next release which you can preview here: 

https://assets.codepen.io/16327/gsap-latest-beta.min.js

 

I can get you a .tgz file if you need to npm install it instead. I'd love to hear if that works better for you. 

 

Thanks for your detailed explanations and suggestions!

  • Like 2
Posted

Hi Jack, a .tgz file would be great thanks, eager to try this out!

 

Many thanks

Gary

Posted

Hello, thanks very much for that, I have tried it out the beta and it does address the issues we are seeing!

 

When it is released (3.12.6?) I will upgrade and remove our workaround.

 

Thanks vey much for the support! much appreciated.

Gary

  • Like 3

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.
×
×
  • Create New...