In this post I'm going to explain how I wrote the following mini CodePen editor in 439 bytes:

  data:text/html;charset=utf-8,<body oninput="document.querySelector('iframe').srcdoc=h.value+'<style>'+c.value+'</style><script>'+j.value+'<\/script>'"><style>body{margin:0}iframe,textarea{width:100%;height:50vh;float:left}textarea{font-family:monospace;font-size:11pt;line-height:1.4;width:33.33%}</style><textarea id=h placeholder=HTML></textarea><textarea id=c placeholder=CSS></textarea><textarea id=j placeholder=JS></textarea><iframe>

If you copy & paste this code into the address bar of a web browser and press enter, instead of loading a web page over http:// or https:// like normal, it will be loading HTML directly. The HTML file it will be loading is the rest of the snippet! The code that lets us do that is this:

  data:text/html;charset=utf-8,

Everything that follows after this is will be a normal HTML document that has been flattened down to a single line. In this case it was short enough I just authored it as a single line like this, but for larger projects you might want to author a version to maintain and edit, and when you go to package it up for a build you minify and remove all the extra spaces and line breaks. I've included a longer version of the snippet at the end of this post.

This file is going to be a super-lightweight CodePen-like HTML, CSS, and JS editor with a live preview on the bottom half of the page. Roughly speaking, the markup I need for this is a body tag, with three textarea elements and one iframe element, so lets add those:

  <body>
  <textarea id=h placeholder=HTML></textarea>
  <textarea id=c placeholder=CSS></textarea>
  <textarea id=j placeholder=JS></textarea>
  <iframe></iframe>
</body>

For the layout, we want the three textarea elements to be half of the page in height, and in three columns. The preview inside our iframe element can just be the bottom half of the screen. We'll add some simple CSS inside a style tag and add that to our body as well:

  body{
  margin: 0;
}
iframe,
textarea{
  width: 100%;
  height: 50vh;
  float: left;
}
textarea{
  font-family: monospace;
  font-size: 11pt;
  line-height: 1.4;
  width: 33.33%;
}

Like I said, super simple styles. All elements are 50vh tall and floated left. The textarea elements end up 33.33% wide, and the iframe is 100% wide so they just naturally make two complete rows. Some simple text styles are added to make the code a little easier to read and write, but we won't get into colors.

By the way, I think great colors for the textarea could be color: #899;and background: #023;

So if we have three textarea elements and a preview window all set up, the only thing left to do is write enough JavaScript to make it so that every time the user types in any of the textareas, it updates the content of our preview iframe with the content of those three elements.

  document.body.oninput = function(){
  document.querySelector('iframe').srcdoc = h.value + '<style>' + c.value + '</style><script>' + j.value + '<\/script>'
}

Let's unpack that a little bit. We want this code to run any time the user types any key in any textarea, so we can accomplish that on body.onkeyup or body.oninput. We run a function that finds our iframe with querySelector(), and replaces its srcdoc with a string we will generate based on the content of the three textarea elements. It should output like this, if all of the textarea elements were empty:

  <style></style><script></script>

That would be the contents of each empty textarea, with the contents of the CSS textarea wrapped in a style tag, and the content of the JS textarea wrapped in a script tag. Suppose we entered Hello World as HTML, body { background: red; } as CSS, and alert(Date.now()) as JS, it would be added to our iframe as:

  Hello World<style>body { background: red; }></style><script>alert(Date.now())</script>

Notice in our JavaScript code above you also must use a backslash \ to escape the slash in the closing script tag in our string where it says …</script>'. This is not necessary when closing our style tag, but for some reason JavaScript doesn't like the </script> without the escaped slash.

Instead of adding the function to our page inside of a script tag though, since body tags can have oninput attributes we can just quote the contents of our JavaScript function inside of <body oninput=""> and it will run on every input event.

So when we add these different parts together, the data URI string that loads HTML, our body tag that contains our JavaScript, a style tag, three textareas for coding and one iframe for previewing our file looks almost done:

  data:text/html;charset=utf-8,<body oninput="document.querySelector('iframe').srcdoc=h.value+'<style>'+c.value+'</style><script>'+j.value+'<\/script>'">
<style>body{margin:0}iframe,textarea{width:100%;height:50vh;float:left}textarea{font-family:monospace;font-size:11pt;line-height:1.4;width:33.33%}</style>
<textarea id=h placeholder=HTML></textarea>
<textarea id=c placeholder=CSS></textarea>
<textarea id=j placeholder=JS></textarea>
<iframe></iframe>
</body>

This looks pretty good, but to make it a little smaller with no consequence we're able to omit the closing </iframe> and </body> tags and it will still work as intended. Here's the final version with all of the pieces in place, with line breaks added for legibility:

  data:text/html;charset=utf-8,<body oninput="document.querySelector('iframe').srcdoc=h.value+'<style>'+c.value+'</style><script>'+j.value+'<\/script>'">
<style>body{margin:0}iframe,textarea{width:100%;height:50vh;float:left}textarea{font-family:monospace;font-size:11pt;line-height:1.4;width:33.33%}</style>
<textarea id=h placeholder=HTML></textarea>
<textarea id=c placeholder=CSS></textarea>
<textarea id=j placeholder=JS></textarea>
<iframe>

And here's a video of writing an editor like this in under two minutes from scratch, so you can see how it all fits together:


Here's the big, fully expanded multi-line version of the editor that includes the code in a more readable multi-line format:

  <body>
  <style>
    body{
      margin: 0;
    }
    iframe,
    textarea{
      width: 100%;
      height: 50vh;
      float: left;
    }
    textarea{
      font-family: monospace;
      font-size: 11pt;
      line-height: 1.4;
      width: 33.33%;
    }
  </style>
  <textarea id="h" placeholder="HTML"></textarea>
  <textarea id="c" placeholder="CSS"></textarea>
  <textarea id="j" placeholder="JS"></textarea>
  <iframe></iframe>
  <script>
    document.body.oninput = function(){
      document.querySelector('iframe').srcdoc
      = h.value+'<style>'+c.value+'</style><script>'+j.value+'<\/script>'
    }
  </script>
</body>