Bookmarklets

Bookmarklets are bookmarks that do something other than just linking to a web page (though they sometimes do that as well). They are pieces of software that run in your browser on the web page that is open when you click them. Like regular bookmarks, you can simply drag and drop the link to your bookmarks bar for use later, perhaps on a different web page.

Bookmarklet links always start with "javascript:" and, on this page, they have this pic beside them: For example, this link: Alert Title will popup an alert displaying the web page's title, which is useful if you want to copy it.

Isolation matters

I create & test bookmarlets in the console (F12) - refresh (F5), paste, execute, edit, repeat - but globally scoped variables can interfere with other scripts.

javascript: {/* linearized/minified code is safely scoped here */};

IF you use let not var. See also, this old PITA:(function(){})()

DoStuff(ThisURL) - location.href

Show on 12ft ladder
javascript:location.href='https://12ft.io/proxy?&q='+location.href;
Show page in Steam
javascript:location.href='steam://openurl/'+location.href;
[-] Decrement url
javascript:let f=location.href.match(/^(.*?)(0*(\d+))([^\d]*)$/);if(f){location=(f[1]+(f[3]*1-1+'').padStart(f[2].length,'0')+f[4]);}
[+] Increment url
javascript:let f=location.href.match(/^(.*?)(0*(\d+))([^\d]*)$/);if(f){location=(f[1]+(f[3]*1+1+'').padStart(f[2].length,'0')+f[4]);}
[up] Parent path url
javascript:location.href=location.href.replace(/\/[^\/]+$/,'')
Deviantart Gallery -> RSS feed
javascript:location.href='https://backend.deviantart.com/rss.xml?q=gallery:'+location.href.match(/^(?:https:\/\/www\.deviantart\.com\/(?:watch\/)?)(\w+)/)[1]

Alert => can copy alert(location.href);

Reddit short link
javascript:alert('https://redd.it/'+location.href.split('/')[6]);
Alert Title
javascript:alert(document.title);

New Window(Stuff(ThisURL))

javascript:window.open(
  'https://DoStuff?q='+location.href,
  'window name/key'
);
Wayback Machine
javascript:window.open('https://web.archive.org/web/*/'+location.href,'waybackmachine');
TinEye search images
javascript:{window.open('http://tineye.com/search?url=location.href');}
Tumblr Gallery
... window.open('http://stone.dialog.jp/tumblr/gallery.html?user='+location.hostname.split('.',1)[0]+'&small=250&large=400&start=0#');
Tumblr Thumbnails
... window.open('http://tumb.la/'+location.hostname.split('.',1)[0]);

URL substitution

let a='URL1',
    b='URL2',
    l=location.href;
location.href=l.replace(b,a);
Youtube (new window)
javascript:{let a='watch?v=',b='embed/' ... window.open(l.replace(b,a));}

bi-directional ... location.href=( l.includes(b)? l.replace(b,a) : l.replace(a,b) );

Youtube <-> full frame
javascript:{let a='watch?v=',b='embed/';l=location.href;location.href=(l.includes(b)?l.replace(b,a):l.replace(a,b));}

Extracting(scraping) data

Open Inoreader article
javascript:window.open('https://www.inoreader.com/article/'+document.querySelector('div[data-oid].article_current').getAttribute('data-oid'));

InoReader uses jQuery => we can too javascript:window.open('https://www.inoreader.com/article/'+$('div[data-oid].article_current').attr('data-oid'));

Open multi-reddit of follows. (Old Reddit)
javascript:window.open('https://old.reddit.com/r/'+[...document.querySelectorAll('li>a.choice[href*="/user/"]')].map(e=>e.textContent.replace('/','_')).join('+'));
Open multi-reddit of follows. (New Reddit - open the list of feeds/follows first if not already docked to the left)
javascript:window.open('https://old.reddit.com/r/'+[...document.querySelectorAll('a[role="menuitem"][aria-label^="u/"]')].map(e=>e.textContent.replace('/','_')).join('+'));
Open multi-reddit of follows. (Both)
javascript:window.open('https://old.reddit.com/r/'+[...document.querySelectorAll(typeof header=='object'?'li>a.choice[href*="/user/"]':'a[role="menuitem"][aria-label^="u/"]')].map(e=>e.textContent.replace('/','_')).join('+'));

Fixes

Rotate Image
javascript:var a=prompt('Clockwise angle?','90');document.getElementsByTagName('img')[0].setAttribute('style','transform:rotate('+a+'deg);margin:auto;');
Fit image to width/height
javascript:{let i=document.getElementsByTagName('img')[0],b=document.body,r=b.clientWidth/i.width;if(r.toFixed(1)==1){r=b.clientHeight/i.height};i.width*=r;i.height*=r;}
Add links to Mitre 10 Search results
javascript:[...document.querySelectorAll('input[name=productSkuId]')].forEach(n=>n.insertAdjacentHTML('beforebegin', '<a href="https://www.mitre10.co.nz/shop/p/'+n.value+'">link</a>'));
Zap stylesheets
javascript:{[...document.styleSheets].forEach(x=>{x.disabled=true;})};

Blah selection in Blag

This is a pattern that's used a lot. E.g. "search for the selected text in <SearchEngine>" is so common, that it's in the context menu for most(all?) browsers.

//this get selection boilerplate covered a bunch of browser types and is probably not needed anymore.
let text = (
    ( window.getSelection && window.getSelection() )
  || 
    ( document.getSelection && document.getSelection() )
  ); 
if (text == '') {
  text = prompt('Blah?')
};
if (text) {
  DoStuff
}
else {
  DoOtherStuff
};

↪ (concise)

let w = window,
    g = w.getSelection,
    d = document.getSelection,
    t = ((g && g()) || (d && d()));
if (t == '') {
  t = prompt('Blah?')
};
if (t) {
  DoStuff
} else {
  DoOtherStuff
};

Whitespace is only needed to seperate letters (and readability).

↪ (linearized) javascript:{let w=window,g=w.getSelection,d=document.getSelection,t=((g&&g())||(d&&d()));if(t==''){t=prompt('Blah?')};if(t){DoStuff}else{DoOtherStuff}};

Regex replace: /s/(?<=\w)\s(?!\w)|(?<!\w)\s(?!\w)|(?<!\w)\s(?=\w)//

Code Beautify JS minifier

↪ (minified) javascript:{let w=window,g=w.getSelection,d=document.getSelection,t=g&&g()||d&&d();""==t&&(t=prompt("Blah?")),t?DoStuff:DoOtherStuff}

(original minified) javascript:{let text=window.getSelection&&window.getSelection()||document.getSelection&&document.getSelection();""==text&&(text=prompt("Blah?")),text?DoStuff:DoOtherStuff;}

No selection? => Prompt for parameter let t=((g&&g())||(d&&d())) ... if(t==''){t=prompt('Query?')};

Confirm address => can copy and/or not open let p = 'address' ... if(confirm(p)){window.open(p)};

encodeURIComponent = safe parameters let p = 'address' ... p = p+encodeURIComponent(t) ... if(confirm(p)){window.open(p)};

(select query) Let me Google that for you
javascript:{let p='https://letmegooglethat.com/?q=',w=window,g=w.getSelection,d=document.getSelection,t=((g&&g())||(d&&d()));if(t==''){t=prompt('Query?')};p=p+encodeURIComponent(t);if(confirm(p)){w.open(p)};}
(select query) DuckDuckGo
... p='https://duckduckgo.com/?q=' ...
(select query) The Movie Database
... p='https://www.themoviedb.org/search?query=' ...
Blah selection in Blag
->
↪ (select query) Blah selection in Blag
javascript:{let p='input',w=window,g=w.getSelection,d=document.getSelection,t=((g&&g())||(d&&d()));if(t==''){t=prompt('Query?')};p=p+encodeURIComponent(t);if(confirm(p)){w.open(p)};}
(select text) Create link to highlight text
... p=location.href+'#:~:text=' ... {t=prompt('Highlight text?')}; ...
(select hash) Create magnet link
... p='magnet:?xt=urn:btih:',n=prompt('Name?') ... {t=prompt('Hash?')};p=p+t+'&dn='+n; ...
(select text) Speak selection (TTS)
DoStuff: let s=w.speechSynthesis,m=new SpeechSynthesisUtterance(),f=function(){m.voice=s.getVoices().filter(function(voice){return /UK.*Fem/.test(voice.name)})[0];s.speak(m)};m.rate=m.pitch=1;m.text=t;if(!s.getVoices().length){setTimeout(f,500)}
or do something based on what is selected... Jira/CASD
javascript:{
  var f='PPPT-',
    p='Ticket Number?',
    w=window,
    x=w.getSelection,
    y=document.getSelection,
    z=((x&&x())||(y&&y())).toString().trim(),
    j='https://mohits.atlassian.net/browse/',
    c='https://servicescentre.moh.govt.nz/CAisd/pdmweb.exe',
    b='?OP=SEARCH+FACTORY=',
    a='+SKIPLIST=1+QBE.EQ.',
    s='ref_num=',
    d='chg';
  if(z==''){z=prompt(p)};
  if(!z){j=c};
  if(/^(\d{1,4})$/.test(z)){z=f+z;};
  if(/^(\d{5})$/.test(z)){j=c+b+d+a+d+'_'+s;};
  if(/^(\d{6})$/.test(z)){j=c+b+'cr'+a+s;};
  p=j+encodeURIComponent(z);
  if(confirm('Open? '+p)){
    w.open(p);
  };
}
  • Misc

    Edit page (Stop editing)
    javascript:document.body.contentEditable='true';document.designMode='on';
    Index of / gallery
    javascript:{
      let d=document,
          l=d.links,
          re=/.+\.(bmp|gif|jfif|jpe?g|png|svg|tiff|webp)/,
          h='<html><head><title>'+d.title
      +' gallery</title><style>img{max-width:15%;}</style></head><body><div>';
      for(i=0;i<l.length;i++){
        let p=l[i].href;
        if(p.toLowerCase().match(re)){
          h+='<img src="'+p+'"/>'
        }
      }
      h+='</div></body></html>';
      d.body.innerHTML=h;
    }
    Index of / full filenames
    javascript:[...document.links].forEach(a =>{
      if( a.text.endsWith('>') ) a.innerText=decodeURIComponent( a.href.split('/').pop() )
    })

    Third-party JS injection.

    The linked scripts are not my code. Use at your own risk.

    Kickass!(Asteroids) from Kick Ass - Destroy the web
    javascript:{ let d=document, s=d.createElement('script'); s.src='http://hi.kickassapp.com/kickass.js'; d.body.appendChild(s); };

    or multiple scripts...

    Katamari! (from here)
    javascript:{ let d=document,s; [ 'http://kathack.com/js/kh.js', 'http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js' ].forEach(u=>{ s=d.createElement('script'); s.src=u; d.body.appendChild(s); }) };

    Subresource Integrity lets you verify the js file hasn't been altered but CORS needs to be enabled by the host (all CDNs support this). The downside is that every update to the js file requires updating the bookmarklet - or the integrity hash inside it (Hash generator).

    YATS (This one is mine)
    javascript:{ let d=document,s; [ { u:'https://www.gible.net/YATS.js', //script file h:'sha384-uEw6nnHT+KwA7i6mSslMVnwEmAEzmzRx+sQMJ7eISEvi54QY6qXvddZyS95VTl1N', //integrity hash e:function(){ //error handler for when integrity hash check inevitably fails if(confirm('JS load failed! Update bookmarklet?')){ window.open('https://www.gible.net/bookmarklets.html#yats') } } }, { u:'https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js', h:'sha384-hhQ2DWmF+rxpBS/u5Hl/5JYmaKB7TRlXyKPFCeq7M7CIQsUih3E3M3+IUjgB5uE1' } ].forEach(e=>{ s=d.createElement('script'); s.src=e.u; s.integrity=e.h; s.crossOrigin='anonymous'; if(e.e)s.onerror=e.e; d.body.appendChild(s); }); };
    YATS (without injection)

    Just do the thing.

    Do (Also mine)
    ... [ 'https://www.gible.net/do.js' ] ...