Forum "read/unread" in JavaScript I've seen plenty of examples and questions on how to do this, but the issue is that they all rely on a massive database on the server-side. I figured I could try to move that to the client-side. Here's what I have:

(function() {
var data, pos, read, write, fcnt, tcnt, i, forums, threads, l, fid, id, t, allunread, post;
data = {forum:{},thread:{}};
if( localStorage[USERID+'.forumhistory']) {
         data = JSON.parse(localStorage[USERID+'.forumhistory']);
}
switch(FORUM_READ.mode) {
     // in the initialisation, FORUM_READ is an object with:
     //   mode: "index" for the list of forums, "forum" for an individual forum
     //         "thread" for an individual thread.
case "forum":
    threads = document.querySelectorAll('.f_thread');
              // each thread element has a "data-id" attribute indicating the thread's ID
              // and a "data-ts" attribute indicating the timestamp of the most recent post
    l = threads.length;
    fid = FORUM_READ.id;
    for( i=0; i&lt;l; i++) {
        id = threads[i].getAttribute("data-id");
        data.thread[fid] = data.thread[fid] || {};
        data.thread[fid][id] = data.thread[fid][id] || data.forum[fid] || DATE;
        if( threads[i].getAttribute("data-ts")*1000 &gt; data.thread[fid][id]) {
            threads[i].cells[0].children[0].src = "/img/misc/f_unread.gif";
        }
        else delete data.thread[fid][id];
    }
    data.forum[fid] = DATE;
    allunread = true;
    for( i in data.thread[fid]) {
        allunread = false;
        break;
    }
    if( allunread) {
        delete data.thread[fid];
    }
    // Fall through to deal with subforums
case "index":
    forums = document.querySelectorAll('.f_forum');
              // similarly to threads, forums have "data-id" and "data-ts" attributes
    l = forums.length;
    for( i=0; i&lt;l; i++) {
        id = forums[i].getAttribute("data-id");
        data.forum[id] = data.forum[id] || DATE;
        if( forums[i].getAttribute("data-ts")*1000 &gt; data.forum[id])
                           forums[i].cells[0].children[0].src = "/img/misc/f_unread.gif";
    }
    break;
case "thread":
              // the last post, #f_post, has a "data-ts" attribute giving its timestamp
    post = document.getElementById('f_post').getAttribute("data-ts")*1000;
    fid = FORUM_READ.forum;
    id = FORUM_READ.id;
    if( data.thread[fid] &amp;&amp; data.thread[fid][id] &amp;&amp; data.thread[fid][id] &lt; post) {
        data.thread[fid][id] = post;
    }
    break;
}
localStorage[USERID+'.forumhistory'] = JSON.stringify(data);
})();


Now, as far as I can tell this works. However, I haven't really been able to test much due to not having anyone else who can test for me (sometimes working alone sucks...). So I'm hoping that someone here will be able to spot any errors in my logic.
No big issues that I can see, but here are a few tips.



When using for-in loops, add a hasOwnProperty check, like this:

for (var i in obj) {
    if (obj.hasOwnProperty(i) {
        // do stuff
    }
 }


Otherwise the loop will also iterate over properties of Object.prototype.



data can more simply and efficiently be declared like this:

data = localStorage[USERID + '.forumhistory'] ?
    JSON.parse(localStorage[USERID+'.forumhistory']) :
    {forum:{},thread:{}};




The switch statement might be more legibly written as a series of if else-if statements (with nesting), but that's mostly a stylistic choice.



data is, actually, the worst variable name there is. It is impossible to create a less specific name. I would suggest renaming it.



In loops where you iterate over an array, like this:

var i, l = forums.length;
for (i = 0; i &lt; l; i++) {
    // use `forums[i]`
}


If you know every element in forums is truthy (not 0, null, undefined, false, or ""), you can use this loop, which is shorter and more efficient, and in my opinion more legible.

var i, forum;
for (i = 0; forum = forums[i]; i++) {
    // use `forum`
}