How to Add Nested Comments to Your WordPress Theme
by c.bavota | Posted in Tutorials | 131 comments
When WordPress 2.7 came out, there were a lot of new features, most of which probably passed by under your radar. I know I missed a bunch and thankfully, I am starting to notice how much more powerful WordPress has become. I had someone request making my Magazine Basic theme function with nested comments and that started me on researching just how this new WP feature worked. It ended up being a harder nut to crack than I first thought, but crack it I did (though I probably spend way too much time on it).
There are quite a few steps to making your theme work with nested comments but the easiest thing to do to start off is open up your theme’s comments.php file and replace it with this:
<?php
// Do not delete these lines
if (!empty($_SERVER['SCRIPT_FILENAME']) && 'comments.php' == basename($_SERVER['SCRIPT_FILENAME']))
die ('Please do not load this page directly. Thanks!');
if ( post_password_required() ) { ?>
<p class="nocomments">This post is password protected. Enter the password to view comments.</p>
<?php
return;
}
?>
<!-- You can start editing here. -->
<?php if ( have_comments() ) : ?>
<h3 id="comments"><?php comments_number('No Responses', 'One Response', '% Responses' );?> to “<?php the_title(); ?>”</h3>
<div class="navigation">
<div class="alignleft"><?php previous_comments_link() ?></div>
<div class="alignright"><?php next_comments_link() ?></div>
</div>
<ol class="commentlist">
<?php wp_list_comments(); ?>
</ol>
<div class="navigation">
<div class="alignleft"><?php previous_comments_link() ?></div>
<div class="alignright"><?php next_comments_link() ?></div>
</div>
<?php else : // this is displayed if there are no comments so far ?>
<?php if ('open' == $post->comment_status) : ?>
<!-- If comments are open, but there are no comments. -->
<?php else : // comments are closed ?>
<!-- If comments are closed. -->
<p class="nocomments">Comments are closed.</p>
<?php endif; ?>
<?php endif; ?>
<?php if ('open' == $post->comment_status) : ?>
<div id="respond">
<h3><?php comment_form_title( 'Leave a Reply', 'Leave a Reply to %s' ); ?></h3>
<div class="cancel-comment-reply">
<small><?php cancel_comment_reply_link(); ?></small>
</div>
<?php if ( get_option('comment_registration') && !$user_ID ) : ?>
<p>You must be <a href="<?php echo get_option('siteurl'); ?>/wp-login.php?redirect_to=<?php echo urlencode(get_permalink()); ?>">logged in</a> to post a comment.</p>
<?php else : ?>
<form action="<?php echo get_option('siteurl'); ?>/wp-comments-post.php" method="post" id="commentform">
<?php if ( $user_ID ) : ?>
<p>Logged in as <a href="<?php echo get_option('siteurl'); ?>/wp-admin/profile.php"><?php echo $user_identity; ?></a>. <a href="<?php echo wp_logout_url(get_permalink()); ?>" title="Log out of this account">Log out »</a></p>
<?php else : ?>
<p><input type="text" name="author" id="author" value="<?php echo $comment_author; ?>" size="22" tabindex="1" <?php if ($req) echo "aria-required='true'"; ?> />
<label for="author"><small>Name <?php if ($req) echo "(required)"; ?></small></label></p>
<p><input type="text" name="email" id="email" value="<?php echo $comment_author_email; ?>" size="22" tabindex="2" <?php if ($req) echo "aria-required='true'"; ?> />
<label for="email"><small>Mail (will not be published) <?php if ($req) echo "(required)"; ?></small></label></p>
<p><input type="text" name="url" id="url" value="<?php echo $comment_author_url; ?>" size="22" tabindex="3" />
<label for="url"><small>Website</small></label></p>
<?php endif; ?>
<!--<p><small><strong>XHTML:</strong> You can use these tags: <code><?php echo allowed_tags(); ?></code></small></p>-->
<p><textarea name="comment" id="comment" cols="100%" rows="10" tabindex="4"></textarea></p>
<p><input name="submit" type="submit" id="submit" tabindex="5" value="Submit Comment" />
<?php comment_id_fields(); ?>
</p>
<?php do_action('comment_form', $post->ID); ?>
</form>
<?php endif; // If registration required and not logged in ?>
</div>
<?php endif; // if you delete this the sky will fall on your head ?>
Now, you need to make sure that nested comments are activated on your WordPress install by going to your admin panel => Settings => Discussion and checking “Enable threaded (nested) comments” Set the level to whatever you want. I like using 2 levels.
If you return to a post page on your site, you should now see reply buttons after each post. And if you click reply, you will noticed that not much happens other than “Click here to cancel reply.” appearing above your reply window. It is a little too subtle for me, so luckily there is a great Javascript hook already installed that you can call by just adding this:
<?php if ( is_singular() ) wp_enqueue_script( 'comment-reply' ); ?>
to you header.php file above <?php wp_head(); ?>.
Now when you click reply, the reply box appears directly below the comment you want to reply to. This is a great little feature and it makes your comments section more intuitive for your user.
The only thing left, really, is styling your comments. With WordPress 2.7, one hook creates the whole comment loop, as opposed to before when you had to call the avatar, the comment text, the date and all that jazz. There is a lot happening with <?php wp_list_comments(); ?> and though some might find it awesome that you don’t have to worry about adding tons of code, others might not like the fact that you can’t edit what appears in your comments and where. There is a solution to this that I will show you below, but for now lets talk about CSS.
WP 2.7 nested comments introduce some new classes and ids for you to style as you wish. It is a pretty long list though.
ol.commentlist {}
ol.commentlist li {}
ol.commentlist li.alt {}
ol.commentlist li.bypostauthor {}
ol.commentlist li.byuser {}
ol.commentlist li.comment-author-admin {}
ol.commentlist li.comment {}
ol.commentlist li.comment div.comment-author {}
ol.commentlist li.comment div.vcard {}
ol.commentlist li.comment div.vcard cite.fn {}
ol.commentlist li.comment div.vcard cite.fn a.url {}
ol.commentlist li.comment div.vcard img.avatar {}
ol.commentlist li.comment div.vcard img.avatar-32 {}
ol.commentlist li.comment div.vcard img.photo {}
ol.commentlist li.comment div.vcard span.says {}
ol.commentlist li.comment div.commentmetadata {}
ol.commentlist li.comment div.comment-meta {}
ol.commentlist li.comment div.comment-meta a {}
ol.commentlist li.comment * {} - (p, em, strong, blockquote, ul, ol, etc.)
ol.commentlist li.comment div.reply {}
ol.commentlist li.comment div.reply a {}
ol.commentlist li.comment ul.children {}
ol.commentlist li.comment ul.children li {}
ol.commentlist li.comment ul.children li.alt {}
ol.commentlist li.comment ul.children li.bypostauthor {}
ol.commentlist li.comment ul.children li.byuser {}
ol.commentlist li.comment ul.children li.comment {}
ol.commentlist li.comment ul.children li.comment-author-admin {}
ol.commentlist li.comment ul.children li.depth-2 {}
ol.commentlist li.comment ul.children li.depth-3 {}
ol.commentlist li.comment ul.children li.depth-4 {}
ol.commentlist li.comment ul.children li.depth-5 {}
ol.commentlist li.comment ul.children li.odd {}
ol.commentlist li.even {}
ol.commentlist li.odd {}
ol.commentlist li.parent {}
ol.commentlist li.pingback {}
ol.commentlist li.pingback div.comment-author {}
ol.commentlist li.pingback div.vcard {}
ol.commentlist li.pingback div.vcard cite.fn {}
ol.commentlist li.pingback div.vcard cite.fn a.url {}
ol.commentlist li.pingback div.vcard span.says {}
ol.commentlist li.pingback div.commentmetadata {}
ol.commentlist li.pingback div.comment-meta {}
ol.commentlist li.pingback div.comment-meta a {}
ol.commentlist li.pingback * {} - (p, em, strong, blockquote, ul, ol, etc.)
ol.commentlist li.pingback div.reply {}
ol.commentlist li.pingback div.reply a {}
ol.commentlist li.pingback ul.children {}
ol.commentlist li.pingback ul.children li {}
ol.commentlist li.pingback ul.children li.alt {}
ol.commentlist li.pingback ul.children li.bypostauthor {}
ol.commentlist li.pingback ul.children li.byuser {}
ol.commentlist li.pingback ul.children li.comment {}
ol.commentlist li.pingback ul.children li.comment-author-admin {}
ol.commentlist li.pingback ul.children li.depth-2 {}
ol.commentlist li.pingback ul.children li.depth-3 {}
ol.commentlist li.pingback ul.children li.depth-4 {}
ol.commentlist li.pingback ul.children li.depth-5 {}
ol.commentlist li.pingback ul.children li.odd {}
ol.commentlist li.thread-alt {}
ol.commentlist li.thread-even {}
ol.commentlist li.thread-odd {}The depth actually goes down to level 10 but I stopped at level 5 above. Hopefully you get the idea. There are a lot of elements to style but of course, you don’t have to style them all. For my Magazine Basic theme, I used only the following:
ol.commentlist { list-style:none; margin:0; padding:0; }
ol.commentlist li { border:1px solid #d5d5d5; border-radius:5px; -moz-border-radius:5px; -webkit-border-radius:5px; margin:0 0 10px; padding:5px 7px 5px 64px; position:relative; }
ol.commentlist li.pingback comment-author { padding:0 170px 0 0; }
ol.commentlist li div.vcard { font-weight:bold; font-size: 14px; line-height: 16px; font-family: helvetica,arial,sans-serif; }
ol.commentlist li div.vcard cite.fn { font-style:normal; font-size: 11px; }
ol.commentlist li div.vcard cite.fn a.url { color:#cc0000; text-decoration:none; }
ol.commentlist li div.vcard cite.fn a.url:hover { color:#000; }
ol.commentlist li div.vcard img.avatar { background: #fff; border:1px solid #aaa; padding: 5px; left:7px; position:absolute; top:7px; }
ol.commentlist li div.comment-meta { font-weight:bold; font-size: 10px; line-height: 16px; font-family: helvetica,arial,sans-serif; position:absolute; right:10px; text-align:right; top:5px; }
ol.commentlist li div.comment-meta a { color:#205B87; text-decoration:none; }
ol.commentlist li p { font-weight:normal; font-size: 12px; line-height: 16px; font-family: helvetica,arial,sans-serif; margin:5px 0 12px; }
ol.commentlist li ul { font-weight:normal; font-size: 12px; line-height: 16px; font-family: helvetica,arial,sans-serif; list-style:square; margin:0 0 12px; padding:0; }
ol.commentlist li div.reply { background:#999; border:1px solid #666; border-radius:2px; -moz-border-radius:2px; -webkit-border-radius:2px; color:#fff; font:bold 9px/1 helvetica,arial,sans-serif; padding:6px 5px 4px; text-align:center; width:36px; }
ol.commentlist li div.reply:hover { background:#cc0000; border:1px solid #cc0000; }
ol.commentlist li div.reply a { color:#fff; text-decoration:none; text-transform:uppercase; }
ol.commentlist li ul.children { list-style:none; margin:12px 0 0; text-indent:0; }
ol.commentlist li ul.children li.depth-2 { margin:0 0 3px; }
ol.commentlist li ul.children li.depth-3 { margin:0 0 3px; }
ol.commentlist li ul.children li.depth-4 { margin:0 0 3px; }
ol.commentlist li ul.children li.depth-5 { margin:0 0 3px; }
ol.commentlist ul.children li.odd { background:#fff; }
ol.commentlist ul.children li.even { background:#f6f6f6; }
ol.commentlist li.pingback div.vcard { padding:0 170px 0 0; }
If you have your nested comment level set to 3 you should now have something that looks like this:

It all works great but there are a few things that I wanted to change. They don’t make it that easy to manipulate things but I figured it out and added a few touches of my own to get it to perform how I wanted it to.
To change the size of the avatar you can change line 25 in comments.php to this:
<?php wp_list_comments('avatar_size=48'); ?>
The default avatar size is 32 and you can change it to anything you want.
If you don’t want your comments to be displayed as the default unordered list (ul), you can change the style to divs or an ordered list (ol) by using the following;
<?php wp_list_comments('style=div'); ?>
Replace div with ol or ul but be sure to also change line 24 and 26 which currently is set for ordered lists so that you have something like this:
<div class="commentlist">
<?php wp_list_comments('style=div'); ?>
</div>
If you want to only display comments, trackbacks, pingbacks (trackbacks and pings) or pings, you can use the following:
<?php wp_list_comments('type=comment'); ?>
Now for those people who want total control over their code. This is not recommended to those who are inexperienced with coding. This actually bypasses all of the internal WordPress functionality in regards to comments and lets you customize everything that the wp_list_comments hook spits out. This is a two step process.
First add:
<?php wp_list_comments('callback=mytheme_comment'); ?>
Then go to your functions.php file (or create a functions.php file if you don’t already have one) and add this:
<?php
function mytheme_comment($comment, $args, $depth) {
$GLOBALS['comment'] = $comment; ?>
<li <?php comment_class(); ?> id="li-comment-<?php comment_ID() ?>">
<div id="comment-<?php comment_ID(); ?>">
<div class="comment-author vcard">
<?php echo get_avatar($comment,$size='36',$default='<path_to_url>' ); ?>
<?php printf(__('<cite class="fn">%s</cite> <span class="says">says:</span>'), get_comment_author_link()) ?>
</div>
<?php if ($comment->comment_approved == '0') : ?>
<em><?php _e('Your comment is awaiting moderation.') ?></em>
<br />
<?php endif; ?>
<div class="comment-meta commentmetadata"><a href="<?php echo htmlspecialchars(get_comment_link( $comment->comment_ID )) ?>">
<?php printf(__('%1$s at %2$s'), get_comment_date(),get_comment_time()) ?></a><?php edit_comment_link(__('(Edit)'),' ','') ?></div>
<?php comment_text() ?>
<?php if($args['max_depth']!=$depth) { ?>
<div class="reply">
<?php comment_reply_link(array_merge($args, array('depth' => $depth, 'max_depth' => $args['max_depth']))) ?>
</div>
<?php } ?>
</div>
<?php
}
?>
Here there is a lot that you can do. You can set your avatar size, change your classes and ids, change you awaiting moderation text or pretty much just rearrange the entire layout. I even added a little if statement at the end to only display the reply button if your comments can go down another level (this took a long time to figure out but is totally worth it in my books).
With everything above in place, your WordPress theme should now be fit to work with nested comments and you should be able to control how they are displayed to your hearts content.



Will this work on WordPress 2.6?
Nope. But I think there are a few plugins that add this functionality to WordPress 2.6 and lower. Check out http://wordpress.org/extend/plugins/ and do a search for nested comments or threaded comments.
I liked it very much. Very interesting.
You are missing a question mark on line 12 of the last pieces of code you have listed.
Hey WillM,
I just went in and fixed that. Thanks.
Cascade comments in WP 2.7, that’s what I need to understand now. Thanks for spending your time and saving mine
For me this code does not work unless I disabled these lines
// Do not delete these lines
if (!emptyempty($_SERVER['SCRIPT_FILENAME']) && ‘comments.php’ == basename($_SERVER['SCRIPT_FILENAME']))
die (‘Please do not load this page directly. Thanks!’);
Hmm. Those really look like lines you shouldn’t delete.
Dont` disable it. Just take a look there is a tiny mistake Try (!empty($_SERVER…. instead of !emtyemty
.
LOL @ cbavota
Did the lines “// Do not delete these lines” tip you off?
Anyway, thanks for the topic. Some priceless info, and you’ve saved me oodles of time digging around wordpress.
Thanks again for supplying these tips
I am looking for a clean and ite magazine style theme for wordpress without too many function calls, any suggestions?. thanks in advance
Thanks to your post, I got my comment forum working.
I don’t know if I’m doing something wrong but no matter how much I reply to comments the depth doesn’t change – every comment has depth-1 as a class, everything is just like the regular depth-1 comments. Really weird, can’t find anything about it anywhere also…
This isn’t quite working with my website. Huh.
I think it has something to do with the weird coding of the theme I use. Aeros, one of the older versions.
Thanks for tutorial!!!
Thanks so much for the info — worked like a dream! Folks like you are what make me love WordPress.
I would LOVE to get this working like you have right on this site. Tried what you said exactly (replaced comments.php, enabled nested comments 3 levels deep, replaced the comment ol code in style.css), but to no avail.
Here’s what it looks like. Please help!
will this work on the new 2.8? Thanks.
when you click reply how do you stop the text area and submit button from indenting?
Not too sure what you mean by indenting. Are you referring to the fact that the reply box appears beneath the comment?
When I click reply my input boxes step to the right. When they are at the bottom they don’t do that. I am not sure how to fix it either..
It was just another css issue. Thanks for all the help.
Thanks to your post, I got my comment forum working.
WordPress is for sure a powerful blog engine and has many devellopers who help it grows steadily.
But man, i can’t believe how come many designers love it while they must put together php code with html and a lot of blackbox.
There are many real CMS system who avoid that and really separate html and php. WordPress is still the most renowed and regarding the high number of blog promoting the good practice thing, it’s not so understandable.
Anyway, thanks for your post, it will surely help me put together some nice layout. But damn, that’s so ugly…
Complete Excellence! Thank you so much for this and I didn’t even have to use a plugin for it. Are you willing to share the code/plugin that you use for comment entry? This thing with the preview and such is pretty neat too.
Nevermind, I figured it out, but I decided I didn’t like the editor that much after all. On the other hand, any tips for increasing the avatar size? I tried adjusting the stylesheet, but that made it blurry and over-lapped the comment.
Right… And if I actually read what you posted carefully, I could have figured that out myself. Which I did. Sorry for bugging you again
hi there, i’m using this on my site with 3 threads deep, but when i click on the reply button the text box isn’t changing size – it’s far too wide. I know i’m missing a bit of code to tell it to align properly, but i don’t know what.
here is a link, just click on any reply button to see what i mean: http://pcgage.net/2009/08/31/nvidia-ceo-slams-cpus-again-predicts-570x-increase-in-gpu-performance/comment-page-1/#comment-41
Your site asks me to sign it to post a reply so I can’t see what the issue is.
@Neologan #4105
try:
comment{
width: 500px;
}
Good luck.
The # in front of comment.
The reply does not work that well
Whoops! didn’t think of that. I’ve changed this so comments can be made without registering so you can now see the problem.
thanks.
any luck?
this guide has helped me lots, but you don’t point out how to combine things like the following:
`print(“wp_list_comments(‘callback=mytheme_comment’)
wp_list_comments(‘style=div’)”);`
I want to do both but do not know how to use both!
Thanks for the great tutorial — just what I was looking for.
tancks.
This Post Helped me
Good Time
Hy,
Thanks for the code and the good explanation.
Can you tell me how you styled the WMD bar in the comment area.
I really like the square look instead of the rounded corners.
Thanks again.