Bookmarklets

Most javascript I write are in the form of bookmarklets.

Here is its structure:

javascript: (()=>{
    console.log("some code here");
})();

Here is how to use one

Formatting

Compared to normal javascript, a finished bookmarklet is similar to minified js with all extraneous whitespace characters removed.

Therefore, a finished bookmarklet:

  • must not contain comments
  • all lines must end with semi-colons, even after a closing curly bracket }
  • new-lines, even in constants and in template strings, are interpreted differently so the use of \n is preferred
  • it is best to have a readable formatted bookmarklet in addition to the finished minified version
Table of contents

Browser Utilities

countdown tab

As a default, 1.8e+6 is 30 minutes. Use whatever value you like in ms.

javascript: (()=> {
  
  const startCountdown = (ms = 1.8e+6) => {
    const countDownTime = new Date(Date.now() + ms).getTime();

    const x = setInterval(() => {
    
      const now = new Date().getTime();
        
      const distance = countDownTime - now;
        
      const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
      const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
      const seconds = Math.floor((distance % (1000 * 60)) / 1000);
        
      document.title = `${hours > 0? hours + ":" : ""}${minutes}:${seconds}`;
        
      if (distance < 0) {
        clearInterval(x);
        document.title = "done!";
      };
    }, 1000);
  };

  startCountdown();
})();
javascript: (()=>{

const URL = window.location.href;

const copy = content => {    
    const tempInput = document.createElement("textarea");
    document.body.appendChild(tempInput);
    tempInput.setAttribute("id", "temporary_input_clipboard");
    document.getElementById("temporary_input_clipboard").value = content;
    tempInput.select();
    document.execCommand("copy");
    document.body.removeChild(tempInput);
};
    
const textPrefix = `#:~:text=`;
let selection = encodeURI(window.getSelection()).replaceAll('-', '%2D');
    
selectedURL = URL+'#:~:text='+selection;
console.log(selectedURL);    
copy(selectedURL);    
window.location.href=selectedURL;
    
})();

this needs configuring for longer selections

toggle dark mode on supported sites

  • useful switching to light theme and back for printing documents into paper or pdf
  • linkedin mods html class; geeks for geeks mods body dataSet
  • cord mods body dataSet
javascript: (()=> {  
    const html = document.querySelector('html');
    const htmlClass = html.className;
    html.className = document?.querySelector('html.theme--dark') 
                   ? htmlClass.replace('theme--dark ', '') 
                   : htmlClass.replace('theme', 'theme theme--dark');
    const bodyData = document.querySelector('body').dataset;
    bodyData.darkMode = bodyData.darkMode === "true" ? "false" : "true";
    const bodyData2 = document.querySelector('[data-theme]').dataset;
    bodyData2.theme = bodyData2.theme === "dark" ? "light" : "dark";
})();

increase video playback speed

javascript: (()=>{
  const originalSpeed = 1;
  const speedToggle = 1.33;
  const udemyIndicator = document.querySelector('.playback-rate--trigger-text--l7hqr');
  const video = document.querySelector('video');
  const setSpeed = (speed) => {
    video.setAttribute('data-speed', speed);
    video.playbackRate = speed;
    udemyIndicator.innerText = speed + 'x';
  };
  video.getAttribute('data-speed') == speedToggle ? video.playbackRate = setSpeed(originalSpeed) : setSpeed(speedToggle);

})();
  • toggles a video back and forth between the originalSpeed (1x) to a specified preferred speed speedToggle (1.3x in this case)
  • this works on all html5 videos and updates the visual indicator on Udemy specifically to match

toggle youtube video annotations

javascript: (()=>{
  document.querySelector('.ytp-ce-element')?.style?.display === 'none' 
    ? document.querySelectorAll('.ytp-ce-element').forEach(div => div.style.display = 'block') 
    : document.querySelectorAll('.ytp-ce-element').forEach(div => div.style.display = 'none');
})();

Development Tools

hard refresh

javascript: (()=>{location.reload()})();

edit content

javascript: (()=>{
  let body = document.querySelector("body");
  body.contentEditable === 'true' ? body.contentEditable = false : body.contentEditable = true;
})();
  • toggles contentEditable property on the body tag.
  • allows quick, light editing of web page format or content without needing to get into dev tools.
  • unlike document.designMode = 'on' || 'off', it can be applied at the element level ranther than on the whole document. It also displays spelling mistakes .

remove all styles

javascript: (()=>{
  [...document.querySelectorAll('style'), ...document.querySelectorAll('link[rel="stylesheet"]')].forEach( style => style.remove());
})();

optionally, also remove inline styles. may have unexpected results:

javascript: (()=>{
  [...document.querySelectorAll('[style]')].forEach( el => el.style = null);
})();

toggle inject CSS

javascript: (()=>{

let css = `
  h1 {color: pink}
`;

const injectCSS = css => {
  let el = document.createElement('style');
  el.type = 'text/css';
  el.id='css_injection';
  el.innerText = css;
  document.head.appendChild(el);
};

const swap = () => !document.getElementById('css_injection') ? injectCSS(css) : document.getElementById('css_injection').remove();
const replace = (newCss=css) => {
  css = newCss;
  !document.getElementById('css_injection') ? injectCSS(css) : document.getElementById('css_injection').innerText = css;
};

swap();

})();
  • toggle custom css to show on a page
  • css needs can be predefined in the bookmarklet as css
  • if pasted in the console or ran as chrome snippet instead, replace() becomes available for use and toggled css is modifiable as an argument

Printing Pages

insert page break before selected element

since $0 is only acessible to dev tools this first version cannot be a bookmarklet

  1. ctrl + shift + c or ⌘ + shift c to select the element, which will be $0
  2. ctrl + enter or ⌘ + return to call the css insertion and insert hr
  3. run insertHR($0) in the console to add more <hr> to the selected element to page break
let css = `
  hr { break-after: page; }
`;

const injectCSS = css => {
  let el = document.createElement('style');
  el.type = 'text/css';
  el.id='css_injection';
  el.innerText = css;
  document.head.appendChild(el);
};

const swap = () => !document.getElementById('css_injection') ? injectCSS(css) : "";
const replace = (newCss=css) => {
  css = newCss;
  !document.getElementById('css_injection') ? injectCSS(css) : document.getElementById('css_injection').innerText = css;
};

const insertHr = (selectedNode) => {
  const hr = document.createElement("hr");
  const parent = selectedNode.parentNode;
  parent.insertBefore(hr, selectedNode);
};

swap();
insertHr($0);

Alternatively, in conjunction with the edit content bookmarklet, <hr> is copied into the clipboard and can be pasted in the position of the cursor as a page break. This will work as a bookmarklet.

javascript: (()=> {  
  let css = `
  hr { break-after: page; }
  @media print { hr { visibility: hidden } }
`;

const injectCSS = css => {
  let el = document.createElement('style');
  el.type = 'text/css';
  el.id='css_injection';
  el.innerText = css;
  document.head.appendChild(el);
};

const swap = () => {!document.getElementById('css_injection') && injectCSS(css) };
swap();
  navigator.clipboard.write([
  new ClipboardItem({
    'text/html': new Blob(['<hr>'], { type: 'text/html' }),
    'text/plain': new Blob(['---'], { type: 'text/plain' })
  })
]);
})();

Leetcode

copy leetcode solution into markdown

javascript: (()=> {  
  
const problem = document.querySelector("[id*='content'] div > a");
const difficulty = document.querySelector("[id*='content'] [class*='text-difficulty']").innerText;
const language = document.getElementById('headlessui-popover-button-:r1e:')?.innerText || document.querySelector('[class="rounded items-center whitespace-nowrap focus:outline-none inline-flex bg-transparent dark:bg-dark-transparent text-text-secondary dark:text-text-secondary active:bg-transparent dark:active:bg-dark-transparent hover:bg-fill-secondary dark:hover:bg-fill-secondary px-1.5 py-0.5 text-sm font-normal group"]').innerText;
const codeIDE = document.querySelector('.view-lines.monaco-mouse-cursor-text');
const codeLines = [...document.querySelectorAll('.view-lines.monaco-mouse-cursor-text .view-line')];
codeLines.pop();
const sortedLines = codeLines.sort((a, b) => parseInt(a.style.top.slice(0,-2)) - parseInt(b.style.top.slice(0,-2)));
const code = sortedLines.map((line) => (line.innerText)).join('\n');
  
const copy = content => {    
  const tempInput = document.createElement("textarea");
  document.body.appendChild(tempInput);
  tempInput.setAttribute("id", "temporary_input_clipboard");
  document.getElementById("temporary_input_clipboard").value = content;
  tempInput.select();
  document.execCommand("copy");
  document.body.removeChild(tempInput);
};

const value = `[${problem.innerText} ${difficulty === 'Easy' ? '🍐' : difficulty === 'Medium' ? '🍊' : '🍎'}](${problem.href})
${language !== 'JavaScript' ? language.toLowerCase() + '\n' : ''}\`\`\`${language === 'JavaScript' ? 'js' : language === 'Ruby' ? 'rb' : language}
${code}
\`\`\`
`;

copy(value);

})();

// `value`'s `\n` at the end of each line must be explicit when minified for a bookmarklet but should be omitted if pasted in the console or chrome snippet

go to leetcode problem ( from another educational source )

javascript: (()=> {  
  let url = new URL(window.location.href);
  if (url.host = 'www.educative.io' || 'designgurus.io') window.location.href = 'https://leetcode.com/problems/' + url.pathname.split('/').pop().replace('solution-', '')
})();

Misc

Mychart open pdf

opens mychart pdf in a full window

url = document.querySelector('.pdfobject').src;
// options:
// open in a popup
window.open(url, '_blank').focus();
// open in current tab
window.location.href = url;

personally preferred bookmarklet use-case

javascript: (()=> {
  url = document.querySelector('.pdfobject').src; window.location.href = url;
})();