setTimeout

309 views
Skip to first unread message

Alex Reynolds

unread,
Aug 29, 2012, 7:06:05 PM8/29/12
to d3...@googlegroups.com
I'd like to ignore (or stop processing) events associated with a single click, if there is a double-click event.

I tried using setTimeout to accomplish this, to delay processing of a single click by some short enough timespan that a double-click event can be recognized.

I tried passing d3 parameters directly and through a closure, but neither seem to work.

Let's say I associate clickNode() and dblclickNode() functions with single- and double-click events, respectively:

var node = nodes.selectAll("g.node")

.on("click", clickNode)
.on("dblclick", dblclickNode)
.call(force.drag);

...

var dblclickTimer = null;
function clickNode(d, i) {
var timerFn = function() { foo(d, i); } /* closure */
dblclickTimer = setTimeout(timerFn, 200);
}

function dblclickNode(d, i) {
clearTimeout(dblclickTimer);
...
}

function foo(d, i) {
var id = d3.select(this).attr("id");
...
}

This code returns a "No Attribute" error the line 'var id = …'.

Is there a better way to mask single clicks, when a double-click event occurs?

Thanks for any advice.

-Alex

marc fawzi

unread,
Aug 29, 2012, 7:12:56 PM8/29/12
to d3...@googlegroups.com
The "this" inside foo is no longer the clicked node, but instead it's the window object (assuming the code is running in that context)

you could pass "this" into foo from clickedNode 

but in general, to prevent response to double click and enable it for single click I would set timeout on click (to do something in 500ms) and clear it on double click

Alex

unread,
Aug 29, 2012, 8:28:50 PM8/29/12
to d3...@googlegroups.com
Can you explain the second option in more detail? I think that's what I am trying to do with the code I posted, or is there another way? Do you have an example code snippet to demonstrate an alternative approach?

Regards,
Alex

marc fawzi

unread,
Aug 29, 2012, 9:41:18 PM8/29/12
to d3...@googlegroups.com
don't know exactly the right way in D3 but in JS/jQuery 

$(someNode).bind("click", waitThenAct)
$(someNode).bind("dblclick", cancelAction)
 
var clickTimer;
 
var waitThenReact = function() {
    
     clickTimer = setTimeout(function() {
         // act on something here
     }, 500 /*wait time*/)    
}

var cancelAction = function() {
    clearTimeout(clickTimer)
}

Not sure if 500ms is the right period to wait, but close enough

Alex

unread,
Aug 30, 2012, 2:01:08 AM8/30/12
to d3...@googlegroups.com
This is almost identical to the code I posted. But this will not work, in any case, because I need to pass node data and index parameters through the timer, and the problem is that the parameters do not get passed through in your example. In my example, my closure passes parameters, but the timer seems to mangle them in some way.

Has anyone successfully handled double-click events without single-click events getting triggered?

Sujay Vennam

unread,
Aug 30, 2012, 2:04:10 AM8/30/12
to d3...@googlegroups.com

Shripad K

unread,
Aug 30, 2012, 2:13:30 AM8/30/12
to d3...@googlegroups.com
Marc is right. Your "this" refers to the window object. The code should be like this:

var node = nodes.selectAll("g.node")

        .on("click", clickNode)
        .on("dblclick", dblclickNode)
        .call(force.drag);

...

var dblclickTimer = null;
function clickNode(d, i) {
  var self = this;
  var timerFn = function() { foo(self, d, i); self = null; } /* remove reference for self so it can be garbage collected (especially in IE) */

  dblclickTimer = setTimeout(timerFn, 200);
}

function dblclickNode(d, i) {
  clearTimeout(dblclickTimer);
  ...
}

function foo(node, d, i) {
  var id = d3.select(node).attr("id");
  ...

Alex

unread,
Aug 30, 2012, 4:31:21 PM8/30/12
to d3...@googlegroups.com
Hmm. This did not work. The single click event still happens on a double-click.

What I needed to do was set the timer to null immediately after clearing it:

function dblclickNode(d, i) {
  clearTimeout(dblclickTimer);
  dblclickTimer = null;
  ...
}

In the "real" click function foo(), I check if the timer is null:

function foo(node, d, i) {
  if (dblclickTimer === null)
    return;

  var id = d3.select(node).attr("id");
  ...
}

Pretty complicated, but it seems to now work. Thanks for getting me started.

-Alex
Reply all
Reply to author
Forward
0 new messages