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
Reintroduce virtual-dom hooks to allow deterministic DOM access from Javascript #11
Comments
I'm very much in favour of adding lifecycle/hooks to elm-html as it is a hard requirement for our adoption of Elm. A simple example is being able to set the focus of a dom-element after it have been mounted or updated, many more scenarios that require knowledge of updates, addition and removal of dom notes exist. What do you think @evancz ? |
I totally agree, but in the meantime here is a workaround which doesn't involve any nasty timeouts. It assumes that you are calling a function This example queues up the function calls so it can invoke them when the element becomes available, but you could as well call an incoming port in your application or anything like that. You can also cache observers, etc, etc function updateElement({ id }) {
const element = document.getElementById(id);
if (element) {
// Do your stuff
} else {
callWhenIdAdded(document.getElementById("app"), id, () => updateElement.apply(null, arguments));
}
}
function callWhenIdAdded(parent: Element, id: String, fun: Function): void {
const observer = new MutationObserver((mutations) => {
// Get an array of the added ids
const addedIds = mutations.reduce((acc, mutationRecord) => {
const ids = Array
.from(mutationRecord.addedNodes)
.filter(node => node.attributes.getNamedItem('id') !== null)
.map(node => node.attributes.getNamedItem('id').value);
return acc.concat(ids);
}, []);
// If that contains the id we are looking for then invoke the function and remove the observer
if (addedIds.indexOf(id) >= 0) {
observer.disconnect();
fun();
}
});
observer.observe(parent, {
childList: true,
subtree: true
});
} P.S. If this does get called multiple times on the same element I don't think there is any guarantee that the function calls will happen in the same order as they were called, so if that matters for your use case you might need to add some logic. |
I think it makes sense to go more in this direction and that is being tracked at https://github.com/elm-lang/html/issues/53 |
Recent changes
With the change from 0.16 to 0.17,
elm-lang/html
now depends on this repo which is not based onMatt-Esch/virtual-dom
anymore. The current implementation of this virtual DOM does not have support for hooks anymore: https://github.com/Matt-Esch/virtual-dom/blob/master/docs/hooks.mdThe hooks allow us to invoke some Javascript once the VNode has been flushed to the DOM. This would include things like Leaflet markers on a Openstreet map, WYSIWYG editors, or whatever. That's the nice part, it gives us more fine-grained access to the DOM once we decide to go
Native
for a piece of functionality.Issue
Not having a way to know whether a VNode has been flushed to the DOM, causes us to write things like
setTimeout(tryUntilElementVisible(var times = 5), 50)
etc. If it's about form inputs, you could useonFocus
, but that's not meant for non-form stuff. Sometimes you want to do something as soon as the element is visible, like attaching a WYSIWYG editor.Proposal
It would be nice if we could use "something" like hooks from Matt-Esch/virtual-dom. This is where the magic happens. It checks whether the property is a hook, and if so, it calls the prototype method
hook
on it.We could add a method
Native.VirtualDom.hook
that adds a hook property to our VNode. This would allow us to opt into adding functionality once our element is flushed to the DOM, and it helps us to kill the peskysetTimeout
stuff we need to do otherwise.Lets discuss!
The text was updated successfully, but these errors were encountered: