window.onload = function(){
// general vars
let isOpenContextMenu = false;
const $contextMenu = document.getElementById('contextMenu');
// all code/pre elements in one object to use in one loop
const $codeElements = document.querySelectorAll('pre, code');
let $actualCodeElement = {};
// methods
function selectText(element) {
var range, selection;
if(window.getSelection) {
selection = window.getSelection();
range = document.createRange();
range.selectNodeContents(element);
selection.removeAllRanges();
selection.addRange(range);
}
}
function unSelectText() {
window.getSelection().removeAllRanges();
}
// listeners
// block right clicke when context menu on code/pre element is open
function listenerContextMenuBlocked(evt){
evt.preventDefault();
}
// clicks when context menu on code/pre elements is open
function listenerMenuClick(ev){
let $clickedElement = ev.target;
do{
if($clickedElement == $contextMenu){
// clicked on context menu
// --> copy action
let codeToCopy = $actualCodeElement.innerText;
let temporaryInput = document.createElement('input');
temporaryInput.type = 'text';
temporaryInput.value = codeToCopy;
document.body.appendChild(temporaryInput);
temporaryInput.select();
document.execCommand('Copy');
document.body.removeChild(temporaryInput);
// --> close menu and reset
$contextMenu.classList.remove('contextMenu--active');
isOpenContextMenu = false;
window.removeEventListener('contextmenu', listenerContextMenuBlocked);
return;
}
$clickedElement = $clickedElement.parentNode;
} while($clickedElement)
// clicked outside context menu
// --> close and reset
$contextMenu.classList.remove('contextMenu--active');
isOpenContextMenu = false;
window.removeEventListener('contextmenu', listenerContextMenuBlocked);
}
// open custom context menu when right click on code/pre elements
function listenerOpenContextMenuCodeBlock(e) {
e.preventDefault();
// used to copy conten in listenerMenuClick()
$actualCodeElement = e.target;
if(false === isOpenContextMenu){
// open context menu
$contextMenu.style.top = e.clientY + 2 + 'px';
$contextMenu.style.left = e.clientX + + 2 + 'px';
$contextMenu.classList.add('contextMenu--active');
isOpenContextMenu = true;
window.addEventListener('click', listenerMenuClick);
window.addEventListener('contextmenu', listenerContextMenuBlocked);
}
}
for(var i = 0; i < $codeElements.length; i++) {
//$actualElement = $codeElements[i]; //
$codeElements[i].addEventListener('contextmenu', listenerOpenContextMenuCodeBlock);
$codeElements[i].onmouseover = function() {
if(false === isOpenContextMenu){
selectText(this)
}
};
$codeElements[i].onmouseout = function() {
if(false === isOpenContextMenu){
unSelectText(this)
}
};
}
};
/* styles needed for custom context menu */
html {
position: relative;
}
#contextMenu {
display: none;
position: absolute;
font-family: sans-serif;
font-size: 11px;
line-height: 12px;
padding: 2px 5px;
background-color: #eeeeee;
border: 1px solid #a5a5a5;
box-shadow: 2px 3px 1px -1px rgba(0,0,0,0.4);;
cursor: context-menu;
z-index: 10;
}
#contextMenu:hover {
background-color: #aad7f3;
}
#contextMenu.contextMenu--active {
display: block;
}
<!-- SIMPLIFIED custom context menu for code/pre elements = hidden on page -->
<nav id="contextMenu" style="top: 50px; left: 30px" >
<div>Copy Codeblock</div>
</nav>
<!-- example elements for code presentation / testing -->
<pre>
Lorem, ipsum dolor sit amet consectetur adipisicing elit.
Provident magni blanditiis, ea necessitatibus esse nihil,
quae iste explicabo beatae perspiciatis quibusdam tempora minima,
eos molestias illum voluptatum voluptate ipsum perferendis!
</pre>
<code>
Li Europan lingues es membres del sam familie. Lor separat existentie
es un myth. Por scientie, musica, sport etc, litot Europa usa li sam vocabular.
Li lingues differe solmen in li grammatica, li pronunciation e li plu commun vocabules.
Omnicos directe al desirabilite de un nov lingua franca: On refusa continuar payar
custosi traductores.
</code>