Tips for Writing Bookmarklets

Perhaps I can provide you with some tips for your bookmarklets. I'll assume you are familiar with Javascript (a good Javascript manual can be found here, by the way). I concentrate in stead on some tidbits that can be useful in general. See also the general rules for writing bookmarklets.


Scope of Bookmarklets

Bookmarklets run in the current browser window and nowhere else. This means that you cannot from window A change anything in window B. This is a matter of security. You can, however, traverse windows inside the current window - in which case we're talking about a frameset.


The DOM Hierarchy

The browser window is the top level object which a bookmarklet references. From there you can browse to all other objects your browser supports. The window object is implicit, so window.document and document are the same thing.

A window always contains a document object. The document contains the page elements, such as links, forms, etc.

A window can contain a number of frames (in which case it's a frameset). These frames can be traversed, and each frame can be treated as a window (since that's what a frame really is).


Detecting Frames

Unless there's a reason for not doing so, your users will appreciate if your bookmarklet can handle both framed and non-framed pages.

Even if the bookmarklet does not support frames it should still be aware of them, so if the user runs the bookmarklet in a framed page it will tell the user it cannot execute. You can use this code for that purpose:

if(frames.length > 0)
  alert('Sorry, frames detected.');
else{
  /* Regular code goes in here */
}

There is just one problem: Embedded frames (IFRAMEs) are reported by the browser as frames (except for Netscape 4.7 which doesn't recognize them). However, you can use the getElementsByTagName method to retrieve the number of IFRAMEs in the current document. So if frames.length is larger than getElementsByTagName('iframe').length, it means that you're dealing with a "real" frameset. Let's modify the code:

if(frames.length > document.getElementsByTagName('iframe').length)
  alert('Sorry, frames detected.');
else{
  /* Regular code goes in here */
}


The Cross-Frame Problem

For security reasons your browser does not allow you to run script code outside the current site's domain. This means that if a frameset found at geocities.com contains a frame with a page from ibm.com, then you cannot run a bookmarklet or any other script code in that frame. In fact, attempting to do so will produce an access exception. You can find the details here.

You may think cross-frames isn't a big problem, but it is for bookmarklet authors, because many pages contain banner ads inside IFRAMEs. These ads are almost always situated at another domain. So if you traverse a frameset one frame at a time you might run into an access exception which makes your bookmarklet fail.

Fortunately, javascript supports exception handling, and all modern browsers implement it (Explorer from ver. 5.5, Netscape from ver. 6, Opera from ver. ?). The syntax is:

try{
  ...
}
catch(e){
  ...
}

Using exception handling it is safe to access cross-frames. You won't get access, but at least you won't crash your bookmarklet. This is illustrated below.


Traversing a Frameset

You can use the code below as a template for bookmarklets that can handle both framed and non-framed pages.

It works like this: A window always has the potential to contain frames, either as IFRAMEs or in an ordinary frameset. These frames can also contain frames, and so on. Starting with the current window, we first manipulate its content in some way (change the color, say), then for each potential frame of the window we recursively call the same method, passing the frame as parameter (remember, a frame is the same as a window). The try...catch ensures the bookmarklet can handle access errors (typically caused by IFRAME ads).

function traverse(w){
  try{
    /* Manipulate w.document */
    ...
    for(var i=0; i<w.frames.length; i++){
      traverse(w.frames[i]);
  }
  catch(e){
    /* Do something with e or just do nothing */
  }
}

traverse(window);


Strings

In Javascript you can use both single quotes ( 'xxx' ) and double quotes ( "xxx" ) in string expressions. If you want a string inside a string you can write it like this: s='x="hello";'. Since you should always use single quotes (it makes it easier to distribute your bookmarklets), you should in stead write it like this: s='x=\'hello\';'. The backslash before the apostrophes means the apostrophe should be considered a character inside the string.

Literalised single quotes (that's what they're called when they have a backslash in front) are also practical when you have words or sentences that use apostrophes, like so: s='Rock\'n\'roll isn\'t dead'.

Finally, you can sometimes use the escape and unescape functions. unescape(%27) is a single quote. The browser will automatically render it as a single quote when the bookmarklet executes.


The function() Trick

Tired of using void to catch return values? If you put the statement (function(){...})() around your code it is encapsulated so you automatically catch all return values. The (function(){...}) part creates a nameless function. The () part runs it. This works in all browsers. Example:

javascript:
(function(){
  ...
  x=3;
  lnks=document.links;
  document.links[0].href='http://www.ibm.com/';
  ...
})
()

Normally you would have to use void to catch the return values (see explanation on the rules page), but using the function() trick you don't have to worry about that.


Reuse of Global Variables

You can make use of the fact that variables in bookmarklets are global (unless it's declared as local inside a function, using var or the function() trick).

An example could be two bookmarklets that share a common variable. Bookmarklet 1 could store the scroll position of the current page in a global variable called scroll. Bookmarklet 2 could then later be called, restoring the scroll position by reading the same variable (provided the user doesn't leave or refresh the page in the meantime).

Another example is the "Read Global Variable" bookmarklet, which gives you the value of any global variable you like.


Script Inclusion

When you want to create bookmarklets with extensive logic it can be a good idea to create an external javascript file ( *.js), then include this file in the currently loaded page.

So far only Internet Explorer allows you to do this. Here's the code:

var script=document.createElement('script');
script.src='http://...mysite.../...myscript.js';
document.getElementsByTagName('head')[0].appendChild(script);
...
/* Call functions found in myscript.js here */

Alternatively, have your bookmarklet open a new window. In the document of this new window you write a copy of the current document, with the exception that you slip in your script reference, using the <script> tag. Here's the code (works in most modern browsers):

var src=''+document.documentElement.innerHTML+'';
src=src.replace(/<HEAD>/i, 
       '<HEAD><script src=\'http://...mysite.../...myscript.js\'></script>');
var win=open();
with(win.document){
  open();
  write(src);
  close();
}
...
/* Manipulate win using myscript.js functions */


Other Tips

See also the links to articles that describe how to write your own bookmarklets.



Top of this page
Go to the main page
Send e-mail
Updated 13 Apr. 2006