3

I have a textarea in HTML that I want someone to be able to enter JavaScript code into and that code to run.

Is there any easy way to accomplish this? I want to do this with HTML and JavaScript alone, so no jQuery. Here is some some of my code.

<!DOCTYPE html>
<html>

<head>
  <title>PlayBox</title>
  <style></style>
</head>

<body>
  <div id="codeOutcome"></div>
  <textarea rows="5" cols="60" id="HTML">
<p>Hello</p>
  </textarea>
  <textarea rows="5" cols="60" id="CSS">
body{
  font-family: 'Courier New', Courier, monospace;
}
  </textarea>
  <textarea rows="5" cols="60" id="Javascript">
console.log('hello')
  </textarea>
  <button onclick="codeRun()">Run</button>
</body>
<script>
  function codeRun() {
    document.getElementsByTagName('style')[0].innerHTML = document.getElementById("CSS").value;
    document.getElementById("codeOutcome").innerHTML = document.getElementById("HTML").value
  }
</script>

</html>

The CSS and HTML already work but I do not know how to accomplish the same thing with Javascript.

To those who answered eval: I tried it, but it would breack once I tried to create an onclick function that triggered something in Javascript. It would tell me that pieces of the HTML that I defined were not defined. How would I avoid this? I'm sorry for not telling you this in advance.

Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
FireTiger
  • 79
  • 5
  • I guess you can just `eval` the code but it's not going to be great. I'd suggest looking at services like JSBin and similar. – VLAZ Nov 23 '20 at 22:12
  • "Is there any easy way to accomplish this?" Yes. In addition to eval which VLAZ mentioned you could set it as the contents of a dynamically added script tag. The question you didn't ask is "should I do this?". Answer is no.... – Jared Smith Nov 23 '20 at 22:20
  • @JaredSmith Why not? It's not enabling anything the user couldn't do by opening the console. – Barmar Nov 23 '20 at 22:25
  • 1
    @Barmar hard to judge the use case here. If a user is allowed to evaluate *other* users' code then this opens up a potential problem. Even if it's not that, JSBin/JSFiddle and so on sites don't *just* eval the code but take extra care in doing it *correctly* so the code is representative. They even throw in some extra stuff like infinite loop protections that might get in the way occasionally but are still not just running the code as it is, as otherwise it might leave some code samples rather unusable. – VLAZ Nov 23 '20 at 23:06
  • I've provided a working snippet. See the example below. – Aib Syed Nov 23 '20 at 23:33
  • **What happens if** you enter `textarea {display:none;}` inside the CSS area and you hit RUN? ;) – Roko C. Buljan Nov 24 '20 at 00:31

4 Answers4

3

To make your code editor's Preview area:

  • Avoid eval()!
    Pass instead the JavaScript code string to Function like: new Function(codeString)(); in order to run it.
  • use a more secure context such as a Sandboxed iframe with flags <iframe id="iframe" src="preview.html" sandbox="allow-same-origin allow-scripts allow-modals allow-forms" frameborder="0"></iframe>
  • Given the iframe is an isolated context, the styles you enter within your code editor will not destroy the default styles of the host app. I.e: imagine someone doing: CSS textarea {display: none;}.

Basically you'll need two files: index.html, preview.html (for the iframe) and use PostMessage API to communicate between the index.html host app and the preview.html file:

Since we cannot use postMessage in the sandboxed (as well :)) Stack Overflow snippets environment, here's a quick gif demo:

enter image description here

index.html

Your main application page with the HTML, CSS and JS textareas and an iframe for the Preview area

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Code Editor - App</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
      * { margin:0; box-sizing:border-box; }
      body { padding:10px; }
      iframe, textarea { width:100%; min-height:3em; }
    </style>
