Подтвердить что ты не робот

Как эффективно выделить элемент под курсором мыши с наложением?

По-прежнему пытаюсь ответить на этот вопрос, и я думаю, что наконец нашел решение, но оно работает слишком медленно.

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

Он в основном прорисовывает все элементы на странице и находит самый верхний элемент под курсором.

Можно ли каким-то образом использовать квад-дерево или что-то еще и сегментировать страницу, чтобы цикл работал быстрее?

4b9b3361

Ответ 1

Можно ли каким-то образом использовать квад-дерево или что-то еще и сегментировать страницу, чтобы цикл работал быстрее?

Просто немного отступите, осознайте, насколько мала проблема, и что чем сложнее вы попытаетесь использовать более сложный ответ, который вы будете использовать.

Теперь вам нужно создать 4 элемента для выделения. Они образуют пустой квадрат, и поэтому ваши события мыши могут запускаться. Это похоже на этот пример перекрытия, который я сделал.

Разница в том, что вам нужны только четыре элемента (без маркеров размера), а размер и положение четырех ящиков немного отличаются (чтобы имитировать красную рамку). Затем вы можете использовать event.target в обработчике событий, потому что по умолчанию он получает реальный верхний элемент.

Другой подход состоит в том, чтобы скрыть элемент exra, получить elementFromPoint, вычислить и вернуть его обратно.

Они быстрее, чем свет, я могу вам сказать. Даже Эйнштейн согласился бы:)

1.) elementFromPoint overlay/border - [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);
}

Ответ 2

Во-первых, я не думаю, что делать $('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, если вам нужна производительность. Все они строят вокруг вызовов функций для каждой итерации, что очень медленно по сравнению с обычным циклом, поскольку инициирование стека вызовов, которое происходит при вызове функции, является огромной накладной для большинства операций итерации, которые вам нужно выполнить.

Код обновлен, чтобы исправить ошибки и включить динамически вставленные элементы.