我正在使用d3.js,而我正处于基于drag
事件更新某些数据的情况,并在dragend
事件之后重绘所有内容。可拖动项目也有一些click
行为。
可拖动项目只能沿x轴移动。拖动项目时,光标位于dragend/mouseup
上可拖动项目的正上方,在重新绘制click
事件后,必须单击该项目两次才能触发。拖动某个项目时,dragend/mouseup
不会直接发生在该项目上方,click
事件会在重绘后按预期(第一次尝试)触发。
我希望click
事件在拖动后始终在第一次点击时触发,无论光标在哪里。
如果我使用click
事件替换可拖动项目上的mouseup
事件,则一切都按预期工作,但click
是我真正想要处理的事件。
以下是一个独立的示例:http://jsfiddle.net/RRCyq/2/
以下是相关的javascript代码:
var data, click_count,did_drag;
// this is the data I'd like to render
data = [
{x : 100, y : 150},
{x : 200, y : 250}
];
// these are some elements I'm using for debugging
click_count = d3.select('#click-count');
did_drag = d3.select('#did-drag');
function draw() {
var drag_behavior,dragged = false;
// clear all circles from the svg element
d3.select('#test').selectAll('circle')
.remove();
drag_behavior = d3.behavior.drag()
.origin(Object)
.on("drag", function(d) {
// indicate that dragging has occurred
dragged = true;
// update the data
d.x = d3.event.x;
// update the display
d3.select(this).attr('cx',d.x);
}).on('dragend',function() {
// data has been updated. redraw.
if(dragged) { draw(); }
});
d3.select('#test').selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr('cx',function(d) { return d.x; })
.attr('cy',function(d) { return d.y; })
.attr('r',20)
.on('click',function() {
did_drag.text(dragged.toString());
if(!dragged) {
// increment the click counter
click_count.text(parseInt(click_count.text()) + 1);
}
}).call(drag_behavior);
}
draw();
答案 0 :(得分:6)
派对有点晚了,buuuut ......
文档建议您在d3.event.defaultPrevented
事件中使用click
来了解该元素是否仅被拖动。如果将其与drag
和dragend
事件结合使用,更简洁的方法是在必要时调用所需的确切函数(请参阅调用flashRect
的时间和方式):
http://jsfiddle.net/langdonx/fE5gN/
var container,
rect,
dragBehavior,
wasDragged = false;
container = d3.select('svg')
.append('g');
rect = container.append('rect')
.attr('width', 100)
.attr('height', 100);
dragBehavior = d3.behavior.drag()
.on('dragstart', onDragStart)
.on('drag', onDrag)
.on('dragend', onDragEnd);
container
.call(dragBehavior)
.on('click', onClick);
function flashRect() {
rect.attr('fill', 'red').transition().attr('fill', 'black');
}
function onDragStart() {
console.log('onDragStart');
}
function onDrag() {
console.log('onDrag');
var x = (d3.event.sourceEvent.pageX - 50);
container.attr('transform', 'translate(' + x + ')');
wasDragged = true;
}
function onDragEnd() {
if (wasDragged === true) {
console.log('onDragEnd');
// always do this on drag end
flashRect();
}
wasDragged = false;
}
function onClick(d) {
if (d3.event.defaultPrevented === false) {
console.log('onClick');
// only do this on click if we didn't just finish dragging
flashRect();
}
}
我不喜欢全局变量,因此我进行了修订以使用数据:http://jsfiddle.net/langdonx/fE5gN/1/
答案 1 :(得分:2)
在观察到我的svg圈子之前所需的点击会再次开始响应点击事件后,可能会在文档中发生任何地方,我决定在黑客上模拟文档上的点击事件(谢谢拖动结束后到https://stackoverflow.com/a/2706236/1015178)。这很难看,但它确实有效。
这是模拟事件的功能(再次,感谢https://stackoverflow.com/a/2706236/1015178)
function eventFire(el, etype){
if (el.fireEvent) {
(el.fireEvent('on' + etype));
} else {
var evObj = document.createEvent('Events');
evObj.initEvent(etype, true, false);
el.dispatchEvent(evObj);
}
}
这是更新的拖动行为:
drag_behavior = d3.behavior.drag()
.origin(Object)
.on("drag", function(d) {
// indicate that dragging has occurred
dragged = true;
// update the data
d.x = d3.event.x;
// update the display
d3.select(this).attr('cx',d.x);
}).on('dragend',function() {
// data has been updated. redraw.
if(dragged) { draw(); }
// simulate a click anywhere, so the svg circles
// will start responding to click events again
eventFire(document,'click');
});
这是我的hackish“修复”的完整工作示例: