Created
January 20, 2023 16:45
-
-
Save roseg43/75bbb53217f58f249024e1a9e5bcbca0 to your computer and use it in GitHub Desktop.
Simple tool for selecting content on a page using a visible marquee
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
(() => { | |
/** | |
* Renders a button that when clicked copies the NodeList passed to the constructor to the clipboard | |
* | |
* @param [NodeList] content - a NodeList of selected elements | |
*/ | |
const CopyToClipBoardBtn = (content) => { | |
const btn = document.createElement('button'); | |
btn.innerText = 'Copy to clipboard'; | |
btn.style.padding = '10px'; | |
btn.style.border = 'none'; | |
btn.style.borderRadius = '5px'; | |
btn.style.backgroundColor = '#000'; | |
btn.style.color = '#fff'; | |
btn.style.cursor = 'pointer'; | |
btn.style.marginTop = '10px'; | |
btn.addEventListener('click', () => { | |
navigator.clipboard.writeText(content); | |
}); | |
return btn; | |
}; | |
/** | |
* Generates a dead simple modal using the supplied content | |
* @param content [NodeList] A list of elements to render in the modal | |
* @returns | |
*/ | |
const Modal = (content) => { | |
const modal = document.createElement('div'); | |
modal.classList.add('modal'); | |
modal.style.position = 'fixed'; | |
modal.style.width = '100%'; | |
modal.style.height = '100%'; | |
modal.style.backgroundColor = 'rgba(255,255,255,0.9)'; | |
modal.style.top = 0; | |
modal.style.left = 0; | |
modal.style.display = 'grid'; | |
modal.style.gridTemplateAreas = '"gutter-left modal-content gutter-right"'; | |
modal.style.gridTemplateColumns = '1fr 10fr 1fr'; | |
modal.style.justifyContent = 'center'; | |
modal.style.alignItems = 'center'; | |
const modalContent = document.createElement('div'); | |
modalContent.classList.add('modal-content'); | |
modalContent.style.gridArea = 'modal-content'; | |
const modalTitle = document.createElement('h1'); | |
modalTitle.innerText = 'Selected Content'; | |
modalContent.appendChild(modalTitle); | |
// Add the content to the modal | |
modalContent.append(...content); | |
// Add a copy to clipboard button | |
const copyBtn = CopyToClipBoardBtn(content); | |
modalContent.appendChild(copyBtn); | |
modal.append(modalContent); | |
const open = () => { | |
document.body.appendChild(modal); | |
// Prevent scroll on the html root | |
document.documentElement.style.overflow = 'hidden'; | |
}; | |
const close = () => { | |
modal.remove(); | |
// Re-enable scroll on the html root | |
document.documentElement.style.overflow = null; | |
}; | |
// If the user presses escape, close the modal | |
const onEscape = (e) => { | |
// Check if the modal has been rendered | |
if (!document.querySelector('.modal')) return; | |
if (e.key === 'Escape') { | |
close(); | |
} | |
}; | |
document.addEventListener('keydown', onEscape); | |
return { | |
open, | |
close | |
}; | |
} | |
/** | |
* When initialized, registers event listeners to allow the | |
* user to click and drag to select a rectangular area of the screen. | |
* The selected area is then used to select all elements within it. | |
* | |
*/ | |
const SelectionMarquee = () => { | |
const init = () => { | |
console.log('init'); | |
let isMouseDown = false; | |
let startX = 0; | |
let startY = 0; | |
let marquee = null; | |
const onMouseDown = (e) => { | |
console.log('mousedown') | |
// Makse sure shift is pressed | |
if (!e.shiftKey) return; | |
isMouseDown = true; | |
console.log(e); | |
startX = e.clientX; | |
startY = e.layerY; | |
marquee = document.createElement('div'); | |
marquee.classList.add('marquee'); | |
marquee.style.left = `${startX}px`; | |
marquee.style.top = `${startY}px`; | |
marquee.style.backgroundColor = 'red'; | |
marquee.style.position = 'absolute'; | |
marquee.style.opacity = '0.4'; | |
document.body.appendChild(marquee); | |
}; | |
const onMouseMove = (e) => { | |
console.log('move'); | |
if (!isMouseDown) return; | |
const width = e.clientX - startX; | |
const height = e.layerY - startY; | |
marquee.style.width = `${width}px`; | |
marquee.style.height = `${height}px`; | |
}; | |
const onMouseUp = (e) => { | |
if (!isMouseDown) return; | |
isMouseDown = false; | |
const marqueeRect = marquee.getBoundingClientRect(); | |
const elements = document.querySelectorAll('*'); | |
for (let i = 0; i < elements.length; i++) { | |
const elementRect = elements[i].getBoundingClientRect(); | |
if (isIntersecting(marqueeRect, elementRect)) { | |
elements[i].classList.add('selected'); | |
} | |
} | |
//marquee.remove(); | |
// Get the selected elements and render them into a modal | |
const selectedEls = getSelectedElements(); | |
const modal = Modal(selectedEls); | |
modal.open(); | |
clearSelectedEls(); | |
}; | |
/** | |
* Gets all selected elements that can be considered content | |
* @returns {NodeList} A list of all elements that have the class 'selected' | |
*/ | |
const getSelectedElements = () => { | |
const allowedTags = [ | |
'P', | |
'H1', | |
'H2', | |
'H3', | |
'H4', | |
'H5', | |
'H6', | |
'UL', | |
'OL', | |
'LI', | |
'A', | |
'IMG', | |
'FIGURE', | |
'FIGCAPTION', | |
'BLOCKQUOTE', | |
'PRE', | |
'CODE', | |
'TABLE', | |
'THEAD', | |
'TBODY', | |
'TR', | |
'TH', | |
'TD', | |
'CAPTION', | |
'EM', | |
'STRONG' | |
]; | |
// Get all selected elements whose tagname is in the allowedTags array | |
const selectedEls = Array.from(document.querySelectorAll('.selected')); | |
return selectedEls.filter((el) => allowedTags.includes(el.tagName)); | |
}; | |
const isIntersecting = (rectA, rectB) => { | |
return !( | |
rectA.right < rectB.left || | |
rectA.left > rectB.right || | |
rectA.bottom < rectB.top || | |
rectA.top > rectB.bottom | |
); | |
}; | |
const clearSelectedEls = () => { | |
const selectedEls = document.querySelectorAll('.selected'); | |
selectedEls.forEach((el) => el.classList.remove('selected')); | |
}; | |
const clearMarquee = () => { | |
if (marquee) marquee.remove(); | |
}; | |
document.addEventListener('mousedown', onMouseDown); | |
document.addEventListener('mousemove', onMouseMove); | |
document.addEventListener('mouseup', onMouseUp); | |
} | |
init(); | |
} | |
SelectionMarquee(); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment