如何在拖动时触发鼠标悬停事件

39

当我将一个元素拖动到另一个具有mouseover事件的

上时,该事件不会触发。但是,如果我悬停在其上而不拖动,则可以正常工作。

如果我拖动另一个元素到它上面,有没有一种方法来检测它上面的hover事件?


你在使用jQuery UI吗? - Purag
不,我正在使用自定义创建的拖拽。 - V.Rashkov
能否提供代码呢?可以将其放在问题中或粘贴到jsfiddle中。 - Purag
4
如果鼠标悬停在被另一元素覆盖的元素上,mouseover事件将不会触发(除非覆盖元素是该元素的子元素,在这种情况下,它会冒泡)。很抱歉,您需要通过X和Y位置来实现您想要的效果。 - Nathan MacInnes
1
X和Y坐标是非常繁琐且容易出错的工作。只需将拖动的元素放在光标旁边,这样它就不会阻碍鼠标与其后面的元素交互。 - Munter
7个回答

17
在所有提供的答案中,我没有看到最简单和明显的一个(也许是我在 OP 的问题中漏掉了什么)。但是,如果有人以后遇到这个问题,并需要快速简单的纯 JS 解决方案...
你可以通过更改元素的 className 属性 ondragover,然后再改回原始的 class ondragleave 来解决它。
my_element.ondragover = function(ev) {  
 ev.preventDefault();  
 this.className = 'myElem_dragover';  
}  
my_element.ondragleave = function(ev) {  
 ev.preventDefault();  
 this.className = 'myElem_orig';  
}

CSS(层叠样式表)
.myElem_orig {     //this is your initial class for element
  top: 30px;
  left: 20px;
  .....
  background-color: blue;  
}  

.myElem_orig:hover {   //this is hover state, just changing bg color
  background-color: red;
}

.myElem_dragover { //new class, needs all attributes from original class
  top: 30px;
  left: 20px;
  ........ 
  background-color: red; //behaves the same like hover does
}

编辑:
忘记提到了,你需要同时恢复原始的类名 ondrop,否则 div 元素会一直保持在 dragover 类名下


1
我认为这个问题早于内置的HTML可拖动属性的广泛使用,除非您正在进行自定义行为,否则这绝对是最简单的方法。 - Grae Kindel
拖放的一个缺点是,据我所知,它无法确定您悬停在元素的哪一侧。比如说,如果您正在对项目列表进行拖放排序,如果您悬停在项目的上半部分,您希望拖动的内容被放置在该项目的上方。但是您只能看到您正在悬停在该项目上,而无法确定位置。 - Kelderic
dragOver事件中,您可以测试鼠标位于上半部分还是下半部分,并根据此作出决策。使用object.getBoundingClientRect(),例如,您可以获得底部边界并从中减去鼠标的Y坐标。您会得到一个比对象高度/2大或小的值。 - Wolf War
有点有趣,我刚刚发布了一个关于那个问题的问题,你在这里的评论可能已经回答了它。我会进一步调查dragover事件,谢谢。 - Kelderic
@AndyMercer 如果你认为getBoundingClientRect()是拖放API的一部分,那么你就错了。实际上,在dragOver事件中,你可以测试许多东西,其中之一就是边界矩形,并使用纯数学计算位置。 - Wolf War
显示剩余3条评论

13

这里是使用X-Y坐标解决方案的示例。

在jsfiddle上的工作示例

示例可以改进,但是是一个很好的起点。

简单地跟踪鼠标位置并检查它是否出现在任何可拖放对象的边界框内。因此,如果mouseup事件在其中任何一个上触发,则拖动的对象将被放下。

您还可以使用您正在拖动的对象的坐标来检测它是否在可投放框上,但这需要一些更多的代码来查找边界框坐标,而对于我来说,使用鼠标就足够了。

该代码使用jQuery,但不使用jQueryUI。 我在Chrome、Firefox和Opera中进行了测试,但没有在IE中测试 :)

如果jsfiddle无法访问,我也将代码添加到此处。

HTML

<p>Drag orange boxes to grey ones</p>
<div class="droppable"></div>
<div class="droppable"></div>
<div class="droppable"></div>
<div class="droppable"></div>

<div class="draggable"></div>
<div class="draggable"></div>
<div class="draggable"></div>
CSS
.droppable {
    width:50px;
    height:50px;
    float: left;
    background-color: #DDD;
    margin: 5px;
}

.draggable {
    width:40px;
    height:40px;
    float: right;
    background-color: #FC0;
    margin: 5px;
    cursor: pointer;
}

.dropped {
    background-color: #FC0;
}

.somethingover {
    background-color: #FCD;
}

JavaScript

var dragged, mousex, mousey, coordinates = [];

var continueDragging = function(e) {
    // Change the location of the draggable object
    dragged.css({
        "left": e.pageX - (dragged.width() / 2),
        "top": e.pageY - (dragged.height() / 2)
    });

    // Check if we hit any boxes
    for (var i in coordinates) {
        if (mousex >= coordinates[i].left && mousex <= coordinates[i].right) {
            if (mousey >= coordinates[i].top && mousey <= coordinates[i].bottom) {
                // Yes, the mouse is on a droppable area
                // Lets change the background color
                coordinates[i].dom.addClass("somethingover");
            }
        } else {
            // Nope, we did not hit any objects yet
            coordinates[i].dom.removeClass("somethingover");
        }
    }

    // Keep the last positions of the mouse coord.s
    mousex = e.pageX;
    mousey = e.pageY;
}

var endDragging = function(e) {
    // Remove document event listeners
    $(document).unbind("mousemove", continueDragging);
    $(document).unbind("mouseup", endDragging);

    // Check if we hit any boxes
    for (var i in coordinates) {
        if (mousex >= coordinates[i].left && mousex <= coordinates[i].right) {
            if (mousey >= coordinates[i].top && mousey <= coordinates[i].bottom) {
                // Yes, the mouse is on a droppable area
                droptarget = coordinates[i].dom;
                droptarget.removeClass("somethingover").addClass("dropped");
                dragged.hide("fast", function() {
                    $(this).remove();
                });
            }
        }
    }

    // Reset variables
    mousex = 0;
    mousey = 0;
    dragged = null;
    coordinates = [];
}

var startDragging = function(e) {
    // Find coordinates of the droppable bounding boxes
    $(".droppable").each(function() {
        var lefttop = $(this).offset();
        // and save them in a container for later access
        coordinates.push({
            dom: $(this),
            left: lefttop.left,
            top: lefttop.top,
            right: lefttop.left + $(this).width(),
            bottom: lefttop.top + $(this).height()
        });
    });

    // When the mouse down event is received
    if (e.type == "mousedown") {
        dragged = $(this);
        // Change the position of the draggable
        dragged.css({
            "left": e.pageX - (dragged.width() / 2),
            "top": e.pageY - (dragged.height() / 2),
            "position": "absolute"
        });
        // Bind the events for dragging and stopping
        $(document).bind("mousemove", continueDragging);
        $(document).bind("mouseup", endDragging);
    }
}

// Start the dragging
$(".draggable").bind("mousedown", startDragging);

一开始我有点怀疑,但这种技术对我非常有效 - 非常感谢! - OrganicPanda

4

有两种基本方法可以做到这一点:

  1. 跟踪mousemove并根据x/y坐标做出反应
  2. 拥有一个透明的目标,其z-index高于拖动容器

第一种选择实际上根本不使用mouseover事件,但会给你相同的结果。

请注意,某些浏览器(ie)不会在透明元素上触发mouseover事件,因此您必须通过设置透明的背景图像或将随机图像设置为背景并将其定位在元素外面来伪造它,就像这样:

element {
 background: url(/path/to/img) no-repeat -10000px 0;
}

这可能会干扰正在拖动的元素,具体取决于他的设置方式。 - Purag

1

1

解决拖动元素阻塞下方元素的hover或mouseenter事件的另一个可能方法是:

pointer-events: none;

如果将此应用于拖动元素,则下方元素上的hover仍然可以正常工作。


1
通过修改emrahgunduz发布的代码,特别是for循环,您还可以管理嵌套的可放置区域。
var dragged, mousex, mousey, coordinates = [];

var continueDragging = function(e) {
    // Change the location of the draggable object
    dragged.css({
        "left": e.pageX - (dragged.width() / 2),
        "top": e.pageY - (dragged.height() / 2)
    });

    // Check if we hit any boxes
    for (var i = coordinates.length - 1; i >= 0; i--) {
        if (mousex >= coordinates[i].left && mousex <= coordinates[i].right) {
            if (mousey >= coordinates[i].top && mousey <= coordinates[i].bottom) {
                // Yes, the mouse is on a droppable area
                // Lets change the background color
                $('.droppable').removeClass("somethingover");
                coordinates[i].dom.addClass("somethingover");
                break;
            }
        } else {
            // Nope, we did not hit any objects yet
            coordinates[i].dom.removeClass("somethingover");
        }
    }

    // Keep the last positions of the mouse coord.s
    mousex = e.pageX;
    mousey = e.pageY;
};

var endDragging = function(e) {
    // Remove document event listeners
    $(document).unbind("mousemove", continueDragging);
    $(document).unbind("mouseup", endDragging);

    // Check if we hit any boxes
    for (var i = coordinates.length - 1; i >= 0; i--) {
        if (mousex >= coordinates[i].left && mousex <= coordinates[i].right) {
            if (mousey >= coordinates[i].top && mousey <= coordinates[i].bottom) {
                // Yes, the mouse is on a droppable area
                droptarget = coordinates[i].dom;
                droptarget.removeClass("somethingover").addClass("dropped");
                dragged.hide("fast", function() {
                    $(this).remove();
                });
            }
        }
    }

    // Reset variables
    mousex = 0;
    mousey = 0;
    dragged = null;
    coordinates = [];
};

var startDragging = function(e) {
    // Find coordinates of the droppable bounding boxes
    $(".droppable").each(function() {
        var lefttop = $(this).offset();
        // and save them in a container for later access
        coordinates.push({
        dom: $(this),
        left: lefttop.left,
        top: lefttop.top,
        right: lefttop.left + $(this).width(),
        bottom: lefttop.top + $(this).height()
    });
};

// When the mouse down event is received
if (e.type == "mousedown") {
    dragged = $(this);
    // Change the position of the draggable
    dragged.css({
        "left": e.pageX - (dragged.width() / 2),
        "top": e.pageY - (dragged.height() / 2),
        "position": "absolute"
    });
    // Bind the events for dragging and stopping
    $(document).bind("mousemove", continueDragging);
    $(document).bind("mouseup", endDragging);
}

// Start the dragging
$(".draggable").bind("mousedown", startDragging);

0
在 jsfiddle 的示例中发现一个小 bug。 当你垂直地离开放置区域时,该区域仍然具有“somethinghover”类。

http://jsfiddle.net/MAazv

替换这个

if (mousex >= coordinates[i].left && mousex <= coordinates[i].right) {
  if (mousey >= coordinates[i].top && mousey <= coordinates[i].bottom) {
    // Yes, the mouse is on a droppable area
    // Lets change the background color
    coordinates[i].dom.addClass("somethingover");
  }
} else {
  // Nope, we did not hit any objects yet
  coordinates[i].dom.removeClass("somethingover");
}

http://jsfiddle.net/MAazv/122

使用这个:

if (mousex >= coordinates[i].left && mousex <= coordinates[i].right && mousey >= coordinates[i].top && mousey <= coordinates[i].bottom) {
  // Yes, the mouse is on a droppable area
  // Lets change the background color
  coordinates[i].dom.addClass("somethingover");
} else {
  // Nope, we did not hit any objects yet
  coordinates[i].dom.removeClass("somethingover");
}


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