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

Allow alternative selector for disposal? #89

Open
severo opened this issue Feb 9, 2021 · 8 comments
Open

Allow alternative selector for disposal? #89

severo opened this issue Feb 9, 2021 · 8 comments
Labels
question Further information is requested

Comments

@severo
Copy link
Contributor

severo commented Feb 9, 2021

disposal() is very useful. I wish it could work in the particular case where the element lives in an iframe in an Observable cell (real use case): currently, the Promise resolves instantaneously after the element has been appended to the iframe body.

See https://observablehq.com/@severo/disposal-in-iframe for a demo.

image

@mootari
Copy link
Member

mootari commented Feb 9, 2021

I guess the selector could be made overridable via a second optional parameter? Alternatively it would have to be changed to "html".

const target = element.closest(".observablehq");

@mbostock mbostock added the enhancement New feature or request label Feb 9, 2021
@mbostock mbostock changed the title disposal does not work in iframe Allow alternative selector for disposal? Feb 9, 2021
@mbostock
Copy link
Member

mbostock commented Feb 9, 2021

The disposal promise needs to know the container that may be removed — otherwise it would have to observe the entire DOM, which would be slow. The implementation currently expects an element with the class “observablehq”, which on Observable corresponds to the cell’s container. We could expose a custom selector as an option to the disposal; in the meantime, I recommend you add the observablehq class to your container, or fork the definition of disposal to do what you want.

@mbostock
Copy link
Member

mbostock commented Feb 9, 2021

@mootari That would require setting subtree: true, which we want to avoid. (Though, I suppose it could be a fallback, or opt-in.)

@mootari
Copy link
Member

mootari commented Feb 9, 2021

If we don't need to support IE11, then there's also Node.isConnected.

Edit: Nevermind, I missed that MutationObserver is called on the target element. For what it's worth, here is a related discussion: whatwg/dom#533

@mbostock
Copy link
Member

If we allow you to pass in a selector, it will be error prone because the disposal promise will only be monitoring the selector element’s immediate children (childList) for changes. So for example if you pass the selector "body" to disposal, but the element to be disposed isn’t an immediate child of the body, and the element is removed from its parent, the disposal promise won’t trigger.

@mbostock mbostock added question Further information is requested and removed enhancement New feature or request labels Feb 23, 2021
@severo
Copy link
Contributor Author

severo commented Mar 12, 2021

I really don't know how to do it with the iframe, I couldn't understand how to use the workarounds you mentioned.

To me, invalidating the cell that contains the iframe cannot be detected from inside the iframe, because .closest() will never hit the .observablehq div outside the iframe.

And if I create a "container" div with the class "observablehq" inside the iframe, it prevents the disposal promise to resolve immediately, which is good, but the disposal promise will in fact never resolve because there would be no "addition of new child nodes or removal of existing child nodes" once the iframe cell is invalidated.

Maybe another heuristic could be found, but for now, it seems I will have to pass the iframe cell invalidation promise in every function, and avoid using disposal(). Or don't use iframes (by the way, we don't have the Observable CSS styles inside the iframe, too bad for the Inputs, they don't look as fancy)

@severo
Copy link
Contributor Author

severo commented Mar 12, 2021

I got it working! I finally understood what you meant @mbostock :)

  • Create a div with the class "observablehq" inside the iframe
  • Create a child to this div. It will be the root for all the content
  • Use disposal() inside the content. It will not resolve for now because there is one ".observablehq" element above in the DOM
  • Use disposal() on the iframe to detect when it is invalidated.
  • Once this Promise resolves, ie when the iframe cell is invalidated, remove the content root from the "div.observablehq" children. This will trigger all the mutation observers inside the content, and as they will not be connected anymore to an element with the class "observablehq", disposal() will resolve.

Notebook updated with the solution - https://observablehq.com/@severo/disposal-in-iframe

@severo
Copy link
Contributor Author

severo commented Mar 12, 2021

The purpose of disposal is to detect disconnected nodes, ideally when they are disconnected, not when an observable's cell is invalidated:

The disposal promise is a heuristic for detecting when an input has been removed from the DOM, say to detach synchronized inputs

Would https://github.com/wessberg/connection-observer be a solution? Is it the same as "setting subtree: true which we want to avoid"?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

3 participants