Threaded Comments

Threaded Comments module for ExpressionEngine 2.x enables comment threads (nested comments, comment on comment) for channel entries.

You can have unlimited depth of comment nesting.

The module will respect all commentings settings you have. It contains all extension hooks that first-party comments module does, so all of your extensions will work.

The syntax of the module has some differences from EE Comment module. Make sure you read these docs.

Installation

Place the directory contained in zip into your /system/expressionengine/third_party directory. Then go to 'Modules' section in your Control Panel and perform installation.

Comment submission form

The form is almost like the form for comments module with some little additions (parent_id, notify_thread)

{exp:threaded_comments:form entry_id="{segment_3}" parent_id="{segment_4}"}
{if error}
<span style="color: red;">{error_text}</span>
{if:else}
{if logged_out}
<p class="input">
<label for="comment-author">Name</label>
<input type="text" name="name" id="comment-author" value="Name" size="22" tabindex="1" />
<small>Required</small>
</p>
<p class="input">
<label for="comment-email">E-mail</label>
<input type="text" name="email" id="comment-email" value="Email" size="22" tabindex="2" />
<small>Required</small>
</p>
{/if}

<p>
<textarea name="comment" id="comment-comment" cols="22" rows="5" tabindex="4">Message</textarea>
</p>
{if logged_out}
<p class="input">
<label for="save_info"><input type="checkbox" id="save_info" name="save_info" value="yes" {save_info} /> Remember my personal information</label>
</p>
{/if}
<p class="input">
<label for="notify_me"><input type="checkbox" id="notify_me" name="notify_me" value="yes" {notify_me} /> Notify me of follow-up comments?</label>
</p>
{if captcha}{captcha}{/if}
<div class="comment-notify-submit">
<p class="comment-submit"><button type="submit" name="submit" value="submit" id="comment-submit" tabindex="5" ><span>Submit</span></button></p>
<div class="clear"></div>
</div>
{/if}
{/exp:threaded_comments:form}

Parameters

While entry_id and url_title are optional, it is strongly recommended that you use them and don't rely on url auto-guessing.

Form fields

It is recommended that you change the value of parent_id dynamically with JavaScript instead of creating new form for each comment reply link.

NOTE: Notification about new replies to comments will work only if you do not moderate your comments. If you have set them to moderate first, the users will receieve notification about all new comments.

Conditional variables

Single variables

Displaying the comments

The syntax of exp:threaded_comments:display tag is a bit different from what you are used to, so make sure you get familiar with it.

{exp:threaded_comments:display entry_id="6"}
<ul>
{comments}
{thread_start}
<ul>
{/thread_start}
<li>
{comment_id}--{comment}--{url_as_author}--{comment_date format="%Y-%m-%d"}
</li>
{thread_end}
</ul>
{/thread_end}
{/comments}
</ul>
{/exp:threaded_comments:display}

See also the alternative nesting method below.

General usage concepts

Any content within exp:threaded_comments:display tag pair will be displayed only once (except what is wrapped into tag pairs, see below). This is the main difference with comments module.
Pagination will be displayed in the place where you insert the tags. If you need it twice, place the tags twice. You don't need {paginate} tag pair.
The 'per page' (limit) parameter for pagination describes how many zero-level comment per page you want to have (i.e. comments that are replies to entry, not to other comments). So the actual number of comments displayed on page can be different. The comments thread will never be splitted between pages.
The actual comments loop should be surrounded with {comments} tag pair. It's content will be repeated as many times as many comments you have. You can add CSS classes to comments of different level by making use of {level} variable. You can also check for its value to use totally different HTML for comments of different level.
To add HTML wrapping when the thread is going a level deeper, use {thread_start} and {thread_end} tag pairs. They should be placed within {comments} loop. All variables available within {comments} are for your use there as well.

Parameters

Global conditionals

Pagination

Variables

Most of variables available in first-party Comment module are available in Threaded Comments as well.

Additionally (outside of {comments} loop) you have:

Comments loop

Surrounded with {comments} tag pair

Conditional variables

Single variables

All variables available within first-party comments module (including date variables) are available here as well. Check out

Additionally available:

Nesting comments

To nest comments, you'll need to use {thread_start} and {thread_end} tag pairs. Anything within those tag pairs will be displayed in the beginning and in the end of 'branch', respectively.
Note that the thread_start & thread_end content will not be wrapped around top-level comments.

Additionally, you can make use of these special variables:

Example: {comments}
{thread_start}<ul class="cmt_lvl_{level}">{/thread_start}
<li>{comment}</li>
{thread_end}</ul>{/thread_end}
{/comments}
</ul>

Alternative nesting method

available since v.2.1

While the method suggested above would work for almost any markup, it will generate invalid html. If you are concerned about that, you should use alternative nesting method. It is making use of 3 tag pairs: {thread_open}, {thread_close} and {thread_container_close}.

{thread_open} tag pair contents will be placed once and should contain the markup that is addded before child comment.

{thread_close} tag pair contents should contain the "closing" markup that is addded after all child comments are displayed. It will be repeated as many times as many child comments you have.

{thread_container_close} tag pair contents will be placed once and should contain the markup to close the root comment container. It will be also use to close comment container if the comment is 'root' and has no nested comments.

Note that there is no {thread_container_open} tag pair, as container opening markup is usually the same and is repeated for each nested comment. If you need it to be different, you can make check for comment level ({if level==0} etc.)

Additionally, you can make use of these special variables (same as above):

Example: {comments}
<li> {comment}
{thread_open}<ul class="cmt_lvl_{level}">{/thread_open}
{thread_close}</li></ul>{/thread_close}
{thread_container_close}</li>{/thread_container_close}
{/comments}
</ul>

Reply-to form using jQuery

It's recommended to use javascript for setting proper parent_id in comment form. Here's sample code that will move and modify the form.

{!-- do not forget to include jQuery library --}
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>

{exp:threaded_comments:display}
<ul>
{comments}
{thread_start}
<ul>
{/thread_start}
<li>
<div class="comment-text">{comment}</div>
<p>{url_as_author} --- <a href="javascript:void(0)" class="reply" rel="{comment_id}">Reply to this comment</a> <a href="javascript:void(0)" class="quote reply" rel="{comment_id}">Quote and reply</a> </p>
</li>
{thread_end}
</ul>
{/thread_end}
{/comments}
</ul>
{/exp:threaded_comments:display}

<p><a href="javascript:void(0)" class="reply" rel="0">Reply to entry</a></p>

{exp:threaded_comments:form}
{if logged_out}
<p> <input type="text" name="name" value="Name" /> </p>
<p> <input type="text" name="email" value="Email" /> </p>
{/if}

<p>
<textarea name="comment" id="comment-comment" cols="22" rows="5" tabindex="4">Message</textarea>
</p>

<p><input type="submit" name="submit" value="submit" /></p>
{/exp:threaded_comments:form}

{!-- this code will set proper parent_id and move the form --}
<style type="text/css">
#comment_form {display: none;}
</style>
<script type="text/javascript">
$(document).ready(function(){
  $('.reply').click(function() {
    $('#comment_form input[name=parent_id]').val($(this).attr('rel'));
    $('#comment_form').insertAfter( // Insert the comment form after...
    $(this)
    .parent() // The containing p tag
    );
    $('#comment_form').show();
  });
  $('.quote').click(function() {
    $('#comment_form textarea[name=comment]').val('[quote]'+
    $(this).parent().parent().find('.comment-text').text()+
    '[/quote]'
    );
  });
});
</script>

You might also want to move the form around, that can also be done with jQuery.

Top of page