</head>
<body>
    
    <textarea id="html">Hello, stack &lt;b&gt;overflow!&lt;/b&gt;</textarea>
    <textarea id="css">body {background: #F48024;}</textarea>
    <textarea id="js">document.body.innerHTML += "&lt;b&gt;World!&lt;/b&gt;";</textarea>

    <iframe id="preview" src="preview.html" sandbox="allow-same-origin allow-scripts allow-modals allow-forms" frameborder="0"></iframe>

    <script>
        const textareas = document.querySelectorAll("#css, #html, #js");
        const preview = document.querySelector("#preview");
        const post = () => textareas.forEach(el => preview.contentWindow.postMessage({
            id: el.id,
            value: el.value
        }, '*')); // Use 'http://example.com' instead of '*' as your targetOrigin

        // Setup Events and Iframe Init
        textareas.forEach(el => el.addEventListener('input', post));
        preview.addEventListener('load', post);
    </script>
</body>
</html>

in the code above, make sure to use your exact domain for targetOrigin instead of * - for additional security.

preview.html

This is the Preview area, actually a brand clean Document which is called into the sandboxed iframe.
Its elements #__app__html, #__app__css and #__app__js will be populated by the strings received from the PostMessage API from the host index.html document:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Preview</title>

    <style id="__app__css"></style>
    <script id="__app__js"></script>
    <script>
        window.addEventListener('message', (evt) => {
            // if (evt.origin !== "http://example.com:8080") return; // Fix and uncomment in production
            document.getElementById(`__app__${evt.data.id}`).innerHTML = evt.data.value;
            if (evt.data.id==='js') new Function(evt.data.value)();
        });
    </script>
</head>

<body id="__app__html"></body>

</html>

Make sure to uncomment the if (evt.origin line for production.

Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
  • I tried this but with no success. I bet a more experienced programmed could have solved the problem but I did some research on iframes and made a program from scratch. I thought you might be interested in my solution, and may show me some faults (if they exist). I used srcdoc instead of using src. I then tied this to the value of the textarea. Does this cause security breaches? – FireTiger Nov 26 '20 at 00:34
  • @FireTiger sure i'm interested in your solution. Drop me a Git link. And PS, I don't understand what was your issue. Create the two files with the exact code above. Run index HTML and there you have it - took me 5 sec. – Roko C. Buljan Nov 26 '20 at 02:14
  • My solution is posted as an answer below. – FireTiger Nov 26 '20 at 19:49
  • Just posted it. – FireTiger Nov 26 '20 at 20:01
1

Here is one way, run the snippet below.

Comments within the code.

//declare var to call text area by id
var textarea = document.getElementById("text");

//declare var to call result by id
var result = document.getElementById("result");

function run() {
//get current value of textarea
var currentJS = result.previousElementSibling.value;
//evaluate the code in textarea
eval(currentJS)
//show result when necessary
result.innerHTML = currentJS;
}
<p>Enter javascript in the area below.</p>
<p><i>Example: alert("hey");</i></p>
<textarea id="text"></textarea>
<div id="result"></div>
<button id="button" onclick="run()">Run</button>
Aib Syed
  • 3,118
  • 2
  • 19
  • 30
1
<!DOCTYPE html>
<html>
    <head>
        <title>PlayBox</title>
        <style>
            iframe {
                width: 100%;
                height: fit-content;
                border: none;
            }
            .code {
                width: 100%;
                height: 300px;
            }
        </style>
    </head>
    <body>
        <iframe id="result" allowfullscreen></iframe>
        <textarea id="code" class="code"> 
<!DOCTYPE html>
    <html>
        <head>
            <title></title>
            <style></style>
        </head>
        <body>
            <script>
            </script>
        </body>
    </html>
         </textarea>
         <p></p>
         <button onclick="run()">Run</button>
         <script>
              function run() {
                   document.getElementById("result").srcdoc = document.getElementById("code").value
                   document.getElementById("result").requestFullscreen()
              }
         </script>
     </body>
</html>

PS. Given that you have a working example, I think that it is safe to assume that the problem is mine, not yours. However, Roko C. Buljan, here is my solution. It will work on any HTML page. The minor difference is that you edit all of the code in one text area, which worked in my case.

FireTiger
  • 79
  • 5
-1

As said here, you can use 'eval' to do it.
Here is example:

<script>    
var doc = "var x=5;var y=6;function mult(a){return a * y;}";
eval(doc);  
document.write("x=" + x + "<br>");
document.write("y=" + y + "<br>");
document.write("mult(x)=" + mult(x) + "<br>");  
</script>
Or Assayag
  • 5,662
  • 13
  • 57
  • 93
  • 1
    Read: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval and https://stackoverflow.com/questions/4537963/what-are-alternatives-to-document-write – Roko C. Buljan Nov 24 '20 at 00:20