0

Try to use CodeMirror in my Elm app.

I bind a textarea from update function like that:

( ..., runCodemirror textAreaId)

Where runCodemirror is a port:

port runCodemirror : String -> Cmd msg

Problem is that event ports.runCodemirror fires before a textarea appears in DOM.

I try to solve that with setTimeout:

app.ports.runCodemirror.subscribe(
  function (textAreaId) {
    setTimeout(
      function() {
        CodeMirror.fromTextArea(document.getElementById(textAreaId));
      },
      100
    );
  }
);

but it's ugly. 100ms is too slow, I see a blinking.

Other options I have: to bind CodeMirror with invisible textarea or MutationObserver API.

Is there a better way?

ztsu
  • 25
  • 7

2 Answers2

1

You could use DOM mutation observers to monitor changes in the DOM and create the JS objects when those events fire, that way you don't need to use ports at all.

ArriveJS provides a nice wrapper around this functionality so you could do something like:

document.arrive(".code-mirror", function() {
  CodeMirror.fromTextArea(this)
})

You can take this further by adding data- attributes to the element in Elm and reading them on the JS side:

document.arrive(".code-mirror", function() {
  const lineNumbers = this.getAttribute('data-line-numbers') 
  CodeMirror.fromTextArea(this, {
    lineNumbers: lineNumbers
  })
})
antfx
  • 3,205
  • 3
  • 35
  • 45
0

If you're open to abusing json decoders, you can use them to run code when an element first appears in the DOM. I wrote about this approach here: https://medium.com/@prozacchiwawa/the-i-m-stupid-elm-language-nugget-7-8d3efd525e3e#.3hatcdfl3 . Otherwise, MutationObserver is the way to go.

Art Yerkes
  • 1,274
  • 9
  • 9