The Firefox Logo, licensed under the MPL2 license

Bookmarklets

Notes published the
9 - 11 minutes to read, 2240 words
Categories: web
Keywords: html javascript web

Since I want to save copies of webpages on Internet Archive, I searched if there was a browser extension that could help me to ease the job.

There are, in fact, such extensions, but then I realized there is a safer way to handle it.

By using a bookmarklet.

A bookmarklet is a bookmark stored in a web browser that contains JavaScript commands that add new features to the browser.

— bookmarklet
from Wikipedia

Bookmarklets are not executed automatically

An add-on for archiving generic webpages on the Internet Archive needs to access all pages to fulfill its job.

Thus such an addon can access any page opened in the browser, also pages with sensitive information. Determining if an addon is malicious or goes rogue is not always easy, thus there is a certain risk in installing any addon, even more so if it has access to all pages all the time.

Bookmarklets, compared to browser extensions, have the main advantage that they are not executed automatically.

This has multiple implications.

It is executed only when triggered explicitly, thus the possible damage is constrained, as the code has access to the data only in a limited time frame.

Another implication is that bookmarklets do not use any resources. It does not matter how complex they are and how many you have unless the browser has an issue with too many bookmarks. It’s just data until you execute it.

Bookmarklets have access only to the current page

Bookmarklets only have access to the current page, they cannot access other pages.

It means that it is safe to execute a bookmarklet even if some other instance or tab of the browser is displaying sensitive information.

This also poses different limitations.

It does not seem possible to create a bookmarklet that clicks on a link on a page, opens a new page, and operates on the newly loaded page.

A bookmarklet is also not able to execute another bookmarklet.

Example of a bookmarklet: archive page on Internet Archive

With these limitations, it is possible to create a function for archiving the page, without giving to a third-party program, no matter where it comes from, access to all pages.

On top of that, the code for this bookmarklet is extremely short and easy to verify

javascript:(function(){location.href='https://web.archive.org/save/'+location.href;})();

The code just prepends https://web.archive.org/save/ to the current URL.

In fact, for saving https://www.example.com on the Internet Archive, it is sufficient to write https://web.archive.org/save/https://www.example.com in the navigation bar.

The bookmarklet avoids the need to modify the URL in the navigation bar manually.

No automatic update mechanism

An addon would have the advantage that if the webpages of Internet Archive would change, then the author of the addon would (hopefully) update it and Firefox install the new version.

With bookmarklets, the end-user is responsible for keeping them up-to-date (or using some external synchronization mechanism).

This is both an advantage and a disadvantage.

The main advantage is that the environment does not change outside the control of the end user.

If an addon goes rouge, and updates are installed automatically, the user might not notice what is happening. With a bookmarklet, this is not the case.

The main disadvantage is that updates are not necessarily nasty, some fix security issues or bugs.

How to create a bookmarklet

Bookmarklets are organized like the other bookmarks. Instead of containing a link to a page, the URL contains the javascript code to execute, prefixed by javascript:.

Some bookmarklets

You can just copy-paste the code into your bookmarks, as shown in the documentation.

Note that unless the bookmarklet loads a new page, all changes to the current page can be undone by simply refreshing the page in your browser.

View the current page on Internet Archive

Similar to saving a website on Internat Archive, it is sufficient to change the URL:

view on Internat Archive
javascript:(function(){location.href='https://web.archive.org/web/*/'+location.href;})();

This bookmarklet takes as an optional parameter a string to match. The output is also sorted, and duplicated elements are removed.

extract links
javascript:(function(){
  function htmlEscape(s){
    return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
  }
  var arr = [].slice.call( document.querySelectorAll('a') );
  var urls = arr.map(function(elem) { return elem.getAttribute('href'); });
  let unique = [...new Set(urls)].sort();
  var p=prompt("Show links with this text in the target url (leave empty to list all links):", "");
  document.writeln('<!DOCTYPE html><html><head><title>Links</title></head><body><ul>');
  for (var i=0; i<unique.length; i++){
    var link = unique[i];
    if( (link !== "" ) && ((p === "") || link.includes(p)) ){
      document.writeln("<li><a href='"+link+"'>" + htmlEscape(link) +"</a></li>");
    }
  };
  document.writeln('</ul></body>');
})();

I would like to be able to open a new tab with the result, but it is generally good that a website is not able to do it.

An addon, on the other hand, can open a new tab without issues; for example, Link Gopher does it.

An alternative would be to open a pop-up window, but those are generally blocked too (and it is generally a good thing).

Edit the current page

Toggle if the page is editable, and show temporarily a banner with the current status.

javascript:(function(){
  a=document.createElement("div");
  a.type='text/css';
  a.style=`position: fixed;top: 0;right: 0;left: 0;bottom: auto;z-index: 100000000;background-color: black;color: white;font-size: 3rem; text-align: center;`;
  if(document.body.contentEditable === "true"){
    document.body.contentEditable="false";
    document.designMode="off";
    a.innerText="Editing disabled";
  }else{
    document.body.contentEditable="true";
    document.designMode="on";
    a.innerText="Editing enabled";
  }
  document.body.appendChild(a);
  setTimeout(()=>{a.remove()},2500);
})();

Reset selection, copy, search, and context menu

Some websites disable selection, copy actions, and/or the context menu.

This snippet should help to regain control, by resetting the relevant hooks.

reset hooks
javascript:(function fun(w, message){
	var arr = ['contextmenu','copy','cut','paste','mousedown','mouseup','beforeunload','beforeprint','keydown','selectstart','dragstart','drag','keyup','click'];
	for(const x of arr){
		if(w['on' + x]){w['on' + x] = null;}
		w.addEventListener(x, function(e){e.stopPropagation()}, true);
	};
	for(var j = 0, f; f = w.frames[j]; j++){
		try{fun(f, false)}catch(e){}
	}
	if(message){
		a=document.createElement("div");
		a.type='text/css';
		a.style=`position: fixed;top: 0;right: 0;left: 0;bottom: auto;z-index: 100000000;background-color: black;color: white;font-size: 3rem; text-align: center;`;
		a.innerText="Hooks disabled";
		document.body.appendChild(a);
		setTimeout(()=>{a.remove()},2500);
	}
})(window, true);

Disable javascript on loaded document

I have to admit I am not sure why it works, but so far, this snippet stops any javascript.

Thus load the site, and then execute the bookmarklet, if you do not want to change it anymore:

javascript:(function(){
	document.body.innerHTML = document.body.innerHTML;
})();

Toggle images with alternate text

alternate text
javascript:(function(){
	[].forEach.call(document.images, function (img) {
		alt=document.createTextNode(img.alt);
		img.parentNode.replaceChild(alt, img);
	});
})();

Although it would be more useful if the browser would show the alternate text (thus not download the images at all), and then be able to individually load the images/video and other potentially heavy resources.

At least it can be useful for testing websites, and see how they look when images are disabled.

Validate the current page with the W3 validator

validate css
javascript:(function(){location.href='https://jigsaw.w3.org/css-validator/validator?uri='+location.href;})();
validate feed
javascript:(function(){location.href='https://validator.w3.org/feed/check.cgi?url='+location.href;})();

Redirect one page to another

Some websites offer multiple web pages with the same content.

Wikipedia, for example, has a separate URL for mobile pages. The page 42 has a correpsonding mobile page.

Another website is, for example, Reddit; it currently provides old.reddit pages.

Instead of changing the URL manually, a click on the corresponding bookmarklet is faster and less error-prone:

javascript:(function(){
	       if(location.hostname === "www.reddit.com" || location.hostname === "reddit.com" ){
		location.hostname = "old.reddit.com";
	} else if(location.hostname === "www.old.reddit.com" || location.hostname === "old.reddit.com" ){
		location.hostname = "reddit.com";
	} else {
		alert('Not a Reddit URL.');
	}
})();

