Javascript 为什么';点击事件是否总是触发?

Javascript 为什么';点击事件是否总是触发?,javascript,events,d3.js,Javascript,Events,D3.js,如果你重新考虑这个问题,我已经把所有的更新移到了底部,所以作为一个问题,它实际上读起来更好 问题 我在使用D3处理浏览器事件时遇到了一点奇怪的问题。不幸的是,这存在于一个相当大的应用程序中,因为我完全不知道原因是什么,我正在努力寻找一个小的可复制的示例,所以我将提供尽可能多的有用信息 所以我的问题是,对于某些DOM元素,click事件似乎不能可靠地触发。我有两组不同的元素填充的圆和白色的圆。您可以在下面的屏幕截图中看到,1002和1003是白色圆圈,而供应商是填充圆圈 现在这个问题只发生在我不

如果你重新考虑这个问题,我已经把所有的更新移到了底部,所以作为一个问题,它实际上读起来更好

问题 我在使用
D3
处理浏览器事件时遇到了一点奇怪的问题。不幸的是,这存在于一个相当大的应用程序中,因为我完全不知道原因是什么,我正在努力寻找一个小的可复制的示例,所以我将提供尽可能多的有用信息

所以我的问题是,对于某些DOM元素,
click
事件似乎不能可靠地触发。我有两组不同的元素填充的圆和白色的圆。您可以在下面的屏幕截图中看到,1002和1003是白色圆圈,而供应商是填充圆圈

现在这个问题只发生在我不理解的白色圆圈上。下面的屏幕截图显示了单击圆圈时发生的情况。单击顺序通过红色数字以及与之相关的日志显示。基本上你看到的是:

  • 穆斯敦
  • 鼠标
  • 有时是咔嗒一声
这个问题有点零散。我设法找到了一个真实的复制品,但在浏览器刷新几次之后,现在复制起来就困难多了。如果我交替单击1002和1003,那么我将继续获得
mousedown
mouseup
事件,但从未单击过
。如果我第二次点击其中一个,那么我确实会得到一个
点击事件。如果我一直单击同一个按钮(此处未显示),则每隔单击一次就会触发
click
事件

如果我对像供应商这样的填充圆圈重复相同的过程,那么它工作正常,
click
每次都会被触发


圆圈是如何创建的 因此,圆(在我的代码中称为行星)被创建为一个模块化组件。数据在其中循环,并为每个数据创建一个实例

data.enter()
    .append("g")
    .attr("class", function (d) { return d.promoted ? "collection moon-group" : "collection planet-group"; })
    .call(drag)
    .attr("transform", function (d) {
        var scale = d.size / 150;
        return "translate(" + [d.x, d.y] + ") scale(" + [scale] + ")";
    })
    .each(function (d) {

        // Create a new planet for each item
        d.planet = new d3.landscape.Planet()
                              .data(d, function () { return d.id; })
                              .append(this, d);
    });
这并没有告诉你太多,下面是一个
力定向图
用于计算位置。
Planet.append()函数中的代码如下所示:

d3.landscape.Planet.prototype.append = function (target) {
    var self = this;

    // Store the target for later
    self.__container = target;
    self.__events = new custom.d3.Events("planet")
                                    .on("click", function (d) { self.__setSelection(d, !d.selected); })
                                    .on("dblclick", function (d) { self.__setFocus(d, !d.focused); self.__setSelection(d, d.focused); });

    // Add the circles
    var circles = d3.select(target)
                    .append("circle")
                    .attr("data-name", function (d) { return d.name; })
                    .attr("class", function(d) { return d.promoted ? "moon" : "planet"; })
                    .attr("r", function () { return self.__animate ? 0 : self.__planetSize; })
                    .call(self.__events);
这里我们可以看到附加的圆(注意每个行星实际上只是一个圆)。custom.d3.Events是为刚刚添加到DOM中的圆构造和调用的。此代码用于填充圆和白色圆,唯一的区别是类中的细微变化。为每个对象生成的DOM如下所示:

填满

供应商
白色

1002

custom.d3.events做什么? 这背后的想法是提供比默认情况下更丰富的事件系统。例如,允许双击(不触发单次单击)和长时间单击等

当使用
circle
容器调用事件时,执行以下操作,使用D3设置一些
raw
事件。这些与在
Planet.append()
函数中连接的不同,因为
events
对象公开了它自己的自定义分派。这些是我用来调试/记录的事件

custom.d3.Events = function () {

   var dispatch = d3.dispatch("click", "dblclick", "longclick", "mousedown", "mouseup", "mouseenter", "mouseleave", "mousemove", "drag");

   var events = function(g) {
       container = g;

       // Register the raw events required
       g.on("mousedown", mousedown)
        .on("mouseenter", mouseenter)
        .on("mouseleave", mouseleave)
        .on("click", clicked)
        .on("contextmenu", contextMenu)
        .on("dblclick", doubleClicked);

       return events;
   };

   // Return the bound events
   return d3.rebind(events, dispatch, "on");
}
所以,在这里,我介绍了一些事件。按相反的顺序看:

点击 单击函数被设置为只记录我们正在处理的值

 function clicked(d, i) {
    console.log("clicked", d3.event.srcElement);
    // don't really care what comes after
 }
松开鼠标 mouseup函数本质上是记录和清除一些全局窗口对象,这将在下一步讨论

 function mouseup(d, i) {
    console.log("mouseup", d3.event.srcElement);
    dispose_window_events();
 }
鼠标按下 mousedown函数稍微复杂一点,我将包括它的全部内容。它可以做很多事情:

  • 将鼠标向下记录到控制台
  • 设置窗口事件(在窗口对象上连接mousemove/mouseup),以便即使鼠标不再位于触发mousedown的圆圈内,也可以触发mouseup
  • 查找鼠标位置并计算一些阈值
  • 设置计时器以触发长时间单击
  • 激发位于custom.d3.event对象上的mousedown分派

    function mousedown(d, i) {
       console.log("mousedown", d3.event.srcElement);
    
       var context = this;
       dragging = true;
       mouseDown = true;
    
       // Wire up events on the window
       setup_window_events();
    
       // Record the initial position of the mouse down
       windowStartPosition = getWindowPosition();
       position = getPosition();
    
       // If two clicks happened far apart (but possibly quickly) then suppress the double click behaviour
       if (windowStartPosition && windowPosition) {
           var distance = mood.math.distanceBetween(windowPosition.x, windowPosition.y, windowStartPosition.x, windowStartPosition.y);
           supressDoubleClick = distance > moveThreshold;
       }
       windowPosition = windowStartPosition;
    
       // Set up the long press timer only if it has been subscribed to - because
       // we don't want to suppress normal clicks otherwise.
       if (events.on("longclick")) {
           longTimer = setTimeout(function () {
               longTimer = null;
               supressClick = true;
               dragging = false;
               dispatch.longclick.call(context, d, i, position);
           }, longClickTimeout);
       }
    
       // Trigger a mouse down event
       dispatch.mousedown.call(context, d, i);
       if(debug) { console.log(name + ": mousedown"); }
    }
    

更新1

我应该补充一点,我在Chrome、IE11和Firefox中都经历过这一点(尽管这似乎是最可靠的浏览器)

不幸的是,经过一些刷新和代码更改/恢复后,我很难获得可靠的复制。然而,我注意到,奇怪的是,以下顺序可以产生不同的结果:

  • F5刷新浏览器
  • 点击
    1002
有时会触发
鼠标向下
鼠标向上
,然后
单击
。其他情况下,它会错过
单击
。这似乎很奇怪,在同一页面的两个不同加载之间会偶尔出现此问题

我还应该补充一点,我已经尝试了以下方法:

 var drag = d3.behavior.drag()
             .on("dragstart", function () { self.__dragstart(); })
             .on("drag", function (d) { self.__drag(d); })
             .on("dragend", function (d) { self.__dragend(d); });
  • 导致
    mousedown
    失败,并验证
    单击
    是否仍然激发,以确保
    mousedown
    中的偶发错误不会导致问题。如果
    鼠标向下
    中出现错误,我可以确认
    单击
    将触发事件
  • 试图检查定时问题。为此,我在
    mousedown
    中插入了一个长阻塞循环,并可以确认
    mouseup
    click
    事件将在相当长的延迟后触发。因此,事件看起来确实像您预期的那样按顺序执行

更新2

@CoolBlue的评论之后的一个快速更新是,在我的事件处理程序中添加名称空间似乎没有任何区别。以下人员仍偶尔遇到此问题:

var events = function(g) {
    container = g;

    // Register the raw events required
    g.on("mousedown.test", mousedown)
     .on("mouseenter.test", mouseenter)
     .on("mouseleave.test", mouseleave)
     .on("click.test", clicked)
     .on("contextmenu.test", contextMenu)
     .on("dblclick.test", doubleClicked);

    return events;
};
而且
css
是我还没有提到的东西。两种不同类型之间的css应该相似。整个集合如下所示,尤其是
点事件
仅针对m中的标签设置为
var events = function(g) {
    container = g;

    // Register the raw events required
    g.on("mousedown.test", function (d) { console.log("mousedown.test"); })
     .on("click.test", function (d) { console.log("click.test"); });

    return events;
};
var drag = d3.behavior.drag()
             .on("dragstart", function () { console.log("dragstart"); self.__dragstart(); })
             .on("drag", function (d, x, y) { console.log("drag", d3.event.sourceEvent.x, d3.event.sourceEvent.y); self.__drag(d); })
             .on("dragend", function (d) { console.log("dragend"); self.__dragend(d); });
return function (suppressClick) {
     console.log("supressClick = ", suppressClick);
     w.on(name, null);
     ...
}
return function (suppressClick) {
    console.log("supressClick = ", suppressClick);
    suppressClick = false;
    w.on(name, null);
    ...
}
 var drag = d3.behavior.drag()
             .on("dragstart", function () { self.__dragstart(); })
             .on("drag", function (d) { self.__drag(d); })
             .on("dragend", function (d) { self.__dragend(d); });
 self.__zoom = d3.behavior
                        .zoom()
                        .scaleExtent([minZoom, maxZoom])
                        .on("zoom", function () { self.__zoomed(d3.event.translate, d3.event.scale); });
var events = function(g) {

    // Register the raw events required
    g.on("mousedown.test", mousedown)
     .on("mouseenter.test", mouseenter)
     .on("mouseleave.test", mouseleave)
     .on("click.test", clicked)
     .on("contextmenu.test", contextMenu)
     .on("dblclick.test", doubleClicked);

    return g;
};