inputs
inputs copied to clipboard
Allow alternative selector for disposal?
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.

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
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.
@mootari That would require setting subtree: true, which we want to avoid. (Though, I suppose it could be a fallback, or opt-in.)
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
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.
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)
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
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"?