almende / vis

⚠️ This project is not maintained anymore! Please go to https://github.com/visjs
7.85k stars 1.48k forks source link

Improve the click/doubleClick event with options how these should be fired. #203

Open dponch opened 10 years ago

dponch commented 10 years ago

Hi,

I want to use the events click and doubleclick on a network graph, but I realized that the event doubleclick triggers the event click, so when I doubleclick 2 functions are called. Is there a way to avoid this?

Thanks

AlexDM0 commented 10 years ago

Hi Dponch,

You could create a timeout function in your onclick function with a timeout you decide. If you have not received a doubleclick by then, interpret it as a click.

var doubleClick = false;

function onclick() {
  setTimeout(function () {
    if (doubleClick == false) {
      doOnClick();
    }
    else {doubleClick = false;}
},400);
}

function doOnClick() {
  ...
  do stuff
}

function onDoubleClick() {
  doubleClick = true;
  ...
  do stuff
}

Regards,

Alex

dponch commented 10 years ago

Hi,

That's a good idea, thanks for your help. But it didn't work out. I'm seeing that when I doubleclick it makes: 1) Call to click event. This can be avoid with your solution 2) Call to doubleclick event 3) Call to click event again. So finally, it does the click stuff.

I'm using Firefox30. Could it be a problem?

AlexDM0 commented 10 years ago

Hi Dponch,

You're right! I forgot about the second click hehe :). This should work:

network.on('click', onClick);
network.on('doubleClick', onDoubleClick);

var doubleClickTime = 0;
var threshold = 200;

function onClick() {
    var t0 = new Date();
    if (t0 - doubleClickTime > threshold) {
        setTimeout(function () {
            if (t0 - doubleClickTime > threshold) {
                doOnClick();
            }
        },threshold);
    }
}

function doOnClick() {
    console.log("execute onClick function");
}

function onDoubleClick() {
    doubleClickTime = new Date();
    console.log("execute onDoubleClick function");
}

Let me know if this works!

dponch commented 10 years ago

It works perfectly! Many thanks!!!

AlexDM0 commented 10 years ago

You're welcome. We have not put this in vis because it would delay the click event. If a user doesnt use the double click that would be annoying :).

dsl101 commented 10 years ago

Alex,

Sorry to reopen an old ticket, but I came here looking for a solution to the multiple event issue. I completely understand why you wouldn't want the code above which delays the click() event, but would it be possible for vis to suppress the second spurious click() event? At the moment, if you double-click on the network, the user code gets:

click()
doubleClick()
click()

Is there a case for ever getting the second click() event? Just the first two would seem to be 'normal' behaviour in, e.g. vb.net, etc. At the moment, I've had to put a variation of your code in to prevent that happening:

network.on('click', onClick);
network.on('doubleClick', onDoubleClick);

var doubleClickTime = 0;
var threshold = 200;

function onClick() {
    var t0 = new Date();
    if (t0 - doubleClickTime > threshold) {
        doOnClick();
    }
}

function doOnClick() {
    console.log("execute onClick function");
}

function onDoubleClick() {
    doubleClickTime = new Date();
    console.log("execute onDoubleClick function");
}

This reduces a double-click on the network to these events:

click()
doubleClick()

which is actually exactly what I want. People who don't want the first click() event when the user double-clicks can the use very similar user-side code to wait and see if there's a double-click event following within their own timeout.

The only side-effect of this would be that a double-click can't be followed by a single-click within the threshold time - but again, that's pretty normal I think.

AlexDM0 commented 10 years ago

Hi Dave,

That's perfectly fine. This code was just a suggestion or idea. To answer your question, no I don't think it's up to us to determine if people want a click after a double click. Since the solution is very easy, I'd prefer to leave that up to the devs using vis.

Regards,

Alex

josdejong commented 10 years ago

It makes sense to me to just have the click-doubleclick events, instead of click-doubleclick-click. The second click is "eaten" or replaced by a doubleclick when within the threshold.

I can't come up with a use case where you would want click-doubleclick-click. I think this behavior is unwanted in practice.

AlexDM0 commented 10 years ago

Double click can color a node and then click again somewhere else to deselect?

AlexDM0 commented 10 years ago

Nevermind, misread the issue.

josdejong commented 10 years ago

I think there are three use cases:

  1. I just use click. Click should react immediately.
  2. I use both click and doubleclick, and it is fine to have both fired on the same user event. Click can then react immediately.
  3. I use both click and doubleclick, and these two involve actions which should not happen on the same user event, I want either click or doubleclick fired but not both. For example on singleclick I create something, on doubleclick I delete something. This means a single click needs to wait for the threshold before it knows it was not the first click of a doubleclick.

I think 1. and 2. are important. We could realize the third option as well, that requires the developer to set an option to specify this behavior.

AlexDM0 commented 10 years ago

The first item, only using click, makes it unacceptable for vis to eat a click for the doubleClick event. Since the events are always fired, regardless of listeners, what do you suggest? Options to pass to network on init?

If the default settings are anything other then it is now it could give unexpected behaviour. Also the doubleClick threshold (time to wait) could be customised. I'm not sure if I'd want to have people use the code as stated above or add more options.

What do you think?

josdejong commented 10 years ago

The first item, only using click, makes it unacceptable for vis to eat a click for the doubleClick event.

Why? Seen from a user perspective, the user either does a single click (resulting in a click event) or a double click (which should result in one click and one doubleclick event). In case of a double click, the second click is fired as a doubleclick event instead of a second click event.

So I think we can keep current behavior, except for the second click event not being fired anymore when doubleclicking. Then we have situations 1. and 2. right?

Option 3 is an extra, that would require configuration via the options.

AlexDM0 commented 10 years ago

If you're only using clicks, and you are clicking twice fast I find it strange that you'd have to listen to the doubleClick event instead of just the click event because you might lose a click. Now vis gives you exactly what happens. There ARE two clicks and they were fast enough that it could be called a double click. I would argue this gives the most reliable result. We could add event options to streamline this if needed.

josdejong commented 10 years ago

yeah, that's true. We could introduce an option for this with three different modes.

dsl101 commented 10 years ago

I'm torn between Alex's last comment (what you get is exactly what is happening), and the vast experience of developing on, e.g. Win32 where that isn't what you get. I'm not saying Win32 is right here, just that I was confused by the event stream being 'different'.

I think a 'doubleClickMode' option would be ideal - I'll leave it to someone else to argue what the default should be :). Then I can code on the user side exactly as I do in other apps, and get the same events I would normally expect. Here's my suggestion:

When the user just 'clicks', you should always get a click() event. When the user 'double-clicks', you should get 1 of 4 things:

  1. doubleClickMode: false - you just get the 2 click() events, very close together. Maybe this isn't strictly necessary - you just wouldn't subscribe to the doubleClick() event...
  2. doubleClickMode: {mode: 'only', threshold: 200} - you just get 1 doubleClick() event, with the side-effect that all click() events will be delayed by threshold milliseconds, to allow for the detection of a double-click
  3. doubleClickMode: {mode: 'normal', threshold: 200} - you get a click() event immediately, and then a doubleClick() event if the user clicks again within threshold milliseconds (2 events in total)
  4. doubleClickMode: {mode: 'all', threshold: 200} - you get a click() event immediately, and then a second click() event and a doubleClick() event if the user clicks again within threshold milliseconds (3 events in total)
AlexDM0 commented 10 years ago

Hi Dave,

I would argue that 1 and 4 are the same. That leaves:

eventOptions : {
  doubleClickThreshold : 200,  // default 200?
  doubleClick: true,  // true will hide the second click, false: will not fire a doubleClick event at all.
  ... maybe future event options?
}

Hows that? If the user chooses to use the double click he'll have to work with the initial click anyway. I'm opposed to delaying ALL clicks, seems nasty.

Regards

dsl101 commented 10 years ago

Agree 1 isn't necessary as 1 == 4 if you don't subscribe to the doubleclick event.

Not sure what doubleClick: false above would give - isn't that the same again as just not subscribing to the double-click event?

I'd strongly argue for including case 2. though, with a warning in the docs that it will delay clicks. If that's the behaviour you want, your user-side click handler will end up being delayed anyway, and this way it makes the user-side code much cleaner and easier to understand. IMO, we shouldn't be putting that level of click handling stuff in the application - it would be much nicer if the library did it for us. Understand this makes vis messier inside though. How about this version?

eventOptions : {
    doubleClickThreshold : 200,  // default 200? Read from OS (yuck)?
    hideSingleClicks: 2  // default 1 (convention) or 0 (backwards compatibility for vis)
        // Hide 0, 1 or 2 of the associated single clicks when a double click is detected
        // 0 - user sees 'click', 'doubleClick', 'click' - no delay to click events
        // 1 - user sees 'click', 'doubleClick' - no delay to click events
        // 2 - user sees 'doubleClick' - all click events delayed

    // ... maybe future event options?
}
AlexDM0 commented 10 years ago

I would argue that doubleClick: false in my example completely removes the double click functionality from the network. If true, we can delay all clicks and hide the second one. The user would essentially be "toggling" the double click functionality on or off.

dsl101 commented 10 years ago

I'm still a little unclear what the difference is between turning doubleClick off, and simply not subscribing to the doubleClick event. But anyway, would doubleClick: false then send click() or click(), click() to the user? And if you set doubleClick(): true, you then have no option to get all the events, which is the current behaviour. Doesn't that risk breaking old code?

AlexDM0 commented 10 years ago

if false, the user would get click - click, if true, the user would get click doubleclick. As for breaking old code, yes that is possible.. we could still emit the doubleclick so if false the user would get click - doubleclick - click as it is now.

dsl101 commented 10 years ago

I think that's basically cases 0 and 1 in my list. Case 2 is the 'new' one, where click = click() and double-click = doubleClick(), which might be good for new apps, and would save the user having to put the wait code in.

In your last scenario, supposing I wanted a user's 'click' to do something, but a user's 'double-click' to do nothing. I'd have to subscribe to the doubleClick() event, just to surpress the previous click() event that I got first on the first press of the mouse! Seems like it makes the library quite awkward from a developers point of view...

AlexDM0 commented 9 years ago

Hi,

Over the last year a lot of feature requests have been made. We have just introduced our new website which has a list of the requested features. We have placed this request on that list.

The list can be found here: http://visjs.org/featureRequests.html

An explaination of the new system can be found here: http://visjs.org/blog.html#New\ website\ for\ vis.js!

I would like to stress that this does not mean we abandon this request. Discussion here will continue if needed on this feature but we will close it to keep our Github issue page more of a bug-todo list.

Future feature requests will still be made here and then added to the website by us.

Regards,

Alex

mojoaxel commented 8 years ago

Reopening as Feature-Request issue (see #2114). Everybody: Please feel free to implement this!

supriyasureshg commented 7 years ago

Onclick of buttons how to show the particular segments of node.In vis js. If any knows please help me out.

wimrijnders commented 7 years ago

@supriyasureshg You commented here so I'll remove the label Inactive. However, I don't really understand your comment, would you mind making it more clear?

saliez commented 6 years ago

It is certainly very nice and ergonomic to be able to distinguish different actions using the same left mouse button. Since the double click is a problem, an alternative could be to detect the time between the button down and the release.

MykhailoMykhaliuk commented 6 years ago

I'd recommend to use Rx.js

rohit2503 commented 6 years ago

I have following work to do :

  1. Click on graph.
  2. Pull the x axes data on which click was made.
  3. Using this x -axis value make ajax call.

I have written a function and map it to the clickCallBack of Dygraph. Things are working fine but soon you double click on graph will again call the function and I lost default feature of reset zoom. I know there should be something to differentiate the single click vs double click. The solution above told will not work for me since I need to fetch x axis value and click on container which holds graph will not fetch me point on which click was made.

I have even tried interactionModel but facing same problem. Please let me know your suggestion. @dponch @AlexDM0

Thanks, Rohit Jaiswal

MykhailoMykhaliuk commented 6 years ago
const source = Rx.Observable.fromEvent(container, 'click')
  .map(({ offsetX  }) => offsetX)
  .throttleTime(300)
  .subscribe(offsetX => {
    console.log(offsetX);
    // make ajax call here
  });
rohit2503 commented 6 years ago

@MishaMykhalyukCogniance will it work for container which holds dygraph based graph. I have not used Rx before.

MykhailoMykhaliuk commented 6 years ago

@rohit2503 It should work. I've checked it on official site: image

rohit2503 commented 6 years ago

Sorry I tried but it's not working. I have used this RX js https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.2.0/rxjs.umd.js in my code. When I am typing in browser console Rx it is giving error Rx not found.