There are multiple ways to approach cookie banners.

A first solution would be to simply hide them, for example

javascript:(function(){
	/* stackoverflow */
	[].forEach.call(document.getElementsByClassName("js-consent-banner"), function (el) {el.remove();});
	/* implemented with iubenda */
	document.getElementById("iubenda-cs-banner")?.remove();
	/* add other "common" banners */
})();

This approach has the main disadvantage, that on every reload, the dialog will be shown again.

As the bookmarklets are not executed automatically, the user also needs to click on the bookmarklet on every reload.

Another approach would be to click on the accept, close, or deny button

javascript:(function(){
	/* stackoverflow */
	document.getElementsByClassName("js-reject-cookies")[0]?.click();
	/* iubenda and others */
	document.getElementsByClassName("iubenda-cs-close-btn")[0]?.click();
	document.getElementsByClassName("g-consentmanager__confirm-selection")[0]?.click();
	document.getElementById("privacy-cp-wall-accept")?.click();
	/* add other "common" banners */
})();

This has the advantage that the banner is not shown if the webpage is reloaded, and also reduces the chances of breaking a website (some sites do not work correctly if the banner is simply ignored).

The main disadvantage is that it might be less efficient.

Some websites unnecessarily reload the whole page after clicking on "accept" or "deny", in such cases, hiding the banner is much more efficient.

In general, clicking on a button executes some additional unknown code, thus it might be better to verify the host before executing .click() or add some additional checks to ensure that what we are clicking is a privacy banner and not something else that just happens to have a matching class name or id.

If you clear the browser cache periodically, or use temporary containers on Firefox, a browser extension might be a more efficient solution.

Hiding or clicking on accept/deny, even through a bookmarklet, is something that you want to happen automatically in most cases.

Clear local storage

Clear what a website might have saved locally, for example cookies.

clear cache
javascript:(function(){
	window.localStorage.clear();
})();

Open/Download images

Download all images found on the current page

download images
javascript:(function(){
	[].forEach.call(document.images, function (el) {
		var url = el.src;
		var elem = document.createElement('a');
		elem.href = url;
		elem.download = url;
		elem.click();
	});
})();

Manage profiles

Firefox profiles have many use cases, but in order to access them, you normally need to start Firefox with a command line parameter, which is not always practical or user-friendly.

While not technically a bookmarklet (it does not execute any code, just opens a page), from the opened page is possible to start a new instance in a different profile from Firefox

download images
about:profile

It has been so practical that I had to include it.

Keyboard shortcuts

It is possible to go full rouge and personalize the view of websites.

For example by adding or styling elements, or by removing distracting components (for example hero banners, videos, sticky headers, and footers, …​ ).

At that point, the fact that bookmarklets are not executed automatically might become a limitation, especially since on every refresh, you need to execute them again.

It is possible to assign a shortcut to a bookmark.

The main disadvantage is that the URL is filled with the address of the bookmark (the Javascript code), which does not happen when clicking on the bookmarklet.

Some recommendations

Bookmarklets need to be a valid one line of JavaScript code prefixed by javascript:.

The snippets written here can be copy-pasted as is as new bookmarks, the browser (at least Firefox and Chrome) will care to remove all the newlines during pasting.

This means that:

  • the semicolon is not optional in most places anymore (it is just easier to add it at the end of every statement)

  • inline comments (// …​) are not supported, only multiline comments (/* …​ */) are possible

To avoid changing the global status by accident, all the code should be written in an anonymous function and executed immediately.

This means that a bookmarklet should generally start with javascript:(function(){ and end with })();.

Note that this is not strictly necessary. javascript:location.href='https://web.archive.org/save/'+location.href; will not break any page, still, using a function does not harm and prevents accidental errors.


Do you want to share your opinion? Or is there an error, some parts that are not clear enough?

You can contact me anytime.