Browser Extension: Arrange Tumblr Posts in Chronological Order

Creating little browser extensions is a great way to learn JavaScript. You don't need to worry about cross-browser compatibility, you can make the project as small or large as you like, and you can fix problems in other people's websites that bug you, specifically. In this post, we'll create a short script that automatically rearranges Tumblr archive pages to read in chronological order - and we'll learn about NodeList on the way.

The Problem

This week, Ryan North started a new Tumblr, in which he's going through the novelization of the first Back to the Future movie, one page at a time. This is a thing I wish to read.

Click to read.

However, Tumblr displays its posts in reverse chronological order - that is, with the most recent posts first. This makes sense for a lot of sites on Tumblr, but in this instance, I really want to read them in the order they were written. The current site layout means that, to do that, I have to scroll down to the top of the first (first written) post, scroll down as I read it, and then scroll up to the top of the second post, and scroll down through that...

This is a tiny, tiny first world problem - totally trivial and almost embarrassing to mention. But that doesn't mean I don't want to fix it.

By the end of this tutorial, you'll make this Tumblr - or any Tumblr you choose - display its posts in chronological order on the page, automatically.

How Can We Do This?

We don't have control over the BtotheF site design; it offers no options to us, so we can't change the order in which the posts are displayed on the page server-side.

But once the HTML is sent to the browser, it's ours. We can manipulate it however we want. And, of course, the language with which we can do this is JavaScript.

I'll give a quick demo. I'll be using Chrome, and recommend that you do, too - if you can't, or don't want to, then grab a copy of Firebug.

This will take the HTML document's <body> element, and append a new text node - a piece of text, basically - containing the word "Hi". If you didn't follow any of that, get up to speed with HTML.

To see the result, scroll right down to the bottom of the Tumblr:

Slightly more impressive, since we've actually changed the contents of the page. The new text node is right at the bottom because we've used appendChild() - which adds the specified node after everything else inside the <body>. As far as our browser is concerned, the last few lines of HTML now look like this:

<!-- END TUMBLR CODE -->
Hi
</body>
</html>

So! Now we have two issues:

How do we use JavaScript to rearrange all the posts in the page?

How do we make this happen automatically, without having to mess around in the console every time we read the Tumblr?

We'll address these in order.

Rearranging the Posts

First, let's figure out how to "find" all of the posts in the HTML. In Chrome, right-click somewhere in the top post, and select "Inspect element". (Firebug should add the same shortcut to Firefox; for other browsers, try the magnifying glass icon.)

The Elements tab of the Developer Tools will appear, with the element you selected highlighted:

As you hover over different elements in this HTML view, you'll see them highlighted in the page itself. Eventually, you'll find that the entire post is contained in a div with a class of post. As you can see from the screenshot above, there are several other such div elements, and they are all contained in a master div with an id of contents.

We can select all of these post elements at once with a single line of code; type this into the JavaScript console:

document.getElementById("contents").getElementsByClassName("post");

(Technically, we could have just written document.getElementsByClassName("post"), but who cares?)

You'll see - again, in Chrome - some feedback:

Great! This indicates that it worked. If you click the arrows, you'll see the contents of each div.

We might as well assign this to a variable, to save us having to type it out over and over:

(This time, the console will return undefined; you can check that it assigned the variable correctly by just typing the word posts in the console.)

We can loop through all of the elements in posts as if it were an array:

for (var i=0; i < posts.length; i++) {
console.log(posts[i]);
}

Hit Shift-Enter to add a new line to your code without running it, in the JavaScript console. console.log() just traces its contents to the console, which is far less annoying than an alert() and far less disruptive than adding text to the HTML DOM.

The result of this simple loop will just be a list of all the div elements. Cool. So presumably we can do something like this...

A NodeList Is Not an Array

When we use getElementsByClassName(), we don't retrieve a simple array of elements, we retrieve a NodeList. Like an array, this is a collection of elements; unlike an array, the collection is updated in real time. If the HTML DOM is modified, the contents of posts gets modified to match.

For example, let's create a new div with a class of post and append it to the contents div:

Run that, then type posts and hit Enter. You'll see an extra div in the list - even though we haven't touched the posts variable.

Since a NodeList is not an array, it doesn't have most of the methods and properties of Array, such as push() In fact, we've already used the only property: length. The only method is .item(n), which just gets the nth item in the list.

However, our earlier demonstration with appendChild() does suggest an alternative solution: what if, instead of trying to rearrange the elements of the NodeList within the NodeList, we rearranged them within the page?

First, we'll run through the list in reverse order, and append each element in turn to the contents div; then, we'll remove the original posts.

Refresh the Tumblr page to clear out all the extra rubbish we've added to the DOM, then do the first step, of adding the elements in reverse order:

The (chronological) first post is already at the top, and the last post is at the bottom, with no posts repeated. This may seem surprising, but it makes sense: we didn't clone any elements of the NodeList, we just appended them to the contents div, in reverse order. Since the NodeList always reflects the contents of the HTML DOM, we just moved each of the 11 elements to a new position - the 11th element got added to the end (no change in position); then the 10th got moved to be after that; the 9th after that, and so on, until the 1st element was right at the end.

So, problem solved, in five simple lines of code. Now let's automate it.

But we're not going to use any of those. Instead, we'll use a great add-on called Greasemonkey, which allows you to install little JS scripts without needing to package them up as full-blown browser extensions. Essentially, it injects snippets of JavaScript into the page, once it's loaded.

Writing the Greasemonkey Script

Create a new text file, and name it ChronologicalTumblr.user.js. Make sure you remove the default .txt extension, and that you use .user.js, rather than just .js - this is the extension for a Greasemonkey script.

Now, we need to add some extra metadata at the top of the script - details about the script itself. All these details should go in comments, starting with ==UserScript== and ending with ==/UserScript==:

Then, we'll specify a namespace. The combination of the name and the namespace is what uniquely identifies the script; if you try to install another Greasemonkey script with the same name and namespace as an existing script, it will overwrite the old one - if only the name or the namespace match, it'll just install the new one alongside the old.

It's common to use a URL here, so I'll use Activetuts+'s. You should use your own, since you're in control of it:

Now we need to specify which URLs this script should affect - that is, on which web pages the JavaScript should automatically be injected into. I want this script to work on the BtotheF homepage, plus the other archive pages, like http://btothef.tumblr.com/page/2:

In this case, it might be a good idea to improve your script to insert a button or link into the page that rearranges the posts when clicked. That way, you can use it when appropriate, without having to manually add every single Tumblr's URL as an @include parameter.

Finishing Off

Installing the script in Chrome is easy: just drag and drop it from your hard drive into a browser window. It'll warn you that scripts could potentially be dangerous (just click Continue), and then check again that you want to install this specific one:

Hit Install. If you're using another browser, refer to the relevant Greasemonkey script's manual - in any case, it'll be simple.

It's not perfect: you may have noticed that the navigation buttons have been moved to the top of the page, since they are contained in a div with a class of post. But hey - if that bugs you, you have the tools to fix it now.