jQuery:如何突出显示鼠标光标下的元素?

19

可能是重复问题:
如何使这个JavaScript运行更快?

我正在尝试在jQuery中创建一个“元素选择器”,就像Firebug一样。基本上,我想突出显示用户鼠标下的元素。这是我目前的进展,但它并没有很好地工作:

$('*').mouseover(function (event) {
    var $this = $(this);
    $div.offset($this.offset()).width($this.width()).height($this.height());
    return false;
});


var $div = $('<div>')
    .css({ 'background-color': 'rgba(255,0,0,.5)', 'position': 'absolute', 'z-index': '65535' })
    .appendTo('body');

基本上,我正在向DOM注入一个具有半透明背景的div。然后我监听每个元素上的mouseover事件,然后移动该div以覆盖该元素。
目前,只要将鼠标移动到页面上就会使整个页面变成红色。我该如何让它更好地运行?
编辑:我相当确定问题在于,一旦我的鼠标触摸页面,就会选择body,然后随着我移动鼠标,高亮显示器不会通过任何瞬间,因为它位于所有内容的上方。

Firebug

在研究 Firebug 的源代码时,我发现了这个:

drawBoxModel: function(el)
{
    // avoid error when the element is not attached a document
    if (!el || !el.parentNode)
        return;

    var box = Firebug.browser.getElementBox(el);

    var windowSize = Firebug.browser.getWindowSize();
    var scrollPosition = Firebug.browser.getWindowScrollPosition();

    // element may be occluded by the chrome, when in frame mode
    var offsetHeight = Firebug.chrome.type == "frame" ? FirebugChrome.height : 0;

    // if element box is not inside the viewport, don't draw the box model
    if (box.top > scrollPosition.top + windowSize.height - offsetHeight ||
        box.left > scrollPosition.left + windowSize.width ||
        scrollPosition.top > box.top + box.height ||
        scrollPosition.left > box.left + box.width )
        return;

    var top = box.top;
    var left = box.left;
    var height = box.height;
    var width = box.width;

    var margin = Firebug.browser.getMeasurementBox(el, "margin");
    var padding = Firebug.browser.getMeasurementBox(el, "padding");
    var border = Firebug.browser.getMeasurementBox(el, "border");

    boxModelStyle.top = top - margin.top + "px";
    boxModelStyle.left = left - margin.left + "px";
    boxModelStyle.height = height + margin.top + margin.bottom + "px";
    boxModelStyle.width = width + margin.left + margin.right + "px";

    boxBorderStyle.top = margin.top + "px";
    boxBorderStyle.left = margin.left + "px";
    boxBorderStyle.height = height + "px";
    boxBorderStyle.width = width + "px";

    boxPaddingStyle.top = margin.top + border.top + "px";
    boxPaddingStyle.left = margin.left + border.left + "px";
    boxPaddingStyle.height = height - border.top - border.bottom + "px";
    boxPaddingStyle.width = width - border.left - border.right + "px";

    boxContentStyle.top = margin.top + border.top + padding.top + "px";
    boxContentStyle.left = margin.left + border.left + padding.left + "px";
    boxContentStyle.height = height - border.top - padding.top - padding.bottom - border.bottom + "px";
    boxContentStyle.width = width - border.left - padding.left - padding.right - border.right + "px";

    if (!boxModelVisible) this.showBoxModel();
},

hideBoxModel: function()
{
    if (!boxModelVisible) return;

    offlineFragment.appendChild(boxModel);
    boxModelVisible = false;
},

showBoxModel: function()
{
    if (boxModelVisible) return;

    if (outlineVisible) this.hideOutline();

    Firebug.browser.document.getElementsByTagName("body")[0].appendChild(boxModel);
    boxModelVisible = true;
}

看起来他们正在使用标准的div + css来绘制它......现在只需要弄清楚他们如何处理事件...(这个文件有28K行)
还有这个片段,我猜它检索适当的对象....虽然我无法弄清楚。他们正在寻找一个类"objectLink-element"......我不知道这个"repObject"是什么。
onMouseMove: function(event)
{
    var target = event.srcElement || event.target;

    var object = getAncestorByClass(target, "objectLink-element");
    object = object ? object.repObject : null;

    if(object && instanceOf(object, "Element") && object.nodeType == 1)
    {
        if(object != lastHighlightedObject)
        {
            Firebug.Inspector.drawBoxModel(object);
            object = lastHighlightedObject;
        }
    }
    else
        Firebug.Inspector.hideBoxModel();

},

我在思考,也许当高亮节点的mousemove或mouseover事件触发时,我可以将它传递给它覆盖的节点?


如果我将一个不可见的元素放在每个比我的高亮显示器具有更高 z-index 值的元素上方,但是给我的高亮显示器一个比实际元素更高的 z-index 值……理论上,它应该可以工作。不可见元素将触发鼠标事件,但高亮效果仍然看起来像是在实际元素的顶部。
这意味着我刚刚增加了 DOM 元素的数量,并且位置必须正确。除非也许我只“抬起”高亮显示器当前覆盖的元素??但这仍然可能是每个元素 >.< 有人帮帮我!

3
尝试做这样的事情有一个非常棘手的困境。基本上,一旦你在鼠标下方绘制出东西,因为鼠标悬停在元素上方,它实际上离开了原始元素,现在悬停在_新的_元素上。 - Matt Ball
@Matt:那我们该怎么解决这个问题呢?Firebug不是通过操作DOM来渲染这些框吗?还是它有自己的渲染引擎? - mpen
1
Firebug在鼠标悬停在网页元素上时会在Firebug中进行单独突出显示。或者这是陈词滥调?当您使用“点击检查”时,它只会勾勒出元素的轮廓,这可能是问题的关键所在 - Firebug可以绘制4个单独的元素,每个边框一个,这样,我提到的进退两难根本不是问题。 - Matt Ball
2
+1 为挖掘 Firebug 源代码点赞 - 我本来就想建议这个的。 - Matt Ball
5个回答

26

所有这些答案都太复杂了... 简单的解决方案:

Javascript:

prevElement = null;
document.addEventListener('mousemove',
    function(e){
        var elem = e.target || e.srcElement;
        if (prevElement!= null) {prevElement.classList.remove("mouseOn");}
        elem.classList.add("mouseOn");
        prevElement = elem;
    },true);

Css:

.mouseOn{
  background-color: #bcd5eb !important;
  outline: 2px solid #5166bb !important;
}

3
为了正确地撤销背景颜色,修改David的答案如下...
$('body *').live('mouseover mouseout', function(event) {
    if (event.type == 'mouseover') {
        $(this).data('bgcolor', $(this).css('background-color'));
        $(this).css('background-color','rgba(255,0,0,.5)');
    } else {
        $(this).css('background-color', $(this).data('bgcolor'));
    }
    return false;
});

2

我可以提供另一种替代方案吗?

如何将 background-color 分配给页面的所有子元素,然后在 hover() 上调整 元素的 background-color 以增加对比度?

$('html').children().css('background-color','rgba(0,0,0,0.2)');
$('body').children().hover(
    function(){
        $(this).css('background-color','#fff');
    },
    function(){
        $(this).css('background-color','rgba(0,0,0,0.2)');
    });

JS Fiddle demo.


可以这样做。如果找不到解决方案,我可能不得不采用这种方法,但这真的很困扰我,因为我知道一定有办法可以做到。 - mpen

1

► 这里

稍微不那么烦人,但它无法在将鼠标从父元素移动到其子元素之前完全将鼠标移开父元素的情况下检测到移动。
var $div = $('<div id="highlighter">').css({
    'background-color': 'rgba(255,0,0,.5)',
    'position': 'absolute',
    'z-index': '65535'
}).hide().prependTo('body');

var $highlit;

$('*').live('mousemove', function(event) {
    if (this.nodeName === 'HTML' || this.nodeName === 'BODY')
    {
        $div.hide();
        return false;
    }
    var $this = this.id === 'highligher' ? $highlit : $(this),

        x = event.pageX,
        y = event.pageY,

        width = $this.width(),
        height = $this.height(),
        offset = $this.offset(),

        minX = offset.left,
        minY = offset.top,
        maxX = minX + width,
        maxY = minY + height;

    if (this.id === 'highlighter')
    {
        if (minX <= x && x <= maxX
            && minY <= y && y <= maxY)
        {
            // nada
        }
        else
        {
            $div.hide();
        }
    }
    else
    {
        $highlit = $this;
        $div.offset(offset).width($this.width()).height($this.height()).show();
    }
    return false;
});

希望这能帮助你开始。你可以调整我写的内容,使用{{link1:document.elementFromPoint(x,y)}}来检查鼠标是否移动到当前高亮元素的子元素中。我现在还没清醒过来,无法想出解决方法。
或者,如果对你来说,轮廓线和高亮一样好用,你可以尝试我在原问题评论中提到的方法。在当前悬停元素周围绘制4个div - 每个轮廓框边缘一个div。不再在你和实际内容之间绘制元素!

*我敢打赌,document.elementFromPoint(x, y)会让获取鼠标下的元素变得更加容易


还差一点...还需要一些工作。我会看看我能做什么。 - mpen

1

当你将鼠标悬停在页面上时,整个页面变成红色的原因是你的代码匹配了 body 元素的 mouseover 事件。你可以通过仅选择 body 的子元素来阻止这种情况发生。

$('body *').bind( //...

如果页面上有大量元素,您可能会遇到性能问题,因为bind将为每个匹配的元素附加一个侦听器。尝试查看jQuery的事件委派API(.delegate()http://api.jquery.com/delegate/),它允许您侦听向根元素传播的事件,并且适用于鼠标事件。


这并没有真正解决问题(使用body *),只是将问题转移到稍深的元素。请查看马特的评论。目前还不太担心性能,我会接受任何可用的东西。 - mpen
另外,我不使用live()或delegate()的原因是因为未来的元素将被包括在内...也就是说,我不希望它开始选择自身(这就是为什么我在绑定事件后附加它的原因)。 - mpen
你尝试过检查传递给回调函数的事件对象中的currentTarget吗?如果currentTarget是你的高亮div,则可以不做任何操作。 - andrewle
不管我是否什么都不做,它仍然会阻止“真实”元素接收事件。但是,如果我能解决其他问题,那么这将允许我使用live() - mpen

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