Really Simple Live Comment Preview

Introduction and Caveat

I'm sure that some of you were expecting this entry to discuss traversing the DOM using jQuery methods. After all, I said I would do just that in my last entry. But a couple nights ago, I had the impulse to try a "live comment preview," so I'm going with it. My next entry, then, will be "How to Get Anything You Want - Part 2." I promise.

Please keep in mind as you read through this tutorial the "learning" part of this site's title: I'm learning jQuery just as many of those who will read this entry are. I mention this now because I have the nagging feeling that it was just too easy to create the live preview. Surely I must be doing something wrong. As always, I am counting on the superior knowledge of my readers to point out where my code fails. But enough of the apologies. Let's get started.

Quick Setup

For the live preview I started with the default comment form in WordPress's Kubrick template:

[html]

[/html]

The important part here is the textarea's id="comment". We want to show the contents of that textarea as the user inputs them.

Create the Preview DIV

Now on to the jQuery code. We first need to create a container in which we can inject the comment text. The container could have been a part of the HTML, or it could have been created when the page loaded, but I decided to create it only when the user first focuses on the comment textarea. Since the live preview is only an enhancement, and since it is a purely visual enhancement, I don't see any need to have either the extra markup inserted or the jQuery triggered unless someone is actually going to use it.

Fortunately, jQuery has a "onefocus" .one('focus') event handler, which, as the name makes painfully clear, is executed only once:

Unlike a call to the normal .focus() method, calling .onefocus() causes the bound function to be executed only the first time it is triggered, and never again (unless it is re-bound). The handler is executed only once for each element. Otherwise, the same rules as described in bind() apply.

Using "onefocus" .one('focus'), we can avoid creating multiple live preview DIVs if the user clicks in, then out, then back in the comment textarea.

Note that in jQuery 1.1, the .onefocus() method has been removed. Now you can achieve the same thing with .one('focus', function() { //your code });

Here is what the DIV creation code looks like:

[js]$('#comment').one('focus', function() { $(this).parent().after('
Live Comment Preview
'); });[/js]

So, the first time an element with an ID of "comment" gets the focus, we move up to this element's parent (<p>) and insert a collection of three DIVs after it. I use a containing DIV (#preview-box) and inside it place a heading DIV (.comment-by) and the important live preview DIV (#live-preview).

Note: I initially created the preview box immediately after the textarea, still within the paragraph, but Internet Explorer 6 did not like that one bit. The preview text wouldn't appear, and I instead got the very helpful JavaScript error: "Unknown runtime error." IE didn't seem to mind the preview box outside the paragraph. Go figure.

Make the Preview DIV Work

Now here comes the really tough part (wink, wink). We're going to make everything inside the comment textarea appear inside the "live-preview" DIV as well, as it is being typed. Unlike the textarea, the live preview will be nicely formatted and show the result of tags such as <strong> and <a href="">, not the tags themselves.

As you might have guessed, jQuery has a bunch of "helper" event functions — keyup, keydown, keypress, etc. — for this type of job. I used keyup:

[js]$('#comment').keyup(function() { $('#live-preview').html( $(this).val() ); });[/js]

Inside the event handler for #comment, $(this) refers to #comment.

Iron Out the Wrinkles

Our live preview works pretty well, just the way it is. The only problem occurs when we hit the Enter key: The preview text stays on the same line.

To fix this little problem, we can use a couple regular expressions. We can start by declaring a variable outside the the keyup function, so it is only done once: var $comment = '' (while the "$" isn't necessary before "comment," it helps me remember that the variable will be storing a jQuery object). Then inside the keyup function, we set the variable to be the value of the comment textarea: $comment = $(this).val(). Finally, we account for the line breaks by using two regular expressions. Since a line break in textareas is read as "\n", we need to replace that with the <br /> tag. If there are two or more line breaks in a row, we'll replace them with two <br /> tags. I realize that it's not properly "semantic," but I think we can get away with it since it's just for preview text that will be discarded anyway. Here are the two "replace" lines:

[js]$comment = $comment.replace(/\n/g, "
"); $comment = $comment.replace(/\n\n+/g, '

');[/js] Notice the "/g"? That's a "global" switch to ensure that every occurrence is replaced. (If you want to learn more about regular expressions in JavaScript, tutorials and references abound on the web).

Now we can put $comment — instead of $(this).val() — inside the parentheses of .html().

UPDATE

Based on helpful comments from "seventoes" and Mary, I have combined the two Regular Expression lines and added a third replace() to prevent anyone from using the Live Preview for cross-site scripting or cross-site request forgery: $comment = $comment.replace(/\n/g, "<br />").replace(/\n\n+/g, '<br /><br />').replace(/(\<\/?)script/g,"$1noscript");. The last replace() rewrites "<script" as "<noscript" and "<script" as "</noscript." That'll teach those mean hackers to try to exploit our JavaScript! Thank you, seventoes and Mary!

The Full Code

Here is what the jQuery looks like in its entirety, wrapped inside $(document).ready() (Important: The third replace() in line 8 is showing "&lt;" because of the way my syntax highlighter is parsing things. If you copy and paste the code, you'll need to change that to "<"):

[js]$(document).ready(function() { $('#comment').one('focus',function() { $('#comment').parent().after('
Live Comment Preview
'); }); var $comment = ''; // that's two single quotation-marks at the end $('#comment').keyup(function() { $comment = $(this).val(); $comment = $comment.replace(/\n/g, "
").replace(/\n\n+/g, '

').replace(/(< \/?)script/g,"$1noscript"); $('#live-preview').html( $comment ); }); });[/js]

That's all there is to it. Successfully tested in Firefox 1.5 - 2.0, Safari 2.0.4, and Internet Explorer 6. As I suggested earlier, it almost seems too easy to be real.

To test my implementation of this code yourself, just post a comment to this entry. Unfortunately, I have had to turn off comments for this entry only, because of spammers' targeted attacks. It has become too much of a hassle to sift through the 100 or so spams I get for this entry each day. I'd love to hear from you anyway. Comment moderation is turned on for anyone who has not previously posted an approved comment. I'm happy to approve comments, even if they only say "testing, testing, this is great!" However, if you want to test submitting a comment with the Live Preview, but you don't care if it is actually published, just type "don't publish" or "don't approve" or something to that effect in the comment so I know to delete it. Please feel free to comment on any of the other entries on this site. Thanks!



Responsive Menu
Add more content here...