Over this holiday break I had the interesting opportunity to write a bookmarklet for a friend who runs a comic based website. Instead of just manipulating the currently loaded page, the bookmarklet needed to send a list of images to another site. Often when writing bookmarklets, we tend to only think of loading our code in the context of a HTML content page. How often do you test your bookmarklets when the browser is viewing an image? In this article I am going to go through the code I used to bootstrap my bookmarklet script, and discuss some of the interesting challenges I experienced along the way.
- Different browsers have various max-lengths of bookmarks. Keep in mind that a bookmarklet is kind of an accidental feature. I think the average max length works out to around 2000 characters, but some browsers (like Internet Explorer 6) have limits as low as 508 characters.
After our bootstrapper loads the script we created, any external libraries will be loaded. For example, I used jQuery and jQuery UI for my most recent project. After the dependencies are loaded, we will then execute our main code.
Another thing to keep in mind when you’re building your bookmarklet is how the site behaves after the function is disabled. For example, if your bookmarklet gives all images on the site a red border, what happens when the user no longer wishes to use the bookmarklet? For this reason, I tend to create a cleanup method that allows our bookmarklet changes to be undone, and leaves the script in a state that can later be used again.
The bootstrap code
For the purposes of this bookmarklet, I needed to write a piece of code that would interact with a standard HTML page and it’s images, or interact with a page that was a single loaded image. For that reason, the first thing we need to do is determine what type of page we’re dealing with. If the page is HTML, we can insert a script. If the page is an image, we need to behave differently. While I found that Firefox and WebKit both generated a HTML container to render image pages, their behavior surrounding script events of these pages were too inconsistent to be depended upon.
After tidying up our script, and adding the surrounding tag, here is a final rendered output of our code, I came up with the following:
Loading jQuery and jQueryUI
If you look at the example in the Smashing Magazine article, you will notice a couple of differences. We need to add an event for onreadystatechange to handle Internet Explorer. I found that IE inconsistently set the readyState of the script object to ‘loaded’ or ‘complete’ in various parts of the DOM, so as a rule I check for both. If you don’t make this change, IE will never notify the script that jQuery is finished loading.
In this case, I’m really only waiting on jQuery and jQuery UI to load. If there were more dependent scripts, I would likely create an array of scripts that need to be loaded, and check all of their completion every turn through the getDepenencies method.
With the supporting code written, we’re now ready to work on our main method. This is where bookmarklets really are different based on your task. In my case, I’m creating a visual element on the page, complete with styles to match the target site. This works pretty much as expected, with a single caveat: any style definitions you create must be at the very bottom of your appended script. Internet Explorer has a nasty habit of inconsistently handling styles and scripts when appended to the DOM. For some reason beyond my understanding, appended style definitions, whether via script or ajax calls, only work if they are at the very bottom of the appended code. This is fantastically fun to figure out on your own, so hopefully I’ve saved you some trouble.
This code simply creates a formatted div and adds it to the top of the page.
Cleaning up the mess
If you look at the generated HTML above, you’ll notice that I include a cancel link. I like to give the user the option to cancel out of using the current bookmarklet, and even relaunch the bookmarklet without issue. So when you’re done, make sure to test closing and re-launching the code. I suggest keeping all of your elements on the page, and simply hiding them from the user:
And for now, that’s it. For the source to this project, visit my GitHub.