Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calling resize() and fit() on SVG that isn't currently displayed breaks all interaction with SVG #279

Open
mdb07a opened this issue Jan 23, 2018 · 4 comments

Comments

@mdb07a
Copy link

mdb07a commented Jan 23, 2018

Bug report

Expected behaviour

After calling resize() and fit() or contain() on a SVG that is not visible, the user should be able to restore full functionality by making the SVG visible again and then using resize() and fit() or contain() in order to restore a usable viewport.

Actual behaviour

If the fit() or contain() method is run on a SVG while it's not visible due to it or its parent element not being displayed on the DOM, those methods (as well as the zoom, pan, and reset methods) will all become broken and will cause a Javascript error on subsequent uses. Since the error also affects fit() and contain(), it's impossible to get back into a usable state without refreshing.

The error is "InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable" in Firefox and "Uncaught InvalidStateError: Failed to execute 'inverse' on 'SVGMatrix': The matrix is not invertible" in Chrome.

Steps to reproduce the behaviour

simple reproduction case: https://codepen.io/mdb07a/pen/YYMLqW

  1. Click the "Hide SVG" button to hide the SVG with "display:none".

  2. Click the "fit()" button to run the resize() and fit() methods on the SVG while it's hidden.

  3. Click the "Show All" button to make the SVG visible again.

  4. Click the "fit()" button to run the resize() and fit() methods on the SVG now that it's visible.

  5. The fit() method will cause the embedded controls to appear appropriately, but will then fail with a Javascript error which will be displayed in the browser console.

  6. Refresh the page to clear this broken state.

  7. Click the "Hide Container" button to hide the container div that the SVG is inside.

  8. Click the "contain()" button to run the resize() and contain() methods on the SVG, which is not visible because its parent element is not displayed.

  9. Click the "Show All" button to make the container visible again.

  10. Click the "contain()" button to run the resize() and contain() methods on the SVG, which is now visible again because its parent element is visible.

  11. The fit() method will cause the embedded controls to appear appropriately, but will then fail with a Javascript error which will be displayed in the browser console.

  12. Click the zoom in or zoom out button on the SVG. You will get the same Javascript error.

  13. Click the Reset button on the SVG. You will get the same Javascript error.

  14. Try to pan the SVG by clicking and dragging. You will get the same Javascript error.

Configuration

Other Notes

Calling resize() and then fit() or contain() while the SVG is not visible essentially zeroes out the CTM. Later operations will then fetch the current CTM, which will be all zeroes, and attempt to transform them, leading to a Javascript error on the inverse() call because inverting a zeroed-out SVGMatrix appears to be an invalid operation. Since this issue affects all further zooming, panning, resetting, and both fit() and contain(), the user is put into an unrecoverable state in which the SVG is impossible to interact with until the instance is destroyed and recreated or the page is refreshed.

That being said, it's easy enough for developers to work around once the issue is understood - just make sure not to resize() and fit()/contain() when the SVG isn't displayed. That shouldn't affect functionality, since there's no real need to make those calls when the SVG isn't displayed, so it's typically just tied to a resize call that runs whether or not the SVG is currently visible. It's just a matter of knowing about this issue and making sure to account for it and ensure that these calls aren't run when the SVG isn't displayed.

@bumbu
Copy link
Owner

bumbu commented Jan 27, 2018

Hi @mdb07a,

Thank you for this very detailed report.
There were few related bug reports in past (although not that detailed: #105, #247). From what I remember the issue was that some browsers simply remove the entire SVG element from DOM when it (or it's parent) have display: hidden. It is probably an optimisation technique, but it just makes the SVG unusable.

I don't think we can do much about it, and I don't think we should check if the element and all it's parents are visible on each method call. It will just add overhead (mostly in terms of reasoning about code and behaviour) for an edge case.

I'll keep this issue open so that other people who'll stumble upon it will know that it's an expected behaviour.

Thank you again!

@mdb07a
Copy link
Author

mdb07a commented Jan 31, 2018

Adding a console message or throwing a clearer error when a zeroed-out SVGMatrix passes through getCTM() might help, since the standard error message is a bit vague (especially on Firefox). Other than that, though, I agree - I thought about it a while, and while detecting the potential failure state is easy enough, actually handling it introduces a fair amount of complexity for something that's not terribly difficult for people to just work around.

@bumbu
Copy link
Owner

bumbu commented Feb 4, 2018

@mdb07a agree, do you mind creating a PR for that?

@ckingchris
Copy link

Is there anyway to hide this error message from the browser console? If not, could there be a setting added such as hideErrors: true/false?

Error:
Uncaught DOMException: Failed to execute 'inverse' on 'SVGMatrix': The matrix is not invertible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants