choojs / choo

:steam_locomotive::train: - sturdy 4kb frontend framework
https://choo.io/
MIT License
6.78k stars 595 forks source link

calling DOM methods #236

Closed pekeler closed 8 years ago

pekeler commented 8 years ago

If an element gets rendered by a choo view, and I need to invoke a method on that element in response to an action, what is the proper way for making that call?

For example scrollIntoView(), focus(), normalize(), select(), etc.

In React, I'd use lifecycle methods for this. Would it make sense to add similar hooks to choo?

pekeler commented 8 years ago

Seems like Elm has a similar issue: https://github.com/evancz/elm-architecture-tutorial/issues/49.

yoshuawuyts commented 8 years ago

Sounds like you probably want to do some finite state machinery around this; the onload and onunload hooks might be helpful here https://github.com/shama/bel#lifecycle-events

pekeler commented 8 years ago

Thanks. Bel's hooks look useful for some use cases.

I'm thinking a generic view callback would cover all use cases. Would something like this be terrible:

const MyView(state, prev, send) => html`
  <div>
    ${tallThing1}
    ${tallThing2}
  </div>
`
MyView.domCallback = (state, prev, rootElement) => {
  // called every time after MyView has been called and morphed into the DOM
  // rootElement is the div in this example
  if (state.currentThing !== prev.currentThing) {
    rootElement.childNodes[state.currentThing].scrollIntoView();
  }
}
yoshuawuyts commented 8 years ago

You could do that in a setTimeout(fn, 0) or process.nextTick(fn); DOM updates resolve synchronously afaik, so it should be good

edit: should def double check tho; not 100% sure, but think generally they do hmmmm

pekeler commented 8 years ago

Oh, cool. The timeout callback doesn't give me the parentElement sugar but is good enough. Coming from React, I assumed DOM updates in Choo are also asynchronous.

timwis commented 8 years ago

Note that you'll have to use query selectors in timeouts because once the view is patched onto the dom, there's no guarantee the element created by the view will still exist. Because choo uses morphdom to apply dom updates, if an element already exists in the dom, it simply gets updated rather than replaced. So if your view creates a <div> each time it runs, and you save a reference to that <div> inside your view and refer to it in a setTimeout callback, it will only work on the first render. Because future renders simply patch the existing <div> with the one they created.

Example: http://requirebin.com/?gist=7721c651eb58622669112c9391d9c1b8