JavaScript - 鼠标悬停高亮显示元素

7

我正在编写一段JavaScript代码,当用户将鼠标悬停在DOM中的某个元素上时,它可以对该元素进行高亮。这应该是一个支持跨浏览器的外部插件,理想情况下,我试图模仿浏览器检查工具的行为。

我不能说没有成功,但我陷入了两难境地,每种方法都有其优缺点。

方式1

我处理mouseover事件,并简单地向目标元素添加一个边框。当我悬停在另一个元素上时,我只需重置现有的高亮元素即可。以下是相应的代码:

function addHighlight(target) {
    target.classList.add('highlighted');
}

function removeHighlight(target) {
    target.classList.remove('highlighted');
}

window.addEventListener('mouseover',function(e) {
    addHighlight(e.target);
});

window.addEventListener('mouseout',function(e) {
    removeHighlight(e.target);
});

示例

该方法的优点

它完全可以正常工作。

该方法的缺点

由于我在现有DOM元素上添加了边框,因此它会重新排列页面上的元素,并且您可以观察到元素的轻微晃动效果。不太美观。

第二种方法

我希望高亮显示是无缝的。也就是说,保留页面的外观,只需在元素上方叠加一个高亮掩码即可。

为了实现这一点,在mouseover事件中,我动态创建了一个掩码元素,其position设置为absolute,并将其坐标设置为目标元素的精确坐标。以下是我的代码:

window.addEventListener('mouseover',function(e) {
    applyMask(e.target);
});

function applyMask(target) {
    if(document.getElementsByClassName('highlight-wrap').length > 0) {
        resizeMask(target);
    }else{
        createMask(target);
    }
}

function resizeMask(target) {
    var rect = target.getBoundingClientRect();
    var hObj = document.getElementsByClassName('highlight-wrap')[0];
    hObj.style.top=rect.top+"px";
    hObj.style.width=rect.width+"px";
    hObj.style.height=rect.height+"px";
    hObj.style.left=rect.left+"px";
   // hObj.style.WebkitTransition='top 0.2s';
}

function createMask(target) {
    var rect = target.getBoundingClientRect();
    var hObj = document.createElement("div");
    hObj.className = 'highlight-wrap';
    hObj.style.position='absolute';
    hObj.style.top=rect.top+"px";
    hObj.style.width=rect.width+"px";
    hObj.style.height=rect.height+"px";
    hObj.style.left=rect.left+"px";
    hObj.style.backgroundColor = '#205081';
    hObj.style.opacity='0.5';
    hObj.style.cursor='default';
    //hObj.style.WebkitTransition='top 0.2s';
    document.body.appendChild(hObj);
}

function clearMasks() {
    var hwrappersLength = document.getElementsByClassName("highlight-wrap").length;
    var hwrappers = document.getElementsByClassName("highlight-wrap");
    if(hwrappersLength > 0) {
        for(var i=0; i<hwrappersLength; i++) {
            console.log("Removing existing wrap");
            hwrappers[i].remove();
        }
    }
}

这里有一个可运行的示例

此方法的优点 我认为这更加简洁,不会影响页面,只是在元素上覆盖一层遮罩。

缺点

当用户将鼠标悬停在最顶部的容器(div)上时,它为该元素创建了遮罩。之后,所有后续的mouseover事件都被忽略,因为它们是在遮罩上注册的,而不是在实际的底层元素上注册的。我需要找到一个解决这个问题的方法。

有谁能帮我改进第二种方法?或者提出另一种方法?

谢谢, Sriram


“当我给现有的DOM元素添加边框时,它会重新排列页面上的元素,你可以观察到元素的轻微晃动效果。” - 使用outline代替边框。 - CBroe
6个回答

11
你应该在CSS中完成这个操作而不是在JS中。使用:hover选择器。
.your-class:hover{
    background-color: #205081;
}

谢谢,但是这里的“your-class”可以是任何东西。是否有可能向body中的所有元素添加伪选择器?这样就可以在任何屏幕上使用它了。 - Sriram Sridharan
1
@sriramsridharan 只需将.you-class替换为* - jonatjano
2
它会突出树中的所有内容,如果您只想突出显示最接近的元素怎么办? - Jepser Bernardino

6

我会继续采用这种方法,但是我不希望“点击”事件在我的基础元素上被注册。 我在我的单击事件侦听器中添加了e.preventDefault(),但它不起作用。 我已更新了fiddle,你能告诉我怎么做吗? - Sriram Sridharan
@sriramsridharan,您能否重新表达一下您的评论,我无法理解 :/(问题可能是我不是英语母语)。我的理解是您不希望触发任何点击事件。 - jonatjano
就像这样。假设我将鼠标悬停在“登录”按钮上,蒙版会出现在登录按钮上方。由于蒙版的pointerEvents属性设置为“none”,如果我执行单击操作,则底层的“登录”按钮将接收到点击事件并提交表单。我的目的是防止基础元素仅接收“click”(和其他一些)事件,同时仍保留您向我展示的突出显示蒙版的方法。 - Sriram Sridharan
就像我们的Chrome检查器一样。当您检查一个元素并单击它时,单击不会注册。相反,对应的元素将在检查器的源选项卡中突出显示。我希望有类似于此的东西。 - Sriram Sridharan
@sriramsridharan 抱歉,找不到如何做:/ - jonatjano

2

我通过@jonatjano更改了版本,如下:

  • 它考虑了视口滚动
  • 它更紧凑
  • 它也可以在开发者控制台中粘贴使用(请注意iframe的混淆)

这里查看

window.addEventListener('mouseover', function (e) {
    updateMask(e.target);
});

function updateMask(target) {
    let elements = document.getElementsByClassName("highlight-wrap")
    let hObj
    if (elements.length !== 0) {
        hObj = elements[0]
    } else {
        hObj = document.createElement("div");
        hObj.className = 'highlight-wrap';
        hObj.style.position = 'absolute';
        hObj.style.backgroundColor = '#205081';
        hObj.style.opacity = '0.5';
        hObj.style.cursor = 'default';
        hObj.style.pointerEvents = 'none';
        document.body.appendChild(hObj);
    }
    let rect = target.getBoundingClientRect();
    hObj.style.left = (rect.left + window.scrollX) + "px";
    hObj.style.top = (rect.top + window.scrollY) + "px";
    hObj.style.width = rect.width + "px";
    hObj.style.height = rect.height + "px";

}

1
很好用。以前的版本在滚动方面存在问题。 - Kim Smith

1
@LouieAlmeda和@jonatjano都是正确的,我只想补充一点,如果你不喜欢重新排列页面上的元素,你可以给元素添加border: 1px solid transparent,然后在鼠标悬停时更改边框颜色。

这可能行不通,因为我无法控制网页的源CSS。你提出的做法假设我也编写了目标页面,但事实并非如此。 - Sriram Sridharan

0

你也可以使用:

[attribute = value]:hover {
   css declarations;
}

并将其应用于所有需要悬停效果的 div 元素。


0

这种方法对我很有效,将 hover 规则应用于所有的 * 元素。

*:hover {
    background-color: #205081 !important;
}

!important确保覆盖在各个HTML节点上设置的背景。请注意CSS特定性,即如果悬停元素上存在类似的!important规则,则浏览器将忽略此CSS规则。

感谢Louie扩展他的答案。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接