May 05

Forum thread collapser using jQuery

Tag: Javascript, Programming, Web, jQueryAbhijeet Maharana @ 5:43 pm

Sometimes when viewing a large forum thread, it would be great if you could highlight all posts by a particular user in that thread. While there might be server side plugins for the various forum software bundles available, the admins may not like the idea of installing them unless requested by a large number of users. I went ahead and wrote a script that does this by manipulating the DOM on the client. Again, such functionality is best implemented as a plugin for the forum software itself. But this is a handy script nevertheless.

It is implemented as a bookmarklet which you can drag to your bookmarks toolbar. When browsing a particular thread, select the username whose posts you want to browse and click the bookmark. It will collapse posts by other users so that posts by the user you are interested in will stand out. Other posts can be expanded/collapsed if needed. If you do not select any username, the bookmarklet will prompt for it. In your user control panel for that forum, you can change the visible posts to the maximum available and then use this to quickly go through the posts that contains answers.

There are many forum hosting software available such as MyBB, vBulletin etc. and each has its own layout of rendering posts. Further, within each, there are themes that modify the post layout. Some display the post metadata to the left while some display it above the post. Browsing through various MyBB based forums, I found that there are mainly 2 types of themes that render post metadata to the left. Both look same. Difference is in the DOM structure. First type uses a big table and individual rows for each post. Second type uses divs and nested tables for each row. A generic script to handle all forum types and variants will require some more work than what I am posting here. This script works with a subset: MyBB based forums with themes of first type.

Here is the bookmarklet: Thread collapser. There will be a slight delay when you run this first time. I adapted the jQuerify bookmarklet by Karl Swedberg to inject jQuery into the DOM and then execute our function when jQuery has been loaded successfully. It loads jQuery from Google CDN and once that is cached locally, you won’t notice the delay. Here is the complete script.

Here is a short description of how it works. I am only posting the body for startScript() which is where all our code resides. We start off by getting hold of the user selection on the page – which is supposed to be the post author. If there is no selection, it prompts for the author.

1
2
3
4
5
6
7
8
var author = getSelectedText();
if(author == '')
        author = prompt('Screen name?');
 
if(author == null || author == '')
        return false;
 
author = author.trim().toLowerCase();

Then we get hold of all the posts on the page and check if there is any post by that particular author. The selectors are fairly straightforward if you revisit the DOM structure of the template type that we are handling. Within each post TR, the first TD holds the post metadata. Post author is within an A element which points to the profile page (member.php with an UID). We iterate through all the post authors and mark a true for each one that matches what we are looking for. After the loop, we check how many true values we have.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var postRows = jQuery('div#container div#content table:first')  // get post table
        .children() // get tbody
        .children() // get trs
        .not('tr:has(td.trow_sep)') // remove post separators
        .not(':odd')    // remove post footer that has links
        .slice(1,-1);   // remove table header and footer
 
var numPostsByAuthor = jQuery('td:first a[href^=member.php]', postRows).map(
        function(){
                if(jQuery(this).text().toLowerCase() == author)
                    return true;
                else
                    return null;
        }
).size();
 
if(numPostsByAuthor == 0)
{
        alert('There are no posts by "' + author + '" on this page.');
        return false;
}

If there are posts by our author, we iterate again – collapsing posts by other authors and attaching click handlers to let the user expand them if needed (like when a post refers to a post above but hasn’t quoted it).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
postRows.each(function(){
        var postAuthor = jQuery('td:first a[href^=member.php]',this).text().toLowerCase();
        if(author != postAuthor)
        {
            var currRow = jQuery(this);
            var nextRow = currRow.next();
 
            currRow.hide();
            var nextClass = nextRow.find('td:first').attr('class');
 
            var nextRowCol1 = jQuery('<td class="' + nextClass + ' smalltext">' + postAuthor + '</td>');
            var nextRowCol2 = jQuery('<td class="' + nextClass + '">[+]</td>');
            nextRow.html('');
            nextRow.append(nextRowCol1, nextRowCol2);
            nextRow.css('cursor','pointer');
 
            nextRow.toggle(function(){
                currRow.show();
                nextRow.css('font-weight','bold');
                nextRowCol2.html('[-]');
            }, function(){
                currRow.hide();
                nextRowCol2.html('[+]');
            });
        }
});

When a post is collapsed, its footer is updated with the post author and a [+] mark next to it. When this post is expanded, the footer is made bold to indicate this has been seen and the [+] turns into a [-]. Here is the output when I tested it on http://www.gfx-depot.com/forum/throw-something-at-the-person-above-you-t-1003.html.

Leave a Reply