0

This function works:

function refreshCodeMirror(){
  $("textarea").each(function(){
    var codeMirror = $(this).data('codeMirror');
    setTimeout(function(codeMirror){
      return function () {
        codeMirror.refresh();
      }
    }(codeMirror), 10)

  });
}

But when I tried to simplify it to this:

function refreshCodeMirror(){
  $("textarea").each(function(){
    var codeMirror = $(this).data('codeMirror');
    setTimeout(codeMirror.refresh, 10)

  });
}

The simplification does not work.

Some (possibly irrelevant) context:

The refreshCodeMirror function is being called in the onclick for a bootstrap tab-header within a django template:

   <div class="row">
        <div class="col-md-12">
            <ul class="nav nav-tabs" role="tablist">
                {% for field in form_tab_fields %}
                    <li role="presentation"{% if forloop.first %} class="active"{% endif %}>
                        <a class="tab-header" href="#{{ field.id_for_label }}_tab" data-toggle="tab" onclick="refreshCodeMirror()">{{ field.label_tag }}</a>
                    </li>
                {% endfor %}
            </ul>
            <div class="tab-content">
                {% for field in form_tab_fields %}
                    <div role="tabpanel"
                         class="form-group tab-pane{% if forloop.first %} active{% endif %}"
                         id="{{ field.id_for_label }}_tab">
                            {{ field }}
                    {{ field.errors }}
                    </div>
                {% endfor %}
            </div>
        </div>
    </div>

Everything works fine with the first function above and, while I would like to remove all the redundant refresh calls, they don't seem to matter and when I use a single text area (by passing the element id), the working function above stops working.

Paul Whipp
  • 13,812
  • 4
  • 37
  • 47
  • that is because the timer calls refresh with another "context" (the instance pointed by the "this" keyword in the runtime), so you need to tell JS which instance use in the "this" keyword in the time of the call, example with jQuery: setTimeout($.proxy(cm.refresh, cm), 10) –  May 16 '16 at 20:41
  • See also [How does the “this” keyword work?](http://stackoverflow.com/q/3127429/218196) and [How to access the correct `this` / context inside a callback?](http://stackoverflow.com/q/20279484/218196). – Felix Kling May 16 '16 at 20:48

1 Answers1

6

The problem is you lose the context.

Functions which are not called directly on an object and are not manually bound to a context, are called in the global context.

var obj = {
  print: function() {
    document.write('<pre>' + this + '</pre>');
  }
};

obj.print();
var p = obj.print;
p();

When you use setTimeout or other similar functions, it's like doing this:

function setTimeout(f, time) {
  wait(time);
  f();
}

So your refresh function is expecting this to be equal to your codeMirror instance but instead it's equal to the window or undefined (depending on if you're in strict mode).

There are a few ways to fix this. One is to pass a new function to setTimeout where you call your original function.

setTimeout(function() {
  codeMirror.refresh();
}, 10);

Another way, it to use bind to pass it a copy of the function with this set to the correct object.

setTimeout(codeMirror.refresh.bind(codeMirror), 10);
Mike Cluck
  • 28,921
  • 12
  • 72
  • 85
  • Passing the 'new' function is what I'm doing in the working solution in my question, is it not? The bind seems to be repeating something since the function is already tied to the codeMirror instance but I see from your handy documentation link to bind that this is not the case, thanks! – Paul Whipp May 16 '16 at 23:04
  • The `setTimout(codeMirror.refresh.bind(codeMirror), 10)` solution does not work :( – Paul Whipp May 16 '16 at 23:14
  • @PaulWhipp Then you probably need that original function reference for something. Impossible to say without a better description of the problem. Either way, the first solution will always work. – Mike Cluck May 17 '16 at 00:25
  • Thanks, yes - it seems something in CodeMirror makes that essential. – Paul Whipp May 18 '16 at 19:52