Much of the functionality and organization I'm implementing on this blog has been inspired by Jason Santa Maria. On his newly-redesigned blog, he provides a live comment preview that I just love. So, that's what I decided I wanted for A Blog Not Limited.
Unfortunately, the built-in comment preview functionality for ExpressionEngine doesn't even come close.
Quick & Dirty
In a previous article, I mentioned the LivePreview script I discovered on the EE Wiki.
However, as I also mentioned, this script lacked several key features I wanted:
- Unobtrusive
- Comment author's name to preview as a link if they provide a URL entry
- Comment preview to display links if the user provides them in the comment
Enter jQuery
I did a little poking around on the internet and found Karl Swedberg's Really Simple Live Comment Preview, which uses jQuery.
This is an excellent little script and explanation of its implementation.
My Implementation
As a designer with very little understanding of JavaScript, I always appreciate it when a fellow web professional is extremely detailed with explanations.
So, in hopes of helping other similarly-challenged folks out there, the following are the steps I took to 1) get jQuery in place and 2) implement Karl's solution.
You can also skip my lengthy explanation and go directly to the final code.
Loading jQuery
My extremely patient and helpful friend Ian recommended that I use Google's AJAX Libraries API for jQuery (rather than downloading jQuery to my server and referencing it from there).
Apparently, this approach is beneficial for caching purposes. And all it required was the addition of the following to my head
:
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">google.load("jquery", "1.2.6");</script>
Form Markup
Next, I made a few modifications to the default EE comment form markup for accessibility:
- Added
fieldset
- Nested the
form
fields inlabel
tags, rather thanp
- Assigned
id
values to theinput
s. - Added
tabindex
values to theinput
s
Here is the form markup, contained by the required EE comment form tag:
{exp:comment:form weblog="articles"}
<fieldset>
-
<label for="name">Name: <input type="text" name="name" value="{name}" size="50" id="name" tabindex="1" /></label>
-
<label for="email">Email:<input type="text" name="email" id="email" value="{email}" size="50" tabindex="2" /></label>
-
<label for="url">URL: <input type="text" name="url" id="url" value="{url}" size="50" tabindex="3" /></label>
-
<label for="comment">Comment:<textarea name="comment" cols="38" rows="10" id="comment" tabindex="4">{comment}</textarea></label>
-
<input type="submit" name="submit" value="Submit" tabindex="5" />
</fieldset>
{/exp:comment:form}
One More jQuery Consideration
For most events, jQuery requires the $(document).ready()
function in the head
, following the aforementioned jQuery loading scripts.
And due to how I am currently serving my pages, I added the CDATA
tag so my markup would validate.
<script type="text/javascript">
-
// <![CDATA[
-
$(document).ready();
-
// ]]>
</script>
I then added Karl's function to $(document).ready()
. I won't even begin to attempt to detail Karl's function. Just reference his excellent documentation for a full and easy–to–understand explanation.
However, as useful as Karl's solution was, it didn't quite give me everything that I wanted. So, I began hacking away to get closer to my desired live comment preview.
Let the Hacks Begin
I wanted to make the following changes to Karl's function to get it closer to my vision:
- Comment preview markup after the
form
, not within it - Suppress preview of markup (
b
,i
,u
,img
) that I've chosen not to allow in my comments - Preview commenter's name, in addition to comment text
- If the user enters a URL, have the commenter's name preview as a link to that URL
- Unobtrusive
Preview Markup & Location
I created the markup for my comment preview based on what I envisioned and added it to the page after the form
markup:
<div id="preview">
-
<h3>What It Will Look Like:</h3>
-
<p>Commenter says:<p>
-
<p class="liveCommentPreview"></p>
<div>
I then modified Karl's function. His $(this).parent().after()
traversal specified that when focus was on the "Comment" textarea
, the specified preview markup would be inserted after this element's parent.
I changed this to a $(this).replaceWith()
manipulation, so that my live preview would replace the markup I had added after the form:
$('#comment').one('focus',function() {
-
$('#liveCommentPreview').replaceWith('<p id="live-comment-preview"></p>');
});
Fun With Regular Expressions
In order to get the the preview to suppress formatting of markup my comment form won't accept, I added an extremely hacked together regular expression:
$comment = $comment.replace("<i>", "").replace("</i>","").replace("<b>", "").replace("</b>","").replace("<u>", "").replace("</u>","").replace("<img", "img");
Commenter's Name
To display the commenter's name, I added a span
to my comment preview markup:
<p><span id="liveNamePreview">Commenter</span> says:</p>
I also had to modify the id
value for my "Name" input
. Apparently "name" is global property in JavaScript and using it as a variable should be avoided. So, I changed it:
<label for="myName">Name: <input type="text" name="name" value="{name}" size="50" id="myName" tabindex="1" /></label>
Note: Under normal circumstances, I always keep the id
value identical to the name
value in my form
s. However, given the aforementioned issue with JavaScript and the default (and required) EE name
value, the id
and name
values are different for this input
.
Next I added the JavaScript, using Karl's original code as my foundation:
$('#myName').one('blur',function() {
-
$('#liveNamePreview').replaceWith('<span id="live-name-preview"></span>');
});
var $myName = ''; // two single quotes
$('#myName').blur(function() {
-
$myName = $(this).val();
-
$('#live-name-preview').html( $myName );
});
This script specifies that when the "Name" input
loses focus (blur
), the specified markup (span id="live-name-preview"
) will replace the span id="liveNamePreview"
I added to my comment preview markup.
The content entered in the input
will then be written inside the generated span id="live-name-preview"
.
Link It
Now to get the commenter name to appear as a link if a URL is entered … First, I created another script:
var $url = ''; // two single quotes
$('#url').one('blur',function() {
-
$('#live-name-preview').replaceWith('<a id="live-url-preview" href=""></a>')
-
$url = $(this).val();
-
$('#live-url-preview').attr({href : $url});
-
$('#live-url-preview').html( $myName );
});
This script specifies that when the "URL" input
loses focus (blur
), a id="live-url-preview"
will replace the generated span id="live-name-preview"
.
Then, the URL entered in the input
will then be written as the href
value for the generated a id="live-url-preview"
.
Make It Unobtrusive
Lastly, I wanted this script to be unobtrusive, so I removed the comment preview markup from my XHTML and added the following to my script before the live preview functions:
$('#commentForm').after('<div id="preview"><h3>What It Will Look Like:</h3><p><span id="liveNamePreview">Commenter</span> says:</p><p id="liveCommentPreview"></p></div>');
So, if JavaScript is enabled, this script writes the comment preview markup to the page after the form
. If JavaScript is disabled, no comment preview markup is generated and, therefore, the live preview functionality isn't triggered.
The Final Product
I have absolutely no doubt there are more efficient, clean and secure ways of approaching a live comment preview. But this is what I — as a humble designer with little JS knowledge — came up with.
I fully expect to make changes to this script as I continue with the development on this blog, and as I test across browsers. But, for now, I'm pretty satisfied.
JavaScript
If you care to reference this experiment in full, here's the final JavaScript:
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">google.load("jquery", "1.2.6");</script>
<script type="text/javascript">
-
// <![CDATA[
-
$(document).ready(function(){
-
$('#commentForm').after('<div id="preview"><h3>What It Will Look Like:</h3><p><span id="liveNamePreview">Commenter</span> says:</p><p id="liveCommentPreview"></p></div>');
-
$('#myName').one('blur',function() {
-
$('#liveNamePreview').replaceWith('<span id="live-name-preview"></span>');
-
});
-
var $myName = ''; // two single quotes
-
$('#myName').blur(function() {
-
$myName = $(this).val();
-
$('#live-name-preview').html( $myName );
-
});
-
var $url = ''; // two single quotes
-
$('#url').one('blur',function() {
-
$('#live-name-preview').replaceWith('<a id="live-url-preview" href=""></a>')
-
$url = $(this).val();
-
$('#live-url-preview').attr({href : $url});
-
$('#live-url-preview').html( $myName );
-
});
-
$('#comment').one('focus',function() {
-
$('#liveCommentPreview').replaceWith('<p id="live-comment-preview"></p>');
-
});
-
var $comment = ''; // two single quotes
-
$('#comment').keyup(function() {
-
$comment = $(this).val();
-
$comment = $comment.replace(/\n/g, "<br />").replace(/\n\n+/g, '<br /><br />').replace(/(<\/?)script/g,"$1noscript");
-
$comment = $comment.replace("<i>", "").replace("</i>","").replace("<b>", "").replace("</b>","").replace("<u>", "").replace("</u>","").replace("<img", "img");
-
$('#live-comment-preview').html( $comment );
-
});
-
});
-
// ]]>
</script>
Markup
And here is the final form markup:
{exp:comment:form weblog="articles"}
<fieldset>
-
<label for="myName">Name: <input type="text" name="name" value="{name}" size="50" id="myName" tabindex="1" /></label>
-
<label for="email">Email:<input type="text" name="email" id="email" value="{email}" size="50" tabindex="2" /></label>
-
<label for="url">URL: <input type="text" name="url" id="url" value="{url}" size="50" tabindex="3" /></label>
-
<label for="comment">Comment:<textarea name="comment" cols="38" rows="10" id="comment" tabindex="4">{comment}</textarea></label>
-
<input type="submit" name="submit" value="Submit" tabindex="5" />
</fieldset>
{/exp:comment:form}
♥ Share the Love