Over a million developers have joined DZone.

Implementing bookmarklets in JavaScript

· Web Dev Zone

Bookmarklets 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/")
Load a script: The following code shows you how to load a script (with a library etc.). It creates a <code> element, adds the attributes src and onload and inserts it directly after the <head> element. The insertion causes the script to load and once loading is finished, the function pointed to by onload will be called.
    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()
Example:
    [].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.
Example interaction:
    $ java -jar yuicompressor-2.4.6.jar copy_url.js
The following code has been minified by YUI Compressor.
(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()}());
The original looks as follows.
    (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

Topics:

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}