Search code examples
javascriptfunctional-programmingreactive-programmingcyclejs

How to execute javascript after DOM is loaded in cycle.js


How can I execute javascript after the DOM is loaded in cycle.js? I can't put it in my main() because the DOM is not loaded at that point. For context, I'm basically trying to initialize a fancy autocomplete which requires the target DOM element to exist, please feel free to give pointers if my approach is wrong.

I tried this:

sources.DOM.select(':root')
  .observable.take(1)
  .subscribe((element) => initAutoCompl())

...but select was returning null, I also tried selecting against other elements as well.


Solution

  • One way (using CycleJS with xstream) to execute JS code once after the DOM is ready is to run a (otherwise) non-operative observable. To do that select ':root' then assign a non-existent event, and initiate the stream with startWith. Once the stream is initiated run a map that executes your setup function. To subscribe to the observable send it to a non-operative driver:

    function main (sources) {
    
      // Initiate observable, run setup function
      const noop$ = sources.DOM.select(':root').events('noop')
        .startWith(1).map(noop => runSetup())
    
    ...
    
      return {
        DOM: vdom$,
        noop: noop$
      }
    }
    
    ...
    
    Cycle.run(main, {
      DOM: makeDOMDriver('#app'),
    
      // non-operative driver
      noop: noop$ => { noop$.addListener(
        {next: noop => {}, error: ()=>{}, complete: ()=>{}}
      ) }
    })
    

    Codepen.io Run Setup example


    Another way is to run your setup script in a driver and trigger it with an of observable.

    function main (sources) {
    
      // trigger observable
      const setup$ = xs.of(1)
    
    ...
    
      return {
        DOM: vdom$,
        setup: setup$
      }
    }
    
    Cycle.run(main, {
      DOM: makeDOMDriver('#app'),
    
      // setup driver
      setup: setup$ => { setup$.addListener({next: setup => {
        // setup code goes here
        console.log('run setup: ', document.querySelector('#app').children.length)
      }, error: ()=>{}, complete: ()=>{}}) }
    })
    

    Codepen.io Setup Driver example.


    If user input is needed before creating the DOM element:

    function main (sources) {
    
      const click$ = sources.DOM.select('div#clickme').events('click')
      const vdom$ = click$
        .mapTo(div('#newDomElement',"I'm new."))
        .startWith(div('#clickme','Click me.'))
    
      return {
        DOM: vdom$,
        setup: click$
      }
    }
    
    Cycle.run(main, {
      DOM: makeDOMDriver('#app'),
      setup: setup$ => { setup$.addListener({next: setup => {
        // setup code goes here
        initAutoComplDummy()
      }, error: ()=>{}, complete: ()=>{}}) }
    })
    

    Codepen.io Setup Driver After User Input example