如何使用叠加效果高亮显示鼠标光标下的元素?

时间:2011-01-17 07:47:18

标签: javascript jquery optimization performance

仍在尝试回答this question,我认为我终于找到了一个解决方案,但运行得太慢了。

var $div = $('<div>')
    .css({ 'border': '1px solid red', 'position': 'absolute', 'z-index': '65535' })
    .appendTo('body');

$('body *').live('mousemove', function(e) {
    var topElement = null;
    $('body *').each(function() {
        if(this == $div[0]) return true;
        var $elem = $(this);
        var pos = $elem.offset();
        var width = $elem.width();
        var height = $elem.height();
        if(e.pageX > pos.left && e.pageY > pos.top
            && e.pageX < (pos.left + width) && e.pageY < (pos.top + height)) {
            var zIndex = document.defaultView.getComputedStyle(this, null).getPropertyValue('z-index');
            if(zIndex == 'auto') zIndex = $elem.parents().length;
            if(topElement == null || zIndex > topElement.zIndex) {
                topElement = {
                    'node': $elem,
                    'zIndex': zIndex
                };
            }

        }
    });
    if(topElement != null ) {
        var $elem = topElement.node;
        $div.offset($elem.offset()).width($elem.width()).height($elem.height());
    }
});

它基本上遍历页面上的所有元素,并找到光标下方的最顶层元素。

是否有某种方法可以使用四叉树或其他东西并对页面进行分段以使循环运行得更快?

2 个答案:

答案 0 :(得分:18)

  

是否有某种方法可以使用四叉树或其他东西并对页面进行分段以使循环运行得更快?

稍微退一步,意识到问题的严重程度,并且你尝试的答案越难,你将会使用更复杂的答案。

现在您需要做的是为突出显示创建4个元素。它们将形成一个空方,因此您的鼠标事件可以自由发射。这类似于我所做的overlay example

不同之处在于您只需要四个元素(没有调整大小标记),并且4个框的大小和位置有点不同(模仿红色边框)。然后,您可以在事件处理程序中使用event.target,因为默认情况下它会获得真正的最顶层元素。

另一种方法是隐藏exra元素,获取elementFromPoint,然后计算然后将其放回去。

我告诉你,它们比光更快。甚至爱因斯坦都会同意:)

1。)elementFromPoint overlay / borders - [Demo1] FF需要v3.0 +

var box = $("<div class='outer' />").css({
  display: "none", position: "absolute", 
  zIndex: 65000, background:"rgba(255, 0, 0, .3)"
}).appendTo("body");

var mouseX, mouseY, target, lastTarget;

// in case you need to support older browsers use a requestAnimationFrame polyfill
// e.g: https://gist.github.com/paulirish/1579671
window.requestAnimationFrame(function frame() {
  window.requestAnimationFrame(frame);
    if (target && target.className === "outer") {
        box.hide();
        target = document.elementFromPoint(mouseX, mouseY);
    }
    box.show();   

    if (target === lastTarget) return;

    lastTarget = target;
    var $target = $(target);
    var offset = $target.offset();
    box.css({
        width:  $target.outerWidth()  - 1, 
        height: $target.outerHeight() - 1, 
        left:   offset.left, 
        top:    offset.top 
    });
});

$("body").mousemove(function (e) {
    mouseX = e.clientX;
    mouseY = e.clientY;
    target = e.target;
});

2。)鼠标悬停边框 - [Demo2]

var box = new Overlay();

$("body").mouseover(function(e){
  var el = $(e.target);
  var offset = el.offset();
  box.render(el.outerWidth(), el.outerHeight(), offset.left, offset.top);
});​

/**
 * This object encapsulates the elements and actions of the overlay.
 */
function Overlay(width, height, left, top) {

    this.width = this.height = this.left = this.top = 0;

    // outer parent
    var outer = $("<div class='outer' />").appendTo("body");

    // red lines (boxes)
    var topbox    = $("<div />").css("height", 1).appendTo(outer);
    var bottombox = $("<div />").css("height", 1).appendTo(outer);  
    var leftbox   = $("<div />").css("width",  1).appendTo(outer);
    var rightbox  = $("<div />").css("width",  1).appendTo(outer);

    // don't count it as a real element
    outer.mouseover(function(){ 
        outer.hide(); 
    });    

    /**
     * Public interface
     */

    this.resize = function resize(width, height, left, top) {
      if (width != null)
        this.width = width;
      if (height != null)
        this.height = height;
      if (left != null)
        this.left = left;
      if (top != null)
        this.top = top;      
    };

    this.show = function show() {
       outer.show();
    };

    this.hide = function hide() {
       outer.hide();
    };     

    this.render = function render(width, height, left, top) {

        this.resize(width, height, left, top);

        topbox.css({
          top:   this.top,
          left:  this.left,
          width: this.width
        });
        bottombox.css({
          top:   this.top + this.height - 1,
          left:  this.left,
          width: this.width
        });
        leftbox.css({
          top:    this.top, 
          left:   this.left, 
          height: this.height
        });
        rightbox.css({
          top:    this.top, 
          left:   this.left + this.width - 1, 
          height: this.height  
        });

        this.show();
    };      

    // initial rendering [optional]
    // this.render(width, height, left, top);
}

答案 1 :(得分:1)

首先,我不认为做$('body *').live是一个非常好的主意,它似乎非常昂贵(考虑一下每次移动鼠标时浏览器必须进行的计算)

那就是说,这是一个忽略了这个方面的优化版本

var $div = $('<div>')
    .css({ 'border': '1px solid red', 'position': 'absolute', 'z-index': '65535' })
    .appendTo('body');

$('body *').live('mousemove', function(e) {
    var topElement = null;
    var $bodyStar = $('body *');
    for(var i=0,elem;elem=$bodyStar[i];i++) {
        if(elem == $div[0]) continue;
        var $elem = $(elem);
        var pos = $elem.offset();
        var width = $elem.width();
        var height = $elem.height();
        if(e.pageX > pos.left && e.pageY > pos.top && e.pageX < (pos.left + width) && e.pageY < (pos.top + height)) {
            var zIndex = document.defaultView.getComputedStyle(this, null).getPropertyValue('z-index');
            if(zIndex == 'auto') zIndex = $elem.parents().length;
            if(topElement == null || zIndex > topElement.zIndex) {
                topElement = {
                    'node': $elem,
                    'zIndex': zIndex
                };
            }

        }
    }
    if(topElement != null) {
        var $elem = topElement.node;
        $div.offset($elem.offset()).width($elem.width()).height($elem.height());
    }
});

为了将来参考,如果您需要性能,请不要使用jQuerys循环机制。它们都是围绕每次迭代的函数调用构建的,与普通循环相比这是非常慢的,因为在执行函数调用时发生的调用堆栈启动对于您需要执行的大多数迭代操作来说是一个巨大的开销。

更新代码以修复错误,并允许动态插入元素。

相关问题