Implementing bookmarklets in JavaScript
Join the DZone community and get the full member experience.
Join For FreeBookmarklets are little plugins for your browsers: JavaScript programs packed into javascript: URLs that you add to your bookmarks and start by clicking on them. They perform an operation on the currently open web page such as submitting it to Twitter. There are even bookmarklets that transform the current web page, for example, to add icons that, when clicked, add an event to Google Calendar. A separate post explains what bookmarklets are in more detail. This post tells you how to implement bookmarklets. It presents techniques, tools, and patterns for doing so.
Preparation and techniques
Firebug: helps you with trying out your JavaScript program in the same environment that the bookmark will eventually run in. Just go to an interesting website and run your code snippets in its shell. Its multi-line code editor is very helpful in this regard: To enable it, click the expansion button in the single-line console. Furthermore, Firebug lets you copy the current code to the clipboard, as a bookmarklet link. This link is crude, but it works. We’ll later see, how it can be made more compact.
Don’t pollute the global namespace: You don’t want to mess up the environment of the web site “in which” your bookmarklet is executed. For example, by using an Immediately Invoked Function Expression (IIFE) the variable t won’t become global, below.
(function() { var t=document.title; console.log("Title: "+t); })Develop in strict mode: If you switch on strict mode, JavaScript warns you about more potential problems. Later on, you can switch it off so that the bookmarklet URL is more compact, but for development, it is very helpful. For example, the code below inadvertently creates a global variable, because a var has been forgotten. The output is abc.
(function() { t = "abc"; }()); console.log(t);Strict mode, however warns you that there is a problem:
(function() { "use strict"; t = "abc"; }());Result: ReferenceError: assignment to undeclared variable t
Finish with undefined: On (non-Webkit) browsers, if you don’t return (or finish with!) undefined, the result replaces the current web page.
- Automatic if you use an IIFE: The result of a function is undefined unless you return something.
- Chain of statements: Make undefined the last statement.
- Single expression: use the void operator. The first bookmarklet below replaces the current web page. The second one doesn’t.
javascript:window.open("http://www.whitehouse.gov/")
javascript:void window.open("http://www.whitehouse.gov/")
var d = document; var s = d.createElement(’script’); s.src=’https://ajax.googleapis.com/.../jquery.min.js’; s.onload = function() {...}; d.getElementsByTagName(’head’)[0].appendChild(s);Caveat: A bookmarklet is subject to the same restrictions as the page on which it is executed. Thus, invoking a bookmarklet on an online page cannot load files from your local hard drive. This is unfortunate, because it would allow you to put most of the code of a bookmarklet into an (offline-accessible) separate file.
Collect input
This section describes how to collect input from the currently shown web page.Parse HTML manually: Standard DOM provides several methods that let you retrieve HTML elements from the current page.
- getElementsByClassName()
- getElementsByName() [attribute name]
- getElementsByTagName()
- getElementById()
[].forEach.call( document.getElementsByTagName("a"), function(x) { console.log(x); });forEach() is an array method (available in ECMAScript 5). By using call(), you can apply it to the non-array returned by the DOM. The DOM frequently returns array-like objects that look like arrays, but are not.
Using jQuery: jQuery is very helpful for extracting information from a web page. But you want to avoid the jQuery instance that you load from clashing with a jQuery instance that the page uses. The following code shows you how to do that.
jQuery.noConflict(true)(function($) { // code using jQuery });This reverts everything to the way it was before loading jQuery. The true argument means that in addition to the global variable $, jQuery will also be reverted to is prior value (if any). The jQuery web site has more information on jQuery.noConflict().
Transform HTML with JQuery: The following code snippet prepends the text "BLA: " to each <h3> tag in the current web page. You can run it in Firefox on a web page that has many of those tags (for example: spiegel.de).
var d = document; var s = d.createElement(’script’); s.src=’https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js’; s.onload = function() { jQuery.noConflict(true)(function($){ $("h3").each(function(i,e) { e.innerHTML = "BLA: "+$(e).text(); }); }); }; d.getElementsByTagName(’head’)[0].appendChild(s);
Producing output
This section describes how to show the results of your bookmarklet. You always have alert() or prompt() (which allows you to show a text to be copied to the user), but it is usually better present your results via HTML.Show output in the current tab:
document.body.innerHTML="<h1>Hi!</h1> Test!";
Show output in a new tab:
var d=window.open().document; d.write("<html><body><h1>Hi!</h1> Test!</body></html>"); d.close();
Minify the bookmarklet code
Yahoo has implemented the YUI Compressor, a tool that minifies your bookmarklet code (makes it as small as possible, without changing what it does). YUI Compressor can be downloaded as a JAR file which makes it easy to install.- Variable names can be minified.
- Property names cannot be minified.
$ java -jar yuicompressor-2.4.6.jar copy_url.jsThe following code has been minified by YUI Compressor.
The original looks as follows.(function(){var e=window.open().document;var c=(window.getSelection?""+window.getSelection():"").replace("<","<");var b={h:document.location.href,t:document.title,st:(c?"\n\n"+c:c),sh:(c?"<br>"+c:c)};e.write(("<html><head><title>Copy link: {t}</title></head><body><a href=’{h}’>{t}</a>{sh}<br><br><textarea id=’text’ cols=’80’ rows=’10’>{t} {h}\n\n<a href=’{h}’>{t}</a>{st}</textarea></body></html>").replace(/{([a-z]+)}/g,function(d,a){return b[a]}));e.close()}());
(function() { var d=window.open().document; var s=(window.getSelection?""+window.getSelection():"") .replace("<","<"); var a={ h: document.location.href, t: document.title, st: (s ? "\n\n"+s : s), // selection text sh: (s ? "<br>"+s : s) // selection HTML }; d.write( ("<html><head><title>Copy link: {t}</title></head><body>" ... ).replace(/{([a-z]+)}/g, function(g0,g1){return a[g1]}) ); d.close(); }())This code is the “copy link” bookmarklet, which uses two notable techniques:
- Mini-templating: The HTML in the output window is produced by inserting the property values of a into a text string, via the replace() method. Embedded references to the property values look like this: {t} refers to property t.
- Doing something with the currently selected text: window.getSelection() is only used if the browser has implemented it.
From http://www.2ality.com/2011/06/implementing-bookmarklets.html
Opinions expressed by DZone contributors are their own.
Comments