Skip to content

Instantly share code, notes, and snippets.

@magasine
Last active December 23, 2022 05:15
Show Gist options
  • Save magasine/33a2456d21101dc535ed017a896973f7 to your computer and use it in GitHub Desktop.
Save magasine/33a2456d21101dc535ed017a896973f7 to your computer and use it in GitHub Desktop.
! Scroll to highlights - Toggle (bookmarklet)
javascript: (function () {
let vCounter = 0;
let objAnchors = {};
let popup;
let popupStyle = `<style>
#id_popup:hover {
border: 2px solid red;
}
#id_popup {
background-color: yellow !important;
border-radius: 10px;
border: 2px solid gray;
color: blue !important;
clear: left;
font-family: sans-serif;
font-size: 1rem;
margin: 0px;
overflow: auto;
padding: 10px;
position: fixed;
right: 10px;
text-align: left;
top: 100px;
transition: width 2s height 4s;
z-zndex: 2147483647;
}
#id_popup_title {
background-color: yellow !important;
}
.highlight {
background-color: yellow !important;
border-radius: 10px;
border: 1px solid red;
color: blue !important;
padding: 2px;
}
#id_btn_fake {
background-color: white !important;
border-radius: 5px;
border: 1px dashed blue;
color: blue;
margin: 1px;
padding: 0px 2px 0px;
}
#id_btn_clear {
background-color: white !important;
border-radius: 5px;
border: 1px solid blue;
color: blue;
font-size: smaller;
padding: 0px 2px 0px 2px;
}
</style>`
// Create popup and their elements
function createPopup() {
// The current window was opened by another window
// Prevent the source page from changing the CSS properties of the popup window
if (window.opener) {
window.opener.focus();
}
// Create the popup element
popup = document.createElement('div');
popup.setAttribute('id', 'id_popup');
// Create the iframe element and append it to the popup
const iframe = document.createElement('iframe');
iframe.setAttribute('id', 'id_iframe_STHL');
popup.appendChild(iframe);
// Set the style and content of the popup title
document.head.innerHTML += popupStyle;
popup.innerHTML = '<div id="id_popup_title"> <details> <summary> <strong>Scroll to Highlights</strong> </summary> <sup> Install or update from <a href="https://gist.github.com/magasine/33a2456d21101dc535ed017a896973f7" target="_blank">GitHub Gist</a> - (<a href="https://twitter.com/magasine" target="_blank">@magasine</a>) </sup> </details> <small> <li>Highlights mark: use hotkeys <button id=id_btn_fake style="cursor: not-allowed;">ctrl</button> + <button id=id_btn_fake style="cursor: not-allowed;">click</button></li><li>Highlights on/off: change checkboxes</li><li>Popup on/off: re-run bookmarklet</li></small></div>';
// Create the clear button
const clearButton = document.createElement('button');
clearButton.setAttribute('id', 'id_btn_clear');
clearButton.innerHTML = 'clear and reload page from cache';
// Add an event listener to the button to clear the popup when clicked
clearButton.addEventListener('click', function () {
hlClear();
popup.parentNode.removeChild(popup);
// window.location.reload(); // this will reload the current page from the server
window.location.reload(true); // this will reload the page from the cache instead of the server
});
// Append the clear button to the popup and text
popup.innerHTML += '<div id="id_tags_title" style="color: blue;"><strong>... Start</strong><br><small>Select text and press hotkeys</small></div>';
popup.appendChild(clearButton);
// Append the popup to the document body
document.body.appendChild(popup);
}
// Toggle popup visibility
function togglePopup() {
let popup = document.getElementById("id_popup");
if (popup.style.display === 'none') {
popup.style.display = 'block';
} else {
popup.style.display = 'none';
}
}
// Create menu items in popup
function createItem(hl_text, hl_id) {
// Create a checkbox element
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.checked = true;
checkbox.addEventListener('change', () => {
const span = document.getElementById(hl_id);
if (checkbox.checked) {
span.style.backgroundColor = 'yellow';
span.style.border = '1px solid red';
} else {
span.style.backgroundColor = '';
span.style.border = '';
}
});
// Create a label element
const label = document.createElement('label');
const hl_text_shortened = hl_text.substring(0, 20) + '...';
label.innerHTML = hl_text;
label.setAttribute('for', hl_id);
label.setAttribute('id', hl_id + '-label');
label.innerHTML = '&nbsp' + `<a href="#${hl_id}">${hl_text_shortened}</a>`;
// Create a list item element
const item = document.createElement('li');
item.appendChild(checkbox);
item.appendChild(label);
return item;
}
// Create hash anchor with highlight id and storage
function hlAnchor(hl_text) {
vCounter += 1;
const hl_id = `ScrollTo-${vCounter}`;
localStorage.setItem(hl_id, hl_text);
return hl_id;
}
// Clear highligts and remove itens from localStorage
function hlClear() {
const localStorageValues = Object.values(objAnchors);
// Clear highlights e remove values
for (let i = 0; i < localStorageValues.length; i++) {
const lsv = document.getElementById(localStorageValues[i]);
lsv.style.backgroundColor = '';
lsv.style.border = '';
localStorage.removeItem(localStorageValues[i]);
}
const localStorageKeys = Object.keys(localStorage);
// Remove keys
for (let i = 0; i < localStorageKeys.length; i++) {
const lsk = localStorage.getItem(localStorageKeys[i]);
if (lsk.includes('ScrollTo-')) {
localStorage.removeItem(localStorageKeys[i]);
}
}
objAnchors = {};
vCounter = 0;
}
// Highlights selections
function hlSelection() {
if (window.getSelection) {
const selection = window.getSelection();
if (selection.rangeCount) {
const range = selection.getRangeAt(0);
const hl_text = range.toString();
if (!objAnchors.hasOwnProperty(hl_text)) {
const hl_id = hlAnchor(hl_text);
objAnchors[hl_text] = hl_id;
const span = document.createElement('span');
span.id = hl_id;
span.style.backgroundColor = 'yellow';
span.style.border = '1px solid red';
span.style.padding = '2px';
range.surroundContents(span);
}
}
}
}
// Populate highlights to popup window
function popupPopulate() {
document.addEventListener('click', (event) => {
if (event.ctrlKey && event.button === 0) {
hlSelection();
// Create a div element
const divNav = document.createElement('div');
// Append items
for (const hl_text in objAnchors) {
const hl_id = objAnchors[hl_text];
const item = createItem(hl_text, hl_id);
divNav.appendChild(item);
}
// Remove any existing 'div' elements from the popup
const divElements = popup.querySelectorAll('div:not([id=id_tags_title])');
for (let i = 0; i < divElements.length; i++) {
divElements[i].style.display = "none";
}
// Append the new 'div' element to the popup
popup.appendChild(divNav);
}
});
}
popupPopulate();
if (!document.getElementById("id_popup")) {
createPopup();
} else {
togglePopup();
}
})();
// Use:
// - Run the bookmarklet to instructions on popup
// - The storage is temporary, while the localStorage cache exists
@magasine
Copy link
Author

magasine commented Dec 16, 2022

Minified or encoded bookmarklet (save in the favorite bar):

`
javascript:(()%3D%3E%7B!function()%7Blet%20e%2Ct%3D0%2Cn%3D%7B%7D%3Bfunction%20o(e%2Ct)%7Bconst%20n%3Ddocument.createElement(%22input%22)%3Bn.type%3D%22checkbox%22%2Cn.checked%3D!0%2Cn.addEventListener(%22change%22%2C(()%3D%3E%7Bconst%20e%3Ddocument.getElementById(t)%3Bn.checked%3F(e.style.backgroundColor%3D%22yellow%22%2Ce.style.border%3D%221px%20solid%20red%22)%3A(e.style.backgroundColor%3D%22%22%2Ce.style.border%3D%22%22)%7D))%3Bconst%20o%3Ddocument.createElement(%22label%22)%2Cl%3De.substring(0%2C20)%2B%22...%22%3Bo.innerHTML%3De%2Co.setAttribute(%22for%22%2Ct)%2Co.setAttribute(%22id%22%2Ct%2B%22-label%22)%2Co.innerHTML%3D%60%26nbsp%3Ca%20href%3D%22%23%24%7Bt%7D%22%3E%24%7Bl%7D%3C%2Fa%3E%60%3Bconst%20r%3Ddocument.createElement(%22li%22)%3Breturn%20r.appendChild(n)%2Cr.appendChild(o)%2Cr%7Dfunction%20l()%7Bif(window.getSelection)%7Bconst%20e%3Dwindow.getSelection()%3Bif(e.rangeCount)%7Bconst%20o%3De.getRangeAt(0)%2Cl%3Do.toString()%3Bif(!n.hasOwnProperty(l))%7Bconst%20e%3Dfunction(e)%7Bt%2B%3D1%3Bconst%20n%3D%60ScrollTo-%24%7Bt%7D%60%3Breturn%20localStorage.setItem(n%2Ce)%2Cn%7D(l)%3Bn%5Bl%5D%3De%3Bconst%20r%3Ddocument.createElement(%22span%22)%3Br.id%3De%2Cr.style.backgroundColor%3D%22yellow%22%2Cr.style.border%3D%221px%20solid%20red%22%2Cr.style.padding%3D%222px%22%2Co.surroundContents(r)%7D%7D%7D%7Ddocument.addEventListener(%22click%22%2C(t%3D%3E%7Bif(t.ctrlKey%26%260%3D%3D%3Dt.button)%7Bl()%3Bconst%20t%3Ddocument.createElement(%22div%22)%3Bfor(const%20e%20in%20n)%7Bconst%20l%3Do(e%2Cn%5Be%5D)%3Bt.appendChild(l)%7Dconst%20r%3De.querySelectorAll(%22div%3Anot(%5Bid%3Did_tags_title%5D)%22)%3Bfor(let%20e%3D0%3Be%3Cr.length%3Be%2B%2B)r%5Be%5D.style.display%3D%22none%22%3Be.appendChild(t)%7D%7D))%2Cdocument.getElementById(%22id_popup%22)%3Ffunction()%7Blet%20e%3Ddocument.getElementById(%22id_popup%22)%3B%22none%22%3D%3D%3De.style.display%3Fe.style.display%3D%22block%22%3Ae.style.display%3D%22none%22%7D()%3Afunction()%7Bwindow.opener%26%26window.opener.focus()%2Ce%3Ddocument.createElement(%22div%22)%2Ce.setAttribute(%22id%22%2C%22id_popup%22)%3Bconst%20o%3Ddocument.createElement(%22iframe%22)%3Bo.setAttribute(%22id%22%2C%22id_iframe_STHL%22)%2Ce.appendChild(o)%2Cdocument.head.innerHTML%2B%3D%22%3Cstyle%3E%5Cn%20%20%20%20%20%20%23id_popup%3Ahover%20%7B%5Cn%20%20%20%20%20%20%20%20%20border%3A%202px%20solid%20red%3B%5Cn%20%20%20%20%20%20%7D%5Cn%20%20%20%20%20%20%23id_popup%20%7B%5Cn%20%20%20%20%20%20%20%20%20background-color%3A%20yellow%20!important%3B%5Cn%20%20%20%20%20%20%20%20%20border-radius%3A%2010px%3B%5Cn%20%20%20%20%20%20%20%20%20border%3A%202px%20solid%20gray%3B%5Cn%20%20%20%20%20%20%20%20%20color%3A%20blue%20!important%3B%5Cn%20%20%20%20%20%20%20%20%20clear%3A%20left%3B%5Cn%20%20%20%20%20%20%20%20%20font-family%3A%20sans-serif%3B%5Cn%20%20%20%20%20%20%20%20%20font-size%3A%201rem%3B%5Cn%20%20%20%20%20%20%20%20%20margin%3A%200px%3B%5Cn%20%20%20%20%20%20%20%20%20overflow%3A%20auto%3B%5Cn%20%20%20%20%20%20%20%20%20padding%3A%2010px%3B%5Cn%20%20%20%20%20%20%20%20%20position%3A%20fixed%3B%5Cn%20%20%20%20%20%20%20%20%20right%3A%2010px%3B%5Cn%20%20%20%20%20%20%20%20%20text-align%3A%20left%3B%5Cn%20%20%20%20%20%20%20%20%20top%3A%20100px%3B%5Cn%20%20%20%20%20%20%20%20%20transition%3A%20width%202s%20height%204s%3B%5Cn%20%20%20%20%20%20%20%20%20z-zndex%3A%202147483647%3B%5Cn%20%20%20%20%20%20%7D%5Cn%20%20%20%20%20%20%23id_popup_title%20%7B%5Cn%20%20%20%20%20%20%20%20%20background-color%3A%20yellow%20!important%3B%5Cn%20%20%20%20%20%20%7D%5Cn%20%20%20%20%20%20.highlight%20%7B%5Cn%20%20%20%20%20%20%20%20%20background-color%3A%20yellow%20!important%3B%5Cn%20%20%20%20%20%20%20%20%20border-radius%3A%2010px%3B%5Cn%20%20%20%20%20%20%20%20%20border%3A%201px%20solid%20red%3B%5Cn%20%20%20%20%20%20%20%20%20color%3A%20blue%20!important%3B%5Cn%20%20%20%20%20%20%20%20%20padding%3A%202px%3B%5Cn%20%20%20%20%20%20%7D%5Cn%20%20%20%20%20%20%23id_btn_fake%20%7B%5Cn%20%20%20%20%20%20%20%20%20background-color%3A%20white%20!important%3B%5Cn%20%20%20%20%20%20%20%20%20border-radius%3A%205px%3B%5Cn%20%20%20%20%20%20%20%20%20border%3A%201px%20dashed%20blue%3B%5Cn%20%20%20%20%20%20%20%20%20color%3A%20blue%3B%5Cn%20%20%20%20%20%20%20%20%20margin%3A%201px%3B%5Cn%20%20%20%20%20%20%20%20%20padding%3A%200px%202px%200px%3B%5Cn%20%20%20%20%20%20%7D%5Cn%20%20%20%20%20%20%23id_btn_clear%20%7B%5Cn%20%20%20%20%20%20%20%20%20background-color%3A%20white%20!important%3B%5Cn%20%20%20%20%20%20%20%20%20border-radius%3A%205px%3B%5Cn%20%20%20%20%20%20%20%20%20border%3A%201px%20solid%20blue%3B%5Cn%20%20%20%20%20%20%20%20%20color%3A%20blue%3B%5Cn%20%20%20%20%20%20%20%20%20font-size%3A%20smaller%3B%5Cn%20%20%20%20%20%20%20%20%20padding%3A%200px%202px%200px%202px%3B%5Cn%20%20%20%20%20%20%7D%5Cn%20%20%20%20%20%20%3C%2Fstyle%3E%22%2Ce.innerHTML%3D'%3Cdiv%20id%3D%22id_popup_title%22%3E%20%3Cdetails%3E%20%3Csummary%3E%20%3Cstrong%3EScroll%20to%20Highlights%3C%2Fstrong%3E%20%3C%2Fsummary%3E%20%3Csup%3E%20Install%20or%20update%20from%20%3Ca%20href%3D%22https%3A%2F%2Fgist.github.com%2Fmagasine%2F33a2456d21101dc535ed017a896973f7%22%20target%3D%22_blank%22%3EGitHub%20Gist%3C%2Fa%3E%20-%20(%3Ca%20href%3D%22https%3A%2F%2Ftwitter.com%2Fmagasine%22%20target%3D%22_blank%22%3E%40magasine%3C%2Fa%3E)%20%3C%2Fsup%3E%20%3C%2Fdetails%3E%20%3Csmall%3E%20%3Cli%3EHighlights%20mark%3A%20use%20hotkeys%20%3Cbutton%20id%3Did_btn_fake%20style%3D%22cursor%3A%20not-allowed%3B%22%3Ectrl%3C%2Fbutton%3E%20%2B%20%3Cbutton%20id%3Did_btn_fake%20style%3D%22cursor%3A%20not-allowed%3B%22%3Eclick%3C%2Fbutton%3E%3C%2Fli%3E%3Cli%3EHighlights%20on%2Foff%3A%20change%20checkboxes%3C%2Fli%3E%3Cli%3EPopup%20on%2Foff%3A%20re-run%20bookmarklet%3C%2Fli%3E%3C%2Fsmall%3E%3C%2Fdiv%3E'%3Bconst%20l%3Ddocument.createElement(%22button%22)%3Bl.setAttribute(%22id%22%2C%22id_btn_clear%22)%2Cl.innerHTML%3D%22clear%20and%20reload%20page%20from%20cache%22%2Cl.addEventListener(%22click%22%2C(function()%7B!function()%7Bconst%20e%3DObject.values(n)%3Bfor(let%20t%3D0%3Bt%3Ce.length%3Bt%2B%2B)%7Bconst%20n%3Ddocument.getElementById(e%5Bt%5D)%3Bn.style.backgroundColor%3D%22%22%2Cn.style.border%3D%22%22%2ClocalStorage.removeItem(e%5Bt%5D)%7Dconst%20o%3DObject.keys(localStorage)%3Bfor(let%20e%3D0%3Be%3Co.length%3Be%2B%2B)localStorage.getItem(o%5Be%5D).includes(%22ScrollTo-%22)%26%26localStorage.removeItem(o%5Be%5D)%3Bn%3D%7B%7D%2Ct%3D0%7D()%2Ce.parentNode.removeChild(e)%2Cwindow.location.reload(!0)%7D))%2Ce.innerHTML%2B%3D'%3Cdiv%20id%3D%22id_tags_title%22%20style%3D%22color%3A%20blue%3B%22%3E%3Cstrong%3E...%20Start%3C%2Fstrong%3E%3Cbr%3E%3Csmall%3ESelect%20text%20and%20press%20hotkeys%3C%2Fsmall%3E%3C%2Fdiv%3E'%2Ce.appendChild(l)%2Cdocument.body.appendChild(e)%7D()%7D()%3B%7D)()%3B

`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment