Reputation: 99
I want to mimic Google Chrome's Inspect Element tool in pure JavaScript.
I am working on an extension for Google Chrome. I want to implement something similar to the Inspect Element tool in pure JavaScript (no jQuery or any other libraries/frameworks/dependencies). If anyone uses AdBlock, I specifically want to mimic the "Block an ad on this page" functionality that allows the user to select an element on the page.
While the extension is active, the user should be able to:
'CODE' element nested inside
is being moused over, the entire 'P' element should have the 'DIV' overlay over it'CODE' element
it should re-size the 'DIV' overlay to cover only the deeper nested 'CODE' element
Why use a 'DIV' overlay instead of just adding a class to the element that adds a border/outline/background color?
The overlay prevents the user from directly interacting with the element. If there is an href/onClick attribute I do not want it to activate when clicked.
Can you just write something that stores all inline 'onclick' attributes, sets them to null/return false; and then restores the original 'onclick' code?
Tried that:
onclicks = document.querySelectorAll('[onclick]');
saved = [];
// disable onclicks
function disableOnclicks() {
for (var i = 0; i < onclicks.length; i++) {
saved[i] = onclicks[i].cloneNode();
onclicks[i].onclick = "return false;";
};
console.log("onclicks disabled");
}
// enable onclicks
function enableOnclicks() {
for (var i = 0; i < onclicks.length; i++) {
onclicks[i].onclick = saved[i].onclick;
};
console.log("onclicks enabled");
}
// if injecting script, make sure all elements have loaded
window.onload = function(){
disableOnclicks();
console.log("onclicks disabled");
enableOnclicks();
console.log("onclicks enabled");
}
unfortunately, it doesn't handle any invisible event listeners that may be on a given page.
// CSS to highlight element
var overlayCSS = document.createElement("style");
overlayCSS.type = "text/css";
overlayCSS.innerHTML = "body .highlight-element{z-index:2147483647!important}.highlight-element{background-color:rgba(130,180,230,.4);outline:#0F4D9A solid 1px;box-sizing:border-box;position:absolute;display:none}";
document.body.appendChild(overlayCSS); // append style tag to body
// create div overlay to highlight elements & prevent onclick events
var overlay = document.createElement("div");
overlay.className = "highlight-element";
document.body.appendChild(overlay); // append div overlay to body
// check if overlay was appended
if (overlay === document.getElementsByClassName('highlight-element')[0]) {
console.log("overlay exists");
};
// positions div overlay based on targetRect AKA element.getBoundingClientRect()
function moveOverlay (targetRect) {
var overlay = document.getElementsByClassName('highlight-element')[0];
// correct for page scroll
overlay.style.top = (targetRect.top + window.scrollY) + "px";
overlay.style.left = targetRect.left + "px";
overlay.style.height = targetRect.height + "px";
overlay.style.width = targetRect.width + "px";
overlay.style.display = "block";
}
// set start time for lastfire
var lastfire = Date.now();
function getInnermostHovered(e) {
// difference between now and the last event
var difference = Date.now() - lastfire;
// console.log(difference);
// delay handling mousemove by some milliseconds
// making 0 may induce epileptic shock...
if (difference > 100) {
// prevent endless highlight loop
if (e.target.getAttribute('class') == "highlight-element") {
e.target.style.display = "none";
}
// get element under mouse
var n = document.querySelector(":hover");
var nn;
// get deepest child element that has :hover
while (n) {
nn = n;
n = nn.querySelector(":hover");
}
console.log("nn: " + nn);
// get dimensions to pass to div overlay
var targetRect = nn.getBoundingClientRect();
// console.log(targetRect.top);
// display overlay div at element position under mouse
moveOverlay(targetRect);
// event fired, so overwrite last fire time
lastfire = Date.now();
}
}
// onMouseMove get deepest child element under the mouse
document.addEventListener('mousemove', getInnermostHovered, false);
Feel free to test it out on this page (refresh the page to cancel), currently I have the overlay sort-of working by alternating displaying/hiding the overlay 'DIV' on the 'mousemove' event. Ideally I want it to run as smooth Chrome's Inspect Element and give me the element data. Any and all ideas are appreciated, but I must stress that I really want to do this in pure JavaScript.
Upvotes: 6
Views: 1306
Reputation: 31840
Solve it with document.elementFromPoint(x,y)
.
This code will work pasted in Chrome's console, press ESC to cancel.
var rootNode = document.documentElement;
var currentNode = currentNode || {};
var lastNode = lastNode || {};
var nodeClone = nodeClone || {};
var prevOnClick = prevOnClick || {};
function nodeHandler (e) {
var x = e.clientX;
var y = e.clientY;
// console.log(x+", "+y);
currentNode = document.elementFromPoint(x, y);
if (currentNode === rootNode || currentNode === document.body){
// If current node is HTML or BODY, do nothing
} else {
if (currentNode === lastNode) {
// If still on same node, do nothing
// console.log('Same node');
} else {
// console.log('Different node');
// if lastNode has classList, check for onclick attribute and remove highlight-element class
if (lastNode.classList) {
// If lastNode had onclick attribute, replace with the untouched value from nodeClone
if (lastNode.getAttribute("onclick") != null) {
prevOnClick = nodeClone.getAttribute("onclick");
lastNode.setAttribute("onclick", prevOnClick);
}
lastNode.classList.remove('highlight-element');
}
// Save currentNode and preserve any inline event (onclick) attributes
nodeClone = currentNode.cloneNode();
// if currentNode has onclick attribute, disable it
if (currentNode.getAttribute("onclick")) {
currentNode.setAttribute("onclick", "return false;");
};
// Add highlight class to currentNode
currentNode.classList.add('highlight-element');
}
// store node
lastNode = currentNode;
}
}
function clickHandler (e) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
console.log("Clicked Node:\n");
console.log(nodeClone);
}
function cancelNodeSelect (e) {
if (e.keyCode == 27) {
// if lastNode has classList, check for onclick attribute and remove highlight-element class
if (lastNode.classList) {
if (lastNode.getAttribute("onclick") != null) {
prevOnClick = nodeClone.getAttribute("onclick");
lastNode.setAttribute("onclick", prevOnClick);
}
lastNode.classList.remove('highlight-element');
}
// remove event listeners
document.removeEventListener('click', clickHandler, false);
document.removeEventListener('mousemove', nodeHandler, false);
document.removeEventListener('keyup', cancelNodeSelect, false);
console.log("escape pressed");
};
}
document.addEventListener('click', clickHandler, false);
document.addEventListener('mousemove', nodeHandler, false);
document.addEventListener('keyup', cancelNodeSelect, false);
And the css:
.highlight-element {
background-color: rgba(130, 180, 230, 0.4);
outline: solid 1px #0F4D9A;
box-sizing: border-box;
}
This answer was posted as edits https://stackoverflow.com/revisions/33050714/2 and https://stackoverflow.com/revisions/33050714/3 to the question Highlight Deepest Child DOM Element with Div Overlay when Moused Over in Pure Javascript by the OP g_let under CC BY-SA 3.0.
Upvotes: 0