Search code examples
jqueryiframeeditorcodemirror

CodeMirror, updating iframe script on keyup


I have a widget with a codemirror editor and an iframe, I am appending a script tag with predefined content.

I am trying to update the script tags contents on keyup, which is working, however it's not actually "updating" the javascript.

To explain: the predefined code has a click event that changes the sample-div background color to blue, and if I edit the code to rather change the color to blue, when I click the sample-div it will change both the background and color to blue.

It's weird because the iframe script tag is getting the correct "text".

Not working in a snippet for some reason so here is the fiddle https://jsfiddle.net/hannacreed/tkp5c017/3/


Solution

  • The javascript "code" is updating but you're disregarding the already delegated events bound to the document by the sample code.

    Any subsequent delegated events on the document will simply "append" to the already bound events. That is why you are seeing previous handlers triggering in addition despite the visible "code" changing.

    Just "reload" the iframe each time you want to update the code. By reload I mean re-create the iframe element. Of course, this can be expensive on keyup so you may also want to look into throttling your reloads.

    See this forked fiddle for a working version.

    Essentially you'll want a function that generates the contents of your iframe...

    function getFrameContents() {
      return `<!doctype html>
    <html>
    <head>
        <style>body { background: #fff; } .sample-div { background: #333; padding: 10px; width: fit-content; height: fit-content; border-radius: 10px; position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%);</style>
        <script>window.$ = parent.$;<\/script>
        <script>${editor.getValue().toString()}<\/script>
    </head>
    <body>
      <div class="wrapper">
          <div class="sample-div"><label>here is some text.</label></div>
      </div>
    </body>
    </html>`;
    }
    

    You also want a function that will create the new iframe in place of the old iframe which replaces the document with the generated content (as per invoking the above function).

    function reloadFrame () {
      var iframe = widget.find('iframe').replaceWith(document.createElement('iframe'));
      iframe = widget.find('iframe');
      var doc = iframe.contents()[0];
      var contents = getFrameContents();
      doc.open();
      doc.writeln(contents);
      doc.close();
    }
    

    Finally, invoking reloadFrame on load and on keyup (consider throttling).

    // load initial frame
    reloadFrame();
    
    // trigger reload on keyup
    $(document).on('keyup', '.CodeMirror', function () {
        reloadFrame();
    });