3

観察された行動

私は d3.js を使用しています。dragイベントに基づいて一部のデータを更新し、イベント後にすべてを再描画したい状況にありdragendます。ドラッグ可能なアイテムにもいくつかのclick動作があります。

ドラッグ可能なアイテムは、x 軸に沿ってのみ移動できます。アイテムがドラッグされ、カーソルが のドラッグ可能なアイテムの真上にある場合、イベントを発生させるdragend/mouseupには、再描画後にアイテムを 2 回クリックする必要があります。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();
4

2 に答える 2

6

パーティーに少し遅れて、buuuut...

ドキュメントでは、イベントで を使用d3.event.defaultPreventedしてclick、要素がドラッグされたかどうかを確認することを提案しています。dragこれをandイベントと組み合わせると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/

于 2013-09-26T16:57:38.117 に答える
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');
    });

これが私のハックな「修正」の完全な実例です:

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

于 2013-03-02T18:26:01.967 に答える