当鼠标悬停在纯Javascript上时,用Div覆盖突出显示最深的子DOM元素

Highlight Deepest Child DOM Element with Div Overlay when Moused Over in Pure Javascript

本文关键字:显示 元素 DOM 覆盖 Div 悬停 鼠标 Javascript 上时      更新时间:2023-09-26

简写:

我想在纯JavaScript中模仿Google Chrome的Inspect Element工具。

长版本:

我正在为谷歌浏览器的扩展工作。我想在纯JavaScript 中实现类似于Inspect Element工具的东西(没有jQuery或任何其他库/框架/依赖项)。如果有人使用AdBlock,我特别想模仿"在此页面上阻止广告"。允许用户在页面上选择一个元素的功能。

当扩展处于活动状态时,用户应该能够:

  • 鼠标悬停在元素上
  • 如果一个带有'CODE' element nested inside的"P"元素被鼠标悬停,整个"P"元素应该有"DIV"覆盖在它上面
  • 如果鼠标移动到嵌套的'CODE' element上,它应该重新调整'DIV'覆盖的大小,以覆盖更深嵌套的'CODE' element
  • 当点击时,底层元素应该被存储,'DIV'覆盖隐藏

为什么使用'DIV'覆盖,而不是只是添加一个类的元素,添加边框/轮廓/背景色?

覆盖层阻止用户直接与元素交互。如果有一个href/onClick属性,我不希望它在点击时激活。

你能不能写一些东西来存储所有内联的'onclick'属性,将它们设置为null/返回false;然后恢复原来的'onclick'代码?

试过:

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");
}

不幸的是,它不能处理给定页面上的任何不可见事件侦听器。

我的当前代码格式化粘贴到控制台:

// 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);

请随意在本页上进行测试(刷新页面以取消),目前我通过在' mousmove '事件上交替显示/隐藏覆盖'DIV'来进行覆盖工作。理想情况下,我希望它运行顺利Chrome的检查元素,并给我的元素数据。任何想法都是值得赞赏的,但我必须强调的是,我真的想用纯JavaScript来做这件事。

document.elementFromPoint(x,y)求解

此代码将工作粘贴在Chrome的控制台,按ESC取消。

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);

和css:

.highlight-element {
    background-color: rgba(130, 180, 230, 0.4);
    outline: solid 1px #0F4D9A;
    box-sizing: border-box;
}

这个答案是作为编辑https://stackoverflow.com/revisions/33050714/2和https://stackoverflow.com/revisions/33050714/3发布的问题,当鼠标悬停在纯Javascript中,OP g_let在CC by - sa 3.0下,用Div覆盖突出显示最深的子DOM元素。