Some days ago, I posted about realizing Ajax enabled comments in Movabletype. Here is how.
Why Ajax comments
When you comment a post on a blog, the page that contains the form you have filled and sent reloads. Instead, using Ajax only part of the page is reloaded.
From the client side, Ajax is substantially a Javascript, more or less complex, that simulates a browser without interface inside the browser you are using. The responses to the requests made by this pseudo-browser are interpreted by the script and translated in live changes to the page you are on.
From the server side, Ajax is… nothing! The pseudo-browser makes standard requests and receives standard responses. You only need to request the right page at the right moment.
But…
My first idea for implementing Ajax on MT was hacking the Perl. The second one was writing a plugin. But reading MT’s code I had a surprise!
From the file Comments.pm of MT library:
# Form a link to the comment my $comment_link; if (!$q->param('static')) { my $url = $app->base . $app->uri; $url .= '?entry_id=' . $q->param('entry_id'); $url .= '&static=0&arch=1' if ($q->param('arch')); $comment_link = $url; } else { my $static = $q->param('static'); if ($static == 1) { # I think what we really want is the individual archive. $comment_link = $entry->permalink;} else { $comment_link = $static . '#' . $comment->id; } }...return $app->redirect($comment_link);
That static parameter, which is almost ignored in Movabletype documentation, is the key of everything: if the request includes a parameter named static with value different from 1, the value of that parameter is used like an URL to redirect the user.
The trick is all here. A little bit of Javascript makes the rest.
The client side
The Javascript is composed by 2 fundamental functions.
function initializeAjax() { var s = false; if (typeof XMLHttpRequest != 'undefined') { s = new XMLHttpRequest(); } else { try { s = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { try { s = new ActiveXObject("Microsoft.XMLHTTP"); } catch (E) { s = false; } } }return s; }
initializeAjax() is the function that returns the hidden browser, which is a software object on which you can operate using a series of functions.
function sendComment(response_page) {connection = initializeAjax(); if (!connection) { return true; }var remember = ""; var forget = "";var fields = document.getElementById("comments_form").elements;fields["post"].setAttribute("disabled", "true"); fields["post"].setAttribute("value", "Processing...");var requestText = ''; requestText += "author="+encodeURIComponent(fields["author"].value); requestText += "&entry_id="+encodeURIComponent(fields["entry_id"].value); requestText += "&email="+encodeURIComponent(fields["email"].value); requestText += "&url="+encodeURIComponent(fields["url"].value); requestText += "&text="+encodeURIComponent(fields["text"].value); requestText += "&static="+encodeURIComponent(response_page); requestText += "&post="+encodeURIComponent('POST'); if (fields["remember"] && fields["remember"].checked==true) requestText += "&remember="+encodeURIComponent(fields["remember"].value); if (fields["forget"] && fields["forget"].checked==true) requestText += "&forget="+encodeURIComponent(fields["forget"].value);connection.open("POST","/cgi-bin/mt/mt-comments.cgi",true); connection.setRequestHeader('Content-Type','application/x-www-form-urlencoded; charset=UTF-8'); connection.setRequestHeader('Content-Length', requestText.length);connection.onreadystatechange = function() { if (connection.readyState == 4) { response = connection.responseText; if ( (connection.status == 200 && response != "invalid") && !response.match(/<html>.*<\/html>/) && !response.match(/<meta[^>]*>/)) { document.getElementById("comments").innerHTML = response; } else { window.alert("Spiacente, ma non e' possibile processare ora il tuo commento. Prova piu' tardi, per favore..."); fields["post"].removeAttribute("disabled"); fields["post"].setAttribute("value", "Post"); } } }connection.send(requestText);return false; }
sendComment() is the function that uses the object returned from initializeAjax() to send request to the server. Once the request is sent, the function waits for the response, that, if regular, is used used to modify the page. sendComment() is called when you send the form, using the onsubmit event of the form element.
The requestText += "&static="+encodeURIComponent(response_page); is the trick: when you send the comment, the XMLHttpRequest object – the hidden browser – is redirected to a specific page (the response_page passed to function sendComment()).
What page?
The response_page is a simple page, generated from MT using an individual template. For example, if you have the posts archived by date using <$MTArchiveDate format="%Y/%m/%d"$>/<$MTEntryTitle dirify="1"$>.html in the Archive File Template, you can create the response_page using a second individual archive with something like <$MTArchiveDate format="%Y/%m/%d"$>/<$MTEntryTitle dirify="1"$>.ajax.html. Being of individual type, this template is rebuild every time a comment is posted.
This template generates the content used to substitute the part of the page interested by Ajax: in the case of comments, the
template could generate files containing the HTML code to use with innerHTML property of a div element (this is what is done here on sistrall.it).
And if there’s no Javascript?
If Javascript is not available for any reason, everything works just like Ajax doesn’t exists: the form is sent from the real browser, the static parameter takes the value 1 from an hidden field and MT redirects the user following the standard way.
Conclusions
That’s all: implementing Ajax enabled comments on MT isn’t difficult at all! The technique here explained is one of the simplest, but more can be done generating, for example, response_pages containing a real XML tree, using the content of different nodes to substitute more elements in the page.

52 comments
Readers left 52 notes on this photograph. Live yours using the form below.
I will be trying this… i’m not an skilled programmer but I will do my best :D
I would like to see how it works when I post.
Nice one
Però.. carino. Molto.
Good work, thanks.
Testin’
testing shmesting
gerg
testing
How do I do this – I don’t understand.
Good work, Would you be able to provide an example of your comment form. I’ve seen another comments/ajax solution and each input field needed an assigned class.
This is pretty awesome though!
Hi, this is very good..
resumé
If I use a word with a unicode character, like “resumé”, the JavaScript is OK with it, so it looks fine in the Comment Preview.
But when I submit it to the backend, the ending “e”, which is supposed to have an aigu accent, ends up getting split from a single multibyte unicode char into two separate latin-1 chars.
this looks good
Hi
good idea
Thank you so much, so useful!
only to test
Testing comments
test
would love to see your comments template snippet as well thanks ;)
the page is still reloading!
test
testing
test
testing again
test
testin
yuyyuyjhguy
Does it work here? Is there a Demo page? Going to try it here.
This is a test
Test test test. Is this thing on?
test
Test
test
yo
Keep up the great work on your blog. Best wishes WaltDe
gtre
test
sistrall.it ? blog Ajax enabled comments for Movabletype explained
sistrall.it ? blog Ajax enabled comments for Movabletype explained
sistrall.it ? blog Ajax enabled comments for Movabletype explained
test
test
test
tesr
test
thank u
test
xdyjvimse vhbpadwq ibueqmtx mciqsy rgnvthe bxqk tjuisn
jntrzhk uqveabjz lvfhbr zxmrakbf rxai ovjrhd ydavnftcl http://www.nsqxh.spaxjflyv.com
amdnalks