防止循环JavaScript事件

时间:2015-01-20 17:37:42

标签: javascript events d3.js

此问题恰好涉及D3特定功能,但不是D3特定问题。我创建了两个D3图表并附加了缩放功能,目的是保持缩放比例和变换位置相等,这样当一个图表被缩放时,另一个图表会自动缩放到相同的比例和位置。为此,我在缩放一个图表时触发自定义事件,以便我可以自动调整另一个图表:

// Within the chart definition:
var svg = ...; // The chart body definition
var zoom =  d3.behavior.zoom()
    .on("zoom", function(e){
        // do stuff...
        $("#chart-div").trigger({
            type: "my.zoom",
            translate: d3.event.translate,
            scale: d3.event.scale
        });
    });
svg.call(zoom);

然后我创建了一些方法,允许我手动调整每个图表的缩放级别,在此过程中触发D3 zoom事件:

var adjustZoom = function(t,s){
    svg.transition().duration(500).call(zoom.scale(s).translate(t).event);
};

然后我将事件监听器附加到我的每个图表中,这样当我与其中一个图表交互并导致其缩放级别改变时,另一个会自动更新:

$("#chart-div1").on("my.zoom", function(e){
    chart2.adjustZoom(e.translate, e.scale);
});
$("#chart-div2").on("my.zoom", function(e){
    chart1.adjustZoom(e.translate, e.scale);
});

显而易见的问题是调用adjustZoom方法会触发D3 zoom事件,因此在两个图表上附加事件侦听器会创建周期性事件,因为每个图表都会尝试无限调整另一个图表。

1. chart1 [manual zoom]      --> trigger d3.zoom --> triggers my.zoom
2. chart2 [my.zoom listener] --> trigger d3.zoom --> triggers my.zoom
3. chart1 [my.zoom listener] --> trigger d3.zoom --> triggers my.zoom
etc...

我想要实现的是一种在决定触发另一个my.zoom事件时检查my.zoom事件是否已被触发的方法,以防止事件的循环触发(阻止第3步)上面的例子),但我不知道该怎么做。无论如何,一个函数或事件是否知道触发它然后停止的事件?

2 个答案:

答案 0 :(得分:0)

如果translatescale不是模糊值,首先想到的选项是使用当前adjustZoom和{调用translate {1}}无操作。因此,如果图表1的值发生变化,它会引发事件,更改图表2的值,而图表2会引发事件,但图表1会打破周期,因为其scaletranslate已经是那些值。

另一种选择是在事件中传递更改的源元素,然后如果它是启动它的图表,则不在图表上触发它。

,例如,触发时:

scale

然后处理时:

$("#chart-div").trigger({
    type: "my.zoom",
    translate: d3.event.translate,
    scale: d3.event.scale,
    source: this              // <=== The new bit
});

请注意,该代码示例假定if (chart2[0] !== e.source) { chart2.adjustZoom(e.translate, e.scale); } chart1是围绕单个元素的jQuery包装器;如果没有,请相应调整。

答案 1 :(得分:0)

我找到了针对我的问题的D3特定解决方案:

D3事件对象d3.event包含一个嵌套对象sourceEvent,它表示触发D3事件的先前事件的堆栈。此嵌套对象中的底部事件是一个简单的DOM事件。例如,通过缩放,这可能是鼠标滚轮或单击事件。在我的示例中,自定义事件表示为包含所有常用事件键和属性的普通对象,因此我更改了我的缩放事件事件处理程序,如下所示:

var zoom =  d3.behavior.zoom()
.on("zoom", function(e){
    // do stuff...
    if (typeof d3.event.sourceEvent.sourceEvent != 'object'){
        $("#chart-div").trigger({
            type: "my.zoom",
            translate: d3.event.translate,
            scale: d3.event.scale
        });
    }
});

这个新测试的作用是检查sourceEvent是否有父事件,如果是,则确保它不是普通对象(我的自定义事件)。现在,当我缩放一个图表时,它会触发my.zoom事件,触发第二个图表上的缩放功能,但没有第二个图表触发自己的my.zoom事件。

很高兴知道是否有类似的通用方法来处理JavaScript事件,但对于我的用例,这个解决方案可能会这样做。