在d3.js中单击“拖动后触发事件”(有时)

时间:2013-02-02 22:54:09

标签: d3.js

观察到的行为

我正在使用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();

2 个答案:

答案 0 :(得分:6)

派对有点晚了,buuuut ......

文档建议您在d3.event.defaultPrevented事件中使用click来了解该元素是否仅被拖动。如果将其与dragdragend事件结合使用,更简洁的方法是在必要时调用所需的确切函数(请参阅调用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“修复”的完整工作示例:

http://jsfiddle.net/RRCyq/3/