31 January 2012

Troubleshooting Blogger’s threaded comments

Earlier this month introduced a long-awaited feature, native threaded comments – with only one level, but still pretty nice to delimit conversations. The instructions sounded easy enough, just a couple of settings to change and the new reply system should kick in. Unfortunately in practice both myself and many other have stumbled across a couple of problems…

If you have Blogger’s commenting feature enabled, “Blog Feed” set to “Full”, and are using “Embedded” comments, then you’re ready to start a discussion with your readers. To check, or change your feed settings, select: “Settings” > “Other” >, and then “Full” from the “Allow Blog Feed” dropdown.

No ‘reply’ button

The first thing I noticed after switching my comments from a pop-up window to embedded was, well, nothing. Meaning the comment form was unchanged, with no new buttons anywhere. I kind of expected something like this because I made several changes to the template and that can prevent centralized code updates from Blogger to install properly. It turned out that was relatively easy to solve: you could either reset the widget templates as suggested on the help forums or – if you have many customizations that would get lost this way – manually go through the template and update the missing pieces of code as explained on the Blogger Developers Network blog. But after doing that I discovered other issues, like:

‘Reply’ button not working

So I got it to show up, but now nothing happened when clicking on the ‘Reply’ button! It looked like there was a missing script and I spent a considerable amount of time trying to track down the source of this problem. First I tried to replace the blog template with a completely new one, which I thought would have the proper updated code – that turned out not to be the case. I compared the source code of an article on my blog to others where the replies were working correctly and I discovered there was indeed a script missing for me:

<script defer='defer' src='//www.blogblog.com/dynamicviews/acacef8b5f7a566b/js/comments.js' type='text/javascript'></script>

This is incidentally the reason why threaded comments were working if I switched to one of the dynamic templates, but not on the older themes. But that wasn’t a proper solution, because I don’t want to switch to a dynamic template just to have replies for comments.

Finally I set up a test blog just to get access to a correct template – replies were working on a freshly created blog. I compared the two templates and discovered several differences, but none of them related to the comment system. Just as I was about to give up on the whole idea of threaded comments for my blog, I discovered a Google Group dedicated to Blogger developers and I posted about my issues there. Now, a week later, I noticed that the replies are finally working for me and the script above is loaded on pages containing comments. I hope the Blogger team has solved a bug and applied the fix to other blogs as well. So, if you previously had problems with the ‘reply’ function now would be the time to test it again, possibly after applying the solutions from the previous section.

Missing icon for blog author

Another feature that could be missing if you have customized your template manually before and it hasn’t updated automatically is the small icon indicating comments from the blog author. Fortunately it’s relatively easy to fix with some CSS code added to the template. I have copied the code below from my test blog; you should place it in the head section between the <b:skin> and </b:skin> tags, for example right before /* Widgets */:

/* Comments ----------------------------------------------- */
.comments .comments-content .icon.blog-author {
  background-repeat: no-repeat;
  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAAd0SU1FB9sLFwMeCjjhcOMAAAD+SURBVDjLtZSvTgNBEIe/WRRnm3U8RC1neQdsm1zSBIU9VVF1FkUguQQsD9ITmD7ECZIJSE4OZo9stoVjC/zc7ky+zH9hXwVwDpTAWWLrgS3QAe8AZgaAJI5zYAmc8r0G4AHYHQKVwII8PZrZFsBFkeRCABYiMh9BRUhnSkPTNCtVXYXURi1FpBDgArj8QU1eVXUzfnjv7yP7kwu1mYrkWlU33vs1QNu2qU8pwN0UpKoqokjWwCztrMuBhEhmh8bD5UDqur75asbcX0BGUB9/HAMB+r32hznJgXy2v0sGLBcyAJ1EK3LFcbo1s91JeLwAbwGYu7TP/3ZGfnXYPgAVNngtqatUNgAAAABJRU5ErkJggg==); }

.comments .comments-content .loadmore a {
  border-top: 1px solid $(link.hover.color);
  border-bottom: 1px solid $(link.hover.color); }

.comments .continue {
  border-top: 2px solid $(link.hover.color); }
You can also customize the icon by replacing the default picture with a custom URL in the argument background-image: url().

Posts with comments not loading in Internet Explorer

Another bug I noticed ever since turning threaded comments on is that blog posts with comments wouldn't load in Internet Explorer 9 anymore – probably a script conflict again. Now, after the replies are working properly in other browsers I looked into this problem again; here are some of the conclusions:

  • The bug only affects the ‘IE9 standards’ mode, so viewers coming from older versions would see the blog loading correctly;
  • My test blog also loads fine, which prompted another observation and a temporary solution. Since I use CSS pretty extensively to style elements on my blog, I have manually edited the template so that the blog would load in ‘IE9 standards’ mode by default – more specifically I added the following code line to the very first section of the template holding meta information:
    <META content='IE=9,chrome=1' http-equiv='X-UA-Compatible'/>
    If you haven’t made any changes to the IE rendering mode, the Blogger default setting will not cause any issues, as it’s optimized for IE7. Or if you have, remove the line above and replace it with this one:
    <meta content='IE=EmulateIE7' http-equiv='X-UA-Compatible'/>
  • If you prefer to keep the compatibility with IE9, there is another solution I discovered by comparing the template with the test blog: replace the 'defer' argument on the script mentioned above with 'async'. This is probably an IE-related bug, as the two arguments are not that different. In order to do this you will have to edit the HTML template with ‘Expand Widget Templates’ checked; search for 'defer' – there should only be two occurrences – and replace the two with 'async'. The result should look like this:
    <script async='async' src='//www.blogblog.com/dynamicviews/acacef8b5f7a566b/js/comments.js' type='text/javascript'></script>

Now that I’ve covered the problems I’ve encountered, there is one other thing I don’t like about the implementation: the comment text uses the <blockquote> tag instead of the usual paragraphs. On one hand this choice is not semantic, since comments are original pieces of content, not quotes from other works – or they should be at least – on the other it breaks the design for people who added custom CSS styles to blockquotes – such as myself. I reported my views on this on the same group for Blogger developers and hopefully it will be fixed as well.

Update: A couple of weeks later, most of the problems have been ironed out, including the use of <blockquote> and the rendering problems in Internet Explorer I mentioned above. In the case of my blog that meant a regression: the ‘reply’-button stopped functioning again. This time the solution was easier. Comparing the code for threaded comments in my customized template with a fresh template on my test blog I noticed a couple of changes, so I decided to replace the new code in my template. And it worked!

You can find the correct code for the script embedded in Blogger templates below.
<b:includable id='threaded_comment_js' var='post'>
   <script async='async' expr:src='data:post.commentSrc' type='text/javascript'/>
    <script type='text/javascript'>
    (function() {
    var items = <data:post.commentJso/>;
    var msgs = <data:post.commentMsgs/>;
    var config = <data:post.commentConfig/>;
    // <![CDATA[
    var cursor = null;
    if (items && items.length > 0) {
    cursor = parseInt(items[items.length - 1].timestamp) + 1;
    var bodyFromEntry = function(entry) {
    if (entry.gd$extendedProperty) {
    for (var k in entry.gd$extendedProperty) {
    if (entry.gd$extendedProperty[k].name == 'blogger.contentRemoved') {
    return '<span class="deleted-comment">' + entry.content.$t + '</span>';
    return entry.content.$t;
    var parse = function(data) {
    cursor = null;
    var comments = [];
    if (data && data.feed && data.feed.entry) {
    for (var i = 0, entry; entry = data.feed.entry[i]; i++) {
    var comment = {};
    // comment ID, parsed out of the original id format
    var id = /blog-(\d+).post-(\d+)/.exec(entry.id.$t);
    comment.id = id ? id[2] : null;
    comment.body = bodyFromEntry(entry);
    comment.timestamp = Date.parse(entry.published.$t) + '';
    if (entry.author && entry.author.constructor === Array) {
    var auth = entry.author[0];
    if (auth) {
    comment.author = {
    name: (auth.name ? auth.name.$t : undefined),
    profileUrl: (auth.uri ? auth.uri.$t : undefined),
    avatarUrl: (auth.gd$image ? auth.gd$image.src : undefined)
    if (entry.link) {
    if (entry.link[2]) {
    comment.link = comment.permalink = entry.link[2].href;
    if (entry.link[3]) {
    var pid = /.*comments\/default\/(\d+)\?.*/.exec(entry.link[3].href);
    if (pid && pid[1]) {
    comment.parentId = pid[1];
    comment.deleteclass = 'item-control blog-admin';
    if (entry.gd$extendedProperty) {
    for (var k in entry.gd$extendedProperty) {
    if (entry.gd$extendedProperty[k].name == 'blogger.itemClass') {
    comment.deleteclass += ' ' + entry.gd$extendedProperty[k].value;
    return comments;
    var paginator = function(callback) {
    if (hasMore()) {
    var url = config.feed + '?alt=json&v=2&orderby=published&reverse=false&max-results=50';
    if (cursor) {
    url += '&published-min=' + new Date(cursor).toISOString();
    window.bloggercomments = function(data) {
    var parsed = parse(data);
    cursor = parsed.length < 50 ? null
    : parseInt(parsed[parsed.length - 1].timestamp) + 1
    window.bloggercomments = null;
    url += '&callback=bloggercomments';
    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = url;
    var hasMore = function() {
    return !!cursor;
    var getMeta = function(key, comment) {
    if ('iswriter' == key) {
    var matches = !!comment.author
    && comment.author.name == config.authorName
    && comment.author.profileUrl == config.authorUrl;
    return matches ? 'true' : '';
    } else if ('deletelink' == key) {
    return config.baseUri + '/delete-comment.g?blogID='
    + config.blogId + '&postID=' + comment.id;
    } else if ('deleteclass' == key) {
    return comment.deleteclass;
    return '';
    var replybox = null;
    var replyUrlParts = null;
    var replyParent = undefined;
    var onReply = function(commentId, domId) {
    if (replybox == null) {
    // lazily cache replybox, and adjust to suit this style:
    replybox = document.getElementById('comment-editor');
    if (replybox != null) {
    replybox.height = '250px';
    replybox.style.display = 'block';
    replyUrlParts = replybox.src.split('#');
    if (replybox && (commentId !== replyParent)) {
    document.getElementById(domId).insertBefore(replybox, null);
    replybox.src = replyUrlParts[0]
    + (commentId ? '&parentID=' + commentId : '')
    + '#' + replyUrlParts[1];
    replyParent = commentId;
    var hash = (window.location.hash || '#').substring(1);
    var startThread, targetComment;
    if (/^comment-form_/.test(hash)) {
    startThread = hash.substring('comment-form_'.length);
    } else if (/^c[0-9]+$/.test(hash)) {
    targetComment = hash.substring(1);
    // Configure commenting API:
    var configJso = {
    'maxDepth': config.maxThreadDepth
    var provider = {
    'id': config.postId,
    'data': items,
    'loadNext': paginator,
    'hasMore': hasMore,
    'getMeta': getMeta,
    'onReply': onReply,
    'rendered': true,
    'initComment': targetComment,
    'initReplyThread': startThread,
    'config': configJso,
    'messages': msgs
    var render = function() {
    if (window.goog && window.goog.comments) {
    var holder = document.getElementById('comment-holder');
    window.goog.comments.render(holder, provider);
    // render now, or queue to render when library loads:
    if (window.goog && window.goog.comments) {
    } else {
    window.goog = window.goog || {};
    window.goog.comments = window.goog.comments || {};
    window.goog.comments.loadQueue = window.goog.comments.loadQueue || [];
   // ]]>


  1. Thanks. Helped me tons!! I'll marry u. LOL.

  2. Hi George,
    I've checked many blogs and website for a solution for fixing 'Reply' button. But your blog is the only one that solved the defect. Thanks a lot. God bless you..! Hope you'll visit my blog to have a look..!

    1. I'm glad it worked for you! As you might have guessed from the length of the article, it certainly wasn't easy...

  3. My reply button is not working on my blog, it's stressing me out! When i click reply nothing happens.. I have to post a new comment to reply to the person. Any suggestions?

    1. Hi Sandra,

      Can you share your blog address, all I see is your Google+ profile.


    2. Its http://www.beauasisbound.com/


    3. Hmm, I can't find anything wrong with the comments script on your blog, in the page source everything looks the same as on my blog.
      The problem could have something to do with the fact that you are hosting the blog on your own domain, some security settings prohibit loading scripts from other domains.

  4. I have the same problem. where I put the script that was missing? help me please. my blog is http://www.lavrense.blogspot.com

    1. Hi savio,

      the address in your comment redirects to www.olavrense.com.br and I don't see Blogger comments on that site.

    2. I also have the same problem too, please help me with my website. www.readtocook.blogspot.com or www.readtocook.com. I can't see my comment and others comment on the blog just only show number of comment and comment form.

  5. Thanks for sharing your thoughts in this post