Creating the Environment To Write Code and Render in Realtime

After the Introduction, its time to build things! We will basically lay out a basic UI for the playground and learn how to render our code in the sandbox.

Markup and Styling


Woohoo! CSSDeck Embeds helping us learn quick. I have setup some HTML and CSS code already as you can see. On the left there are the HTML, CSS, JS codeboxes with textarea as editors. On the right there is the output pane where the rendering and displaying is going to happen.

We’re using an iframe as a sandbox as they are great for this exact purpose. Rendering everything in an iframe means no conflict with the styles or scripts from the main window. What I mean is that, if we use a plain HTML block element like a div or section instead of an iframe and throw all our code from the code editors into that element, then our styles and scripts for that restricted output will conflict with the ones that we define in and for our main window. Hope that makes sense.

Realtime Rendering

The most important bit now. Taking all the code from the code editors as something is typed and injecting that to the iframe.

Step 1

Attaching a handler to the keyup event of the textarea.

var html_editor = document.querySelector('#html textarea'),
	css_editor = document.querySelector('#css textarea'),
	js_editor = document.querySelector('#js textarea');

var editors = [html_editor, css_editor, js_editor];

// Attaching the onkeyup Event
editors.forEach(function(editor, i, arr) {

	editor.addEventListener('keyup', function() {

		// The function that'll prepare the code and inject
		// into the iframe.
		render();

	}, false);

});

Now, whenever one types into the editors, the event handler is going to fire up, which will execute the render method. Soon, we’ll define this method that’ll do the real work of sending over the code to the iframe.

STEP 2

Preparing a function that can take html, css and javascript code (from code editors) and in turn prepare the entire HTML code that we can simply inject into the iframe.

// Base template
var base_tpl =
	"<!doctype html>\n" +
	"<html>\n\t" +
	"<head>\n\t\t" +
	"<meta charset=\"utf-8\">\n\t\t" +
	"<title>Test</title>\n\n\t\t\n\t" +
	"</head>\n\t" +
	"<body>\n\t\n\t" +
	"</body>\n" +
	"</html>";

var prepareSource = function() {
	var html = html_editor.value,
		css = css_editor.value,
		js = js_editor.value,
		src = '';

	// HTML
	src = base_tpl.replace('</body>', html + '</body>');

	// CSS
	css = '<style>' + css + '</style>';
	src = src.replace('</head>', css + '</head>');

	// Javascript
	js = '<script>' + js + '</script>';
	src = src.replace('</body>', js + '</body>');

	return src;
};

The function prepareSource() gets the contents of the editors and then does some basic string replacements on the base template we stored in base_tpl. This way we form our entire HTML source that we can simply inject into the iframe.

STEP 3

Writing a function that calls prepareSource() and injects the returned code into the iframe. Using doc.open(), doc.write(), doc.close() is a really easy way to do that and it just works great.

var render = function() {
	var source = prepareSource();

	var iframe = document.querySelector('#output iframe'),
		iframe_doc = iframe.contentDocument;

	iframe_doc.open();
	iframe_doc.write(source);
	iframe_doc.close();
};

Some developers consider it a bad practise to use those 3 document methods but I really think its just fine in our context. Using those methods we kind of replace the entire document with a new one which means we go through the entire initialization process which is expensive and slow (by about 30-50ms ?). But it doesn’t matter, modern browser engines are pretty fast and smart. Oh, and those methods are also blocking and synchronous.

Anyway, I have been using this approach for months on CSSDeck without any problems and I am sure quite a few other similar sites do the same.

The other way is to inject the HTML code from the editor into the iframe’s body node, CSS code inside a style element in the iframe’s head node and JS code inside a script element at the end of the iframe’s body node. Some code:

// For HTML
iframe_doc.body.innerHTML = code_for_body;
// or
var old_body = iframe_doc.body;
var new_body = document.createElement('body');
new_body.innerHTML = html_code;
old_body.parentNode.replaceChild(new_body, old_body);

// For CSS
var style = document.createElement('style');
style.innerHTML = css_code;
iframe_doc.head.appendChild(style);

// For JS
var script = document.createElement('script');
script.innerHTML = js_code;
iframe_doc.body.appendChild(script);

This way works fine when you are just dealing with html and css code, but when Javascript comes into action, things might not work well. I remember facing a lot of problems with this approach. For example, G+ and Twitter share buttons wouldn’t load, although FB Like button would load fine. Also faced some problems with Google Map API in the sandbox. Sometimes, there were inconsistent rendering too, that can get a bit frustrating for the end user.

STEP 4

Celebrations! We made it. Now you realize how easy it is ? Final Result.

Extra Tips

  1. You may want to call the render function once on page load just incase you are loading code from some database.
  2. The keyup event gets called on every keypress which is inefficient since we don’t want to render our code on pressing keys like the arrows, ctrl, alt, shift, etc. Precisely, we want to render when there is a code change in our textarea.
    The onchange event is not the proper solution to this problem, because it won’t be triggered until the textarea has lost focus. One solution to this problem is to use the oninput event that has been standardized in the HTML5 specification. This event gets triggered whenever there is a user input detected. Unfortunately, it is not supported in IE6-IE8 but you can get some polyfills. Also IE9 support is a bit buggy as the event doesn’t get fired when the backspace and delete keys are pressed or there is a cut operation.
    The other working solution is to use a global flag (variable) that holds the value of your textarea fields. Then, in the keyup event handler, you can check whether the stored value is same as the editor contents or not. If not, then the code was changed and you need to render and also save the current value into that flag. Here’s a sample code:

    var code = '';
    editor.addEventListener('keyup', function() {
    	// editor contents is not the same as the code
    	// in our flag/storage. This means the code has been
    	// changed.
    	if (code !== this.value) {
    		render();
    		code = this.value;
    	}
    }, false);
    

What’s Next?

In the next part we’ll discuss some of the security problems this method has if we allow others to create on our own platform like CSSDeck does and how to solve those issues.

Share:

One thought on “Creating the Environment To Write Code and Render in Realtime

  1. Bengtegård

    Hi!

    Is there any implications if I wish to only and only use the JavaScript editor for output?
    Not wanting to use HTML/CSS that is!

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>