2

I tried to develop a live code editor.

The code gives the error:

Uncaught SyntaxError: Unexpected end of input

Where is the problem or is there any solution?

function compile() {
  let htmlContent = document.getElementById('html');
  let cssContent = document.getElementById('css');
  let jsContent = document.getElementById('js');

  let output = document.getElementById('output');

  output = output.contentWindow || output.contentDocument.document || output.contentDocument;

  let doc = output.document;

  doc.open();
  doc.write(
    `<style>${cssContent.value}</style>
                <body>${htmlContent.value}</body>
                <script>${jsContent.value}</script>`
  );
  doc.close();
}
<textarea id="html" cols="30" rows="10"></textarea>
<textarea id="css" cols="30" rows="10"></textarea>
<textarea id="js" cols="30" rows="10"></textarea>
<button onclick="compile()">Run</button>
<iframe id="output"></iframe>
Manas Khandelwal
  • 3,790
  • 2
  • 11
  • 24
Md. Harun Or Rashid
  • 2,000
  • 4
  • 21
  • 37

2 Answers2

2

Closing the script tag

As pointed out by @Sanmeet in his (deleted) answer, the error is caused by not escaping the backslash in the end script tag: if the html parser decodes a <script> tag, it buffers input up to the next </script> tag and passes it to the JavaScript compiler. In this case it then treated what was left of the script as text an put it in the body element.

Reusing variable and class names

Escaping the end tag to <\/script> reveals another problem. Google Chrome does not re-initialize global variable space when document.open is called (as was correct historical practice). Sadly Firefox has altered its behavior to follow suite and no longer re-initializes variables either.

One possible solution is to create a new iframe element each time the run button is clicked. The example below extends the posted code to create a new iframe. If you declare let, const or class identifiers in the JavaScript box, you can now press the run button repeatedly without changing variable and class names.

function compile() {
  let htmlContent = document.getElementById('html');
  let cssContent = document.getElementById('css');
  let jsContent = document.getElementById('js');

  let output = document.getElementById('output');
  let replacement = document.createElement('iframe');
  replacement.id = "output";
  output.replaceWith( replacement);
  output = replacement;

  output = output.contentWindow || output.contentDocument.document || output.contentDocument;

  let doc = output.document;

  doc.open();
  doc.write(
    `<style>${cssContent.value}</style>
                <body>${htmlContent.value}</body>
                <script>${jsContent.value}<\/script>`
  );
  doc.close();
}
<textarea id="html" cols="30" rows="10"  placeholder="html..."></textarea>
<textarea id="css" cols="30" rows="10" placeholder="css..."></textarea>
<textarea id="js" cols="30" rows="10" placeholder="js..."></textarea>
<button onclick="compile()">Run</button>
<iframe id="output"></iframe>

Code Snippet Error

Executing the snippet generates a security error trying to access doc. It works as intended in a browser.

traktor
  • 17,588
  • 4
  • 32
  • 53
1

If you insert inside a script tag, no matter if it's a string in quotes, apostrophes, or even a template literal, it will always close the script tag. You have to escape it.

 doc.write(
  `<style>${cssContent.value}</style>
   <body>${htmlContent.value}</body>
   <script>${jsContent.value}<\/script>`);

However, if you use external script , it should work fine without escaping.

More discussion here: "Unterminated template literal" syntax error when literal contains script tag

Imtiyaz Shaikh
  • 425
  • 3
  • 7