In-Page Previews

Sometimes the simplest technology can have the biggest impact. Of all the changes I've ever made at any of my sites, the one that people liked the best was also one of the simplest to implement: live preview.

Live preview echoes what a user writes as she types. It allows the user to see how the writing looks in the context in which it will be published, rather than within little windows in small forms. It does so before she submits the new or modified material. It's also quite easy to implement a nonscript and/or accessible alternative: you provide a preview page where a user can review the writing before it's permanently submitted. You can even provide both: live preview and a Preview button.

Where is live preview most useful? Anytime you ask for commentary from the web page reader. This includes email messages to be sent to customer service, comments on weblogs, feedback in product reviews—anyplace a user is writing more than a few words.

Live preview consists of listening for any activity in a form field, capturing the keys pressed, and echoing them to another page element. How you echo depends on how active you want the preview to be.

You can echo the letters as they're typed for a true "live" experience. You can also provide a Preview button, but rather than opening up a new page, it will display the comment directly in the page. This approach doesn't capture the keypress, and the data from the comment field is accessed only when the Preview button is activated.

Both approaches have good points and bad, which I'll get into as we look at each individual approach.

Live Echo Preview

A live, echoed preview is relatively simple to set up. You begin by capturing the keyup event in either a form textarea or input element, and take all of the text and reflect it in an associated div element through the innerHTML property.

In the code, you do need to adjust the text, replacing the hard carriage returns with a break element, br. This keeps the text more or less in sync with the input.

Example 4-9 includes a complete application that uses live preview. Since the example is so small, the stylesheet and script are both included within the web page. The previewed text isn't modified other than transforming the carriage break into a br. If you want to modify the text preview in other ways, such as stripping HTML tags to reflect what the comment will look like published, you'll need to adjust the script accordingly. Remember, though, to keep the preview simple—the function to live preview the input is called whenever a keyup event happens in the form element.

Example 4-9. Simple live preview

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Live Preview</title>
<style type="text/css">
#preview
{
    background-color: #ccc;
    margin: 20px;
    padding: 10px;
    width: 500px;
}
input
{
    margin-right: 10px;
}
</style>
<script type="text/javascript" src="addingajax.js">
</script>
<script type="text/javascript">
//<![CDATA[

manageEvent(window,"load",function(  ) {
            aaManageEvent(document.getElementById('comment'),"keyup",echoPreview)});

// echo the keypresses
function echoPreview(evnt) {
   var commentText = document.getElementById("comment").value;
   modText = commentText.split(/\n/).join("<br />");
   var previewElem = document.getElementById("preview");
   previewElem.innerHTML = modText;
}
//]]>
</script>
</head>
<body>
<form action="preview.htm" method="post">
<div>
<textarea id="comment" cols=50 rows=10></textarea><br />
<input type="button" value="Preview" />
<input type="submit" value="Save" />
</div>
</form>

<div id="preview">
</div>
</body>
</html>

Whatever is typed into the textarea is reflected in the preview element.

The form also has Preview and Save buttons, both of which go to the preview.php page. From there, a user can save the comment or click the Back button to return to the form and continue editing. Neither requires script. Later on in this chapter, we'll see how we can submit the form and display the new comment, all without leaving the page.

There's one major problem with live preview, and that's what happens when the page is served with an XHTML MIME type. The way that browsers implement innerHTML differs among applications, though most do support some version of innerHTML. However, what happens during live preview if you begin to add an XHTML tag differs dramatically between browsers. In Firefox 2.x and up, the incomplete tag throws dozens of errors until it's completed, all of them saying the text is not well-formed XML. In Opera, however, the browser treats the incomplete tag as escaped content until the tag is closed, in which case, it then treats it as XHTML.

Tip

A workaround is to escape HTML characters, <, >, and &, replacing the modText generation with the following:

 modText = commentText.replace(/&/g, '&amp;').replace(/>/g,
> '&gt;').replace(/</g, '&lt;').split(/\n/).join("<br/>");

This disables the links and other HTML. In nontrusted situations, you may want to consider completely stripping out all HTML.

The differences in behavior between browsers is significant enough that you'll probably want to use live preview only with pages served as HTML, at least until the new HTML 5.0 standard comes out, which should clarify how a document fragment is managed. In the meantime, if you want XHTML and an in-page preview, I'd suggest you use the "Ajax" version of comment preview.

Tip

There are many implementations of live preview online, but I first saw it via a Wordpress plug-in created by Chris Davis at http://www.chrisjdavis.org.

Ajax Preview

The Ajax preview version of live preview doesn't echo the preview text as it occurs; rather, it takes all of the text when a Preview button is clicked, but instead of bringing up a separate preview page, the text is echoed in the preview area.

The advantage to this method is that the application isn't as CPU-intensive, since it doesn't have to catch all of the keyup events. It's also much friendlier from an XHTML perspective since the input can be formatted for proper XHTML handling before it's displayed. Live preview is just that: live. No real formatting can be done other than to strip out markup.

The page demonstrating Ajax preview is identical to that shown in Example 4-9 except for a few elements in the script and the addition of an identifier on the Preview button. Example 4-10 shows the page with these modifications highlighted.

Example 4-10. Using Ajax preview on comments

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Ajax Preview</title>
<style type="text/css">
#preview
{
        background-color: #ffc;
        margin: 20px;
        padding: 10px;
        width: 500px;
}
input
{
        margin-right: 5px;
}
form
{
        margin: 10px;
        width: 550px;
}
</style>
<script type="text/javascript" src="addingajax.js">
</script>
<script type="text/javascript">
//<![CDATA[

aaManageEvent(window,"load",function(  ) {
     aaManageEvent(document.getElementById('previewbutton'),'click',showPreview)});

// echo keypress
function showPreview(evnt) {

   // cancel button's default click behavior
   evnt = evnt ? evnt : window.event;
   aaCancelEvent(evnt);

   // add preview

   var commentText = document.getElementById("comment").value;
   modText = commentText.split(/\n/).join("<br />");
   var previewElem = document.getElementById("preview");
   previewElem.innerHTML = modText;
}
//]]>
</script>
</head>
<body>
<form action="preview.php" method="post">
<fieldset>
<legend>Comment</legend>
<label for="comment">Comment:</label><br />
<textarea id="comment" name="comment" cols="50" rows="10"></textarea><br />
<input type="submit" name="button" value="Preview" id="previewbutton" />
<input type="submit" name="button" value="Save" /> // Should these buttons not
have different values for name? //
</fieldset>
</form>
<div id="preview">
</div>
</body>
</html>

The event that triggers the preview now is a click of the Preview button. To prevent the default behavior, submitting the form, from occurring, the event is terminated within the event handler function.

With Ajax preview, embedding XHTML elements in the comment won't trigger an error while the text is in an incomplete state like it does with Firefox 2.x (at least, at the time of this writing).

However, if you put in "bad" XHTML and you're using a browser that checks the content with innerHTML, you will get JavaScript errors when you do assign the text to the innerHTML element. Throwing an error isn't a bad thing, but throwing a JavaScript error is.

A better approach would be to place the code setting the innerHTML element within a try...catch block and provide an error message meant for the web page reader, not the developer:

   modText = commentText.split(/\n/).join("<br />");
   var previewElem = document.getElementById("preview");
   try {
      previewElem.innerHTML = modText;
   } catch(err) {
      previewElem.innerHTML = "<p>An error occurred, please check your
comment for (X)HTML errors.</p>";
   }

Opera will just correct (or tolerate) the bad markup, as will Safari, IE, and OmniWeb, but Firefox and WebKit both trigger the error handling. Unfortunately, there's no way to pinpoint the error—not unless we want to attempt to create an XML document and then see what happens—a rather drastic approach, and one that goes way beyond the usefulness of this effect. A better technique is to avoid all errors by stripping out the markup and providing buttons to allow formatting of hypertext links and such.

Tip

Again, if you don't want the risk of XHTML in the comments, either escape the HTML characters or strip tags out altogether.

Another nice effect you can use with commentary and the like, especially if all comments are listed in the page, is using Ajax to update the data store and then "refreshing" the list without having to refresh the page. Throw in a color fade, and you've got a nice bit of polish without too much code.

Get Adding Ajax now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.