为什么 Raphael 中的拖动会因在气泡阶段的封闭元素中停止 mousemove 的传播而被破坏?
我正在尝试调试复杂的 Web 应用程序中的事件处理错误,但我已将问题简化为一个简单的示例,该示例演示了我感到困惑的行为。
我的示例页面基于 Raphaël 的一个示例,如下所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Raphaël · Drag-n-drop Example</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script src="http://github.com/DmitryBaranovskiy/raphael/raw/master/raphael-min.js"></script>
<script>
window.onload = function () {
$('body').mousemove(function(e) {
// console.log("in body's mousemove");
// Uncommenting the next line stops the move
// function below from being called:
// e.stopPropagation();
});
var R = Raphael(0, 0, "100%", "100%");
var r = R.circle(100, 100, 50).attr({fill: "hsb(0, 1, 1)", stroke: "none", opacity: .5});
var start = function () {
this.ox = this.attr("cx");
this.oy = this.attr("cy");
this.animate({r: 70, opacity: .25}, 500, ">");
},
move = function (dx, dy) {
// console.log("in move function for the circle", this);
this.attr({cx: this.ox + dx, cy: this.oy + dy});
},
up = function () {
this.animate({r: 50, opacity: .5}, 500, ">");
};
r.drag(move, start, up);
};
</script>
</head>
<body>
<div id="holder"></div>
</body>
</html>
该版本按预期工作 - 您可以拖动红色圆圈 - 但取消注释 e.stopPropagation()
换行符拖动。如果您有 Firebug 或 Chrome 并取消注释 console.log
行,您可以看到 move
函数从未被调用 - 不知怎的 mousemove
处理程序因为在事件到达圆的处理程序之前调用 body
元素。
我不明白这是怎么回事,因为 body
元素上的 mousemove
处理程序是在事件处理的冒泡阶段设置的。如果我正确理解事件处理的顺序,则 mousemove
处理程序在这里的调用大致如下:
- capture Phase: body 元素的 mousemove (null)
- capture Phase: svg 元素的 mousemove (null)
- capture Phase: the Circle 的 mousemove (null)
- bubble Phase: the Circle mousemove(在拖动开始时由 Raphaël 设置,并调用
move
函数) - bubble stage:svg 元素的 mousemove (null)
- bubble stage:body 元素的 mousemove(如上设置,并停止传播)
如果这是正确的,我不明白在气泡阶段停止 body
的 mousemove 处理程序中的事件传播如何会中断拖动,因为它应该在圆圈处理事件之后发生。
如果有人能解释一下这个例子中发生了什么,特别是我是否误解了事件处理的工作原理,或者 Raphaël 如何实现拖动有什么特殊之处,那就太好了。
I am trying to debug an event handling bug in a complicated web application, but I've reduced the problem to a simple example that demonstrates the behaviour that I'm confused by.
My example page, based one of the Raphaël examples, is as follows:
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Raphaël · Drag-n-drop Example</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script src="http://github.com/DmitryBaranovskiy/raphael/raw/master/raphael-min.js"></script>
<script>
window.onload = function () {
$('body').mousemove(function(e) {
// console.log("in body's mousemove");
// Uncommenting the next line stops the move
// function below from being called:
// e.stopPropagation();
});
var R = Raphael(0, 0, "100%", "100%");
var r = R.circle(100, 100, 50).attr({fill: "hsb(0, 1, 1)", stroke: "none", opacity: .5});
var start = function () {
this.ox = this.attr("cx");
this.oy = this.attr("cy");
this.animate({r: 70, opacity: .25}, 500, ">");
},
move = function (dx, dy) {
// console.log("in move function for the circle", this);
this.attr({cx: this.ox + dx, cy: this.oy + dy});
},
up = function () {
this.animate({r: 50, opacity: .5}, 500, ">");
};
r.drag(move, start, up);
};
</script>
</head>
<body>
<div id="holder"></div>
</body>
</html>
That version works as expected — you can drag the red circle around — but uncommenting the e.stopPropagation()
line breaks dragging. If you have Firebug or Chrome and uncomment the console.log
lines, you can see that the move
function is never called - somehow the mousemove
handler for the body
element is being called before the event gets to the circle's handler.
I don't understand how this can be, since the mousemove
hander on the body
element is set in the bubble phase of event handling. If I understand the order in which events are processed correctly, the order in which mousemove
handlers would be called here is approximately:
- capture phase: the body element's mousemove (null)
- capture phase: the svg element's mousemove (null)
- capture phase: the circle's mousemove (null)
- bubble phase: the circle's mousemove (set by Raphaël when dragging starts, and which calls the
move
function) - bubble phase: the svg element's mousemove (null)
- bubble phase: the body element's mousemove (set as above, and which stops propagation)
If that's right, I don't understand how stopping propagation of the event in the mousemove handler of body
in the bubble phase could break dragging, since it should happen after the circle has dealt with the event.
It would be great if someone could explain what's going on in this example, and in particular whether I've misunderstood how the event handling works, or there's something peculiar about how Raphaël implements dragging.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我尝试在 Raphaël 邮件列表上询问这个问题,那里的回复解释说 Raphaël mousemove 处理程序实际上附加到
document
上,这解释了我所看到的行为。其中一个回复还解释了为什么需要这样实现拖动:I tried asking about this on the Raphaël mailing list, and the replies there explain that the Raphaël mousemove handler is actually attached to
document
, which explains the behaviour I was seeing. One of the replies also explains why dragging needs to be implemented like that: