Creating Hosted Favelets

version 1.0
Last Revision: 06.20.2004
steve@slayeroffice.com
slayeroffice.com

Introduction

What kinda favelets?

Hosted favelets are what I have taken to calling favelets who's scripts are hosted on a server somewhere rather than stored directly in the bookmark proper. For example, a non-hosted (or should we call them native?) favelet would look something like this:

// line returns added for legability
javascript:
x=parseInt(prompt('First Number'));
y=parseInt(prompt('Second Number'));
alert(x+y);

The above code would go into an anchor tag's href, a user could add it to his favorites and then anywhere they went on the web they could add numbers together. A hosted favelet, on the other hand, would look something like this:

// line returns added for legability
javascript:
z=document.createElement("script");
z.setAttribute('src','http://yoursite.com/yourscript.js');
z.setAttribute('type','text/javascript');
z.setAttribute('id','yourFaveletID');
void(document.getElementsByTagName("head")[0].appendChild(z));

The preceding code dynamically appends a script element to the head of the document. If all goes well, the browser will source the script and your favelet will execute.

This has been well covered in Simon Willison's article, Better Living Through Bookmarklets on Sitepoint, and is not the focus of this article. My intention is to address some of the more common issues you may run into when developing a favelet of this nature and how to address or avoid them and to illustrate the techniques I employ when developing them.

The Good and the Bad

First, lets talk a little about the advantages and disadvantages of hosted favelets so you'll know what you're getting into.

Benefits

Drawbacks

Coding Techniques

During Development

When I first begin writing a hosted favelet, I do it all in-line in an HTML document. You could, of course, develop the entire thing as the module that it will eventually become, but that's going to take you forever and I don't recommend it. Doing it this way allows you to work in only one file, locally, rather than two or three off the server.

Intializing the Favelet

Every application needs an entry point, and I always make mine a function called "init". This is where I create any HTML objects that the script will require and call any setup functions. Here is what a typical init method would look like:

function init() {
	//create the style sheet reference
	styleObject = d.getElementsByTagName("head")[0].appendChild(d.createElement("link"));
	styleObject.rel="Stylesheet";
	styleObject.type="text/css";
	styleObject.href="http://yoursite.com/yourCSS.css";
	styleObject.id="mCSS";

	//create the main container element
	mObj = d.body.appendChild(d.createElement("div"));
	mObj.id="mContainer";
	mObj.appendChild(d.createTextNode("Hello there."));

	//create a textarea element
	tObj = mObj.appendChild(d.createElement("textarea"));
	tObj.id="mTextArea";

	//create a button
	bObj = mObj.appendChild(d.createElement("input"));
	bObj.className="btn";
	bObj.type = "button";
	bObj.id="nBtn";
	...
	
}

The first thing I do is create the style sheet's link element. I do this first to prevent as much flashing of unstyled content as possible.

Next I create a DIV element that will act as the "window" for the favelet. To this object I'll append all of the dependency objects that the application will require, such as buttons, links, textareas and etc. I append them all to this object not only because they belong there as part of the UI, but because we have to get rid of them at some point as well. More on why this makes it easy in a bit.

Javascript Tips

The DOM

The ability to create elements dynamically lies at the core of the hosted favelet. These are the main DOM methods you will be using, with links to their documentation at the Gecko DOM Reference:

The innerHTML property of HTML elements comes in handy, as does createTextNode.

IMPORTANT! -- If you intend to use your favelet on sites serving their content with an application/xhtml+xml MIME type, DO NOT use innerHTML. It won't work and your application will not execute.

Coding Tips

CSS Tips

Creating a style sheet for a hosted favelet is no different than creating one for a normal HTML page, though there is one notable expection. You must be extremely explicit in your rule declarations. By this I mean that you don't have the luxury to rely on default rules for your UI, such as text-align defaulting to left. When your favelet is invoked it will inherit the styles of the page it is invoked on. To avoid this, here are some tips.

I could go on, but you probably get the picture. Just be extremely thorough in your style rules and you wont be surprised by what your UI looks like on someone else's site.

Cleaning it All Up

It's important that you give your user a means of exiting your favelet. I generally use the "escape" key for this (key code 27: go here for easy key code lookup), but some of the favelets I've written simply have buttons or links that say "close". You don't want a user to have to reload the document to get rid of your favelet when they have finished using it - that would be like having to reboot Windows to get out of Word. Oh, wait...

At any rate, it isn't enough to just set the display of the container element to "none" and be done with it. You've no way of knowing what it is the user is up to on their page. You could have css selectors with the same name as theirs, or javascript variables or functions with the same names which could cause all sorts of debugging headaches for the user if they arent as savvy as you in this sort of thing. (Yes, I know you're going out of your way with the unique naming convention, but its a great big interweb we've got here...). Outside of that, its just the polite and responsible thing to do.

Your clean-up method should remove everything you've created. Style sheets, container elements and even the original script that started the whole thing. Here is what a typical clean-up method looks like:

// remove the container element
document.body.removeChild(document.getElementById("mainContainer"));
// remove the style sheet.
document.getElementsByTagName("head")[0].removeChild(document.getElementById("cssFile"));
// finally, remove the script
document.getElementsByTagName("head")[0].removeChild(document.getElementById("scriptFile"));

In the above example, if you had followed my earlier advice about referencing created objects by variables, you could use those variables in place of the document.getElementById stuff, i.e.:

// remove the container element
d.body.removeChild(mObj)

It probably goes without saying - but by removing the container element from the DOM, you'll also remove all of it's children like your buttons and links and images, so no need to worry about removing them individually. This is why its important to append everything you'll be needing to one parent object - as the commercial says, it "makes clean-up a snap".

Testing your Favelet

Obviously you want to make sure the thing works before you release it out into the wilds. Here's what I do:

Conclusion

So there you have it - probably nothing you couldn't have figured out on your own, and certainly not anything ground breaking or revolutionary within these pages, but hopefully someone will benefit. If you have questions not answered here or suggestions, feel free to contact me.

Document History