inputs icon indicating copy to clipboard operation
inputs copied to clipboard

Allow alternative selector for disposal?

Open severo opened this issue 4 years ago • 8 comments

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

severo avatar Feb 09 '21 22:02 severo

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

https://github.com/observablehq/inputs/blob/cc5f9d12f9d6e7ca7849942424b145695bc9de85/src/disposal.js#L4

mootari avatar Feb 09 '21 22:02 mootari

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 avatar Feb 09 '21 22:02 mbostock

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

mbostock avatar Feb 09 '21 22:02 mbostock

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: https://github.com/whatwg/dom/issues/533

mootari avatar Feb 09 '21 22:02 mootari

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 avatar Feb 23 '21 05:02 mbostock

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 avatar Mar 12 '21 00:03 severo

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 avatar Mar 12 '21 09:03 severo

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"?

severo avatar Mar 12 '21 12:03 severo