In Part 4 I described the comment backend. This installment sheds a light on the comment front-end.
Comments can be tricky. One lesson I learned early: When your comment form is standard HTML form, it attracts spam like a light bulb attracts moths. So the requirement were:
- The original blog entry should not contain any HTML form. It should be loaded on a button click using JavaScript. Nota bene: this isn't hide/show, but actual manipulation of the DOM
- The dynamic form shall not contain a POST URL, but submission should be in JavaScript - keeps a lot of the scumbags out already
- Submission should be secured with a Captcha
- Some formatting should be allowed. I opted for a Markdown editor with preview capabilities
The first component is the placeholder for the existing comments and the button showing the comment form:
<a name="comments"></a>
{{^commentsclosed}}
<div class="well well-raised" style="text-align : center">
<button class="btn btn-lg btn-info" data-toggle="collapse" data-target="#commentform_{{UNID}}" type="button">
Add your comment... <span class="glyphicon glyphicon-comment"></span>
</button>
</div>
<div id="commentform_{{UNID}}" class="collapse"></div>
{{/commentsclosed}}
<div class="well well-raised">
<h4>Comments</h4>
<ol id="commentList">
{{#comments}}
<li>
{{#gravatarURL}}<img src="{{.}}" class="gravatarimg" /> {{/gravatarURL}} posted by <b>{{author}}</b> on <i>{{createdString}}</i>:
<br /> {{& comment}}
<hr style="clear : both" />
</li> {{/comments}} {{^comments}}
<li id="nocomments">
<h5>No comments yet, be the first to comment</h5>
</li>
{{/comments}}
</ol>
</div>
The second component is the comment form, implemented as mustache template - one of the reasons I picked Mustache: runs on the server and the client in tons of languages
<form title="Comment form for blog discussion" onSubmit="return addComment(this,'{{recaptchaid}}','{{parentId}}')" class="form-vertical well well-raised">
<fieldset>
<legend>Add your comment</legend>
<p>Please note: <b>Comments without a valid and working eMail address will be removed.</b>
<br /> This is my site, so I decide what stays here and what goes.</p>
<div class="control-group" id="commentcontrol">
<label class="control-label" for="Commentor">Name (required, published)</label>
<div class="controls">
<input class="input-xlarge focused" id="Commentor" size="30" accesskey="n" name="nameAuthor" />
</div>
<label class="control-label" for="Email">eMail (required, not published)</label>
<div class="controls">
<input type="eMail" class="input-xlarge focused" id="Email" size="30" accesskey="n" name="txtEmail" placeholder="A working eMail please!" />
</div>
<label class="control-label" for="webSite">URL (optional)</label>
<div class="controls">
<input type="url" class="input-xlarge" id="webSite" size="30" accesskey="n" name="txtWebSite" />
</div>
<div class="controls">
<div id="wmd-panel" class="wmd-panel">
<table style="width : 100%" border="0">
<tr>
<td style="width : 50%; vertical-align : top">
<label class="control-label" for="wmd-input">Your Comment (Use markdown like <a href="//stackoverflow.com/editing-help" target="_blank">Stackoverflow</a>)</label>
<div id="wmd-button-bar"></div>
<textarea class="wmd-input" id="wmd-input" name="Body"></textarea>
</td>
<td style="width : 50%; vertical-align : top">
<label class="control-label">Preview</label>
<div id="wmd-preview" class="wmd-panel wmd-preview"></div>
</td>
</tr>
</table>
</div>
</div>
<div class="controls" id="captchadiv">Captcha here</div>
<div class="form-actions">
<button id="commentsubmit" type="submit" class="btn btn-primary btn-large">Post your comment</button>
</div>
</div>
<div class="alert alert-block" id="alertContainer" style="display : none">One moment please, submitting comment...</div>
</fieldset>
</form>
The whole mechanism gets to work with just a few jQuery JavaScript functions (Vanilla JS would work too, but I had JQuery already for the social buttons, so I reused that) and the respective JS files: