Last active
December 23, 2022 05:15
-
-
Save magasine/33a2456d21101dc535ed017a896973f7 to your computer and use it in GitHub Desktop.
! Scroll to highlights - Toggle (bookmarklet)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 = ' ' + `<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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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
`