Mini CodePen in 439 Bytes
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 becolor: #899;
andbackground: #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>