Jump to content
Search Community

Draggable causes two clicks on iPad?

holgersindbaek test
Moderator Tag

Warning: Please note

This thread was started before GSAP 3 was released. Some information, especially the syntax, may be out of date for GSAP 3. Please see the GSAP 3 migration guide and release notes for more information about how to update the code to GSAP 3's syntax. 

Recommended Posts

I've recently been trying to integrate GreenSock into my website in order to take advantage of the Draggable element.

 

I was able to get it almost working, but I found what looks like a bug on the iPad. I am not sure if the issue is due to touchscreen, per se, or something specific to the iPad.

 

I have a working piece of code which demonstrates the issue, here.

 

To reproduce the behavior:

1) Visit the above link, and click the square with the blue outline. It is a Draggable. However, just click it, don't bother dragging it, since the issue occurs with a simple click. Notice that when you click it, the counter increments by 1. That is correct behavior, because I have a click handler on that square in addition to the Draggable.

2) Now, visit that link from an iPad, and touch the square with the blue outline. Same code, different behavior: the counter increments first by 1, then there's a slight flash, and then it increments by 2! This indicates a click event was fired twice. This is a problem for me. Why is click happening twice? Is there some way to stop it? I need the same behavior on both mouse clicks and touch clicks.

 

You can read the source code if you want to see what's being done. But basically, I create a Draggable on that blue square. I have a completely separate event handler for the click event (I'm not using Draggable's onClick property, and would very much prefer not to do so).

 

Can anyone help with this? Is it a known issue, is it... a feature? Is there some way I can handle this without doing contortions to detect iPad/touchscreen?

 

I will create a Codepen URL if anyone wants me to do so, but the code is just there in my link.

Link to comment
Share on other sites

I noticed you've got allowEventDefault:true on the Draggable. Have you tried deleting that? 

 

I know that when building Draggable, it was a huge nightmare trying to normalize all the browser behavior with click/drags/pointerEvents/mouseEvents/touchEvents because they all have various bugs/quirks/differences. That's one of the reasons we have an onClick handler in Draggable - so you can save yourself from some of these nightmares and let us handle them internally :) I'm curious why you're trying to avoid using it. 

  • Like 3
Link to comment
Share on other sites

5 hours ago, GreenSock said:

That's one of the reasons we have an onClick handler in Draggable - so you can save yourself from some of these nightmares and let us handle them internally :) I'm curious why you're trying to avoid using it. 

The reason I'm trying to avoid using Draggable's onClick is because I'm just trying to grab the "drag" functionality, and add it to a huge code base which uses another library entirely. The existing code base has a different framework for dealing with clicks. I suspect that trying to make GreenSock handle clicks will cause bugs in other ways. Just trying to disturb the existing code base in the most minimal way, hope that's clear.

  • Thanks 1
Link to comment
Share on other sites

Hi @marya and welcome to the GreenSock forum!

 

Why not use onPress instead of onClick?  onPress is like using mousedown.

 

Draggable Docs: https://greensock.com/docs/Utilities/Draggable

  • onPress:  Function - a function that should be called as soon as the mouse (or touch) presses down on the element. Inside that function, "this" refers to the Draggable instance (unless you specifically set the scope using onPressScope), making it easy to access the target element (this.target) or the boundary coordinates (this.maxX, this.minX, this.maxY, and this.minY). By default, thepointerEvent (last mouse or touch event related to the Draggable) will be passed as the only parameter to the callback so that you can, for example, access its pageX or pageY or target or currentTarget, etc.
Happy Tweening :)
  • Like 1
Link to comment
Share on other sites

3 minutes ago, Jonathan said:

Why not use onPress instead of onClick?  onPress is like using mousedown.

Thanks for the welcome, jonathan!

 

I am trying to avoid using GreenSock for anything other than dragging functionality. There's already a framework in place for handling clicks/presses; I don't want to have to rewrite any of that code. If I can just use GreenSock for dragging, then it's super helpful. I plan to subscribe if I can get that working.

Link to comment
Share on other sites

I was looking at this thread and waiting to see if you reported anything back after Jack's comment.

 

To me, it sounds like an issue with onTouchEnd and onClick being fired by the iPad. I can't confirm as I do not have access to an iPad currently and cannot replicate.

 

iPads have (or used to have, I haven't checked in a while) a slight delay before firing a click event. So, your event listener might be picking up the onTouchEnd as soon as you stop touching the screen and, after the 300ms, the click event is fired.

  • Like 2
Link to comment
Share on other sites

You can also use event methods like stopPropagation or stopImmediatePropagation to prevent any event bubbling and prevent it from firing other events. Keep in mind that stopPropagation is different then preventDefault.

 

event.stopPropagation

https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation

 

event.stopImmediatePropagation

https://developer.mozilla.org/en-US/docs/Web/API/Event/stopImmediatePropagation

 

Happy Tweening! :)

  • Like 2
Link to comment
Share on other sites

16 hours ago, GreenSock said:

I noticed you've got allowEventDefault:true on the Draggable. Have you tried deleting that? 

 

I know that when building Draggable, it was a huge nightmare trying to normalize all the browser behavior with click/drags/pointerEvents/mouseEvents/touchEvents because they all have various bugs/quirks/differences. That's one of the reasons we have an onClick handler in Draggable - so you can save yourself from some of these nightmares and let us handle them internally :) I'm curious why you're trying to avoid using it. 

So I set allowEventDefault:false in this demo. That is the only difference between this demo and the previous one; allowEventDefault is set to false.

 

(By the way, I don't see documentation saying what the default value is for that variable. Am I missing something or is the documentation missing?)

 

Anyway, doing that does indeed seem to fix the issues on almost every browser + device that I tried. Unfortunately, there's still one case where this breaks. I have a Windows 10 touchscreen device called a Nextbook. When I run the demo on the most recent version of Chrome, 75.0.3770.142 (32-bit), the clicks either occur once or twice, randomly. I don't see any pattern to whether there's one click or two happening. I don't know whether it is worth it for your to investigate this particular problem. I did take a look at the Draggable source, and I can see that it must have been quite a nightmare getting it working on many different systems.

 

I'm going to investigate whether the Draggable.onDrag function can be used for my purposes as well.

 

Thank you. I am now a supporter at the "ShockinglyGreen" level ?

  • Like 2
Link to comment
Share on other sites

12 minutes ago, marya said:

By the way, I don't see documentation saying what the default value is for that variable. Am I missing something or is the documentation missing?

 

You're not missing anything. Looking at the entry now I see the text is not explicit enough. It implies the default value as being false but it should be clearer. It will be amended in due course. Thanks for pointing that out.

 

13 minutes ago, marya said:

I am now a supporter at the "ShockinglyGreen" level ?

 

Welcome to the Club. You will see life's greener now. ;)

 

  • Like 2
Link to comment
Share on other sites

4 minutes ago, Dipscom said:

 

You're not missing anything. Looking at the entry now I see the text is not explicit enough. It implies the default value as being false but it should be clearer. It will be amended in due course. Thanks for pointing that out.

 

 

Welcome to the Club. You will see life's greener now. ;)

 

Given the results from the second experiment that I did, it seems to me this is a GreenSock issue, which apparently is fixed by setting allowEventDefault to false. Maybe?? I'm going to go down the rathole of trying to replace my own onClick functionality with Draggable's onClick to see if I can fix the issue in Win touchscreen+Chrome too. I have only one card game (Gin Rummy) at World of Card Games which requires cards to be dragged, so I'm hopeful I can confine all changes to that one game.

Link to comment
Share on other sites

So the whole "double click" thing on iOS may be a general iOS behavior with JavaScript. I've found a couple references to it out on Google.

 

Adding this meta tag fixes the problem in your test case:

<meta name="viewport" content="width=device-width">

 

I think by specifying a "viewport" you are telling iOS that you are going to handle touch events yourself and not to 'simulate' any sort of onClick type events. Or something :)

 

On an somewhat related note, I also find this script very useful when needing to drag things with Greensock Draggable on iOS: https://github.com/lazd/iNoBounce

It prevents the whole screen from bouncing around when dragging things.

  • Like 4
Link to comment
Share on other sites

The only downside to the CSS overscroll-behavior property is that it disables pull-to-refresh, not just containing the bouncy scroll behavior.

 

Hopefully eventually they also add another property value that allows you to contain bouncy overscroll without disabling pull to refresh. :blink:

  • Like 2
Link to comment
Share on other sites

7 hours ago, marya said:

So I set allowEventDefault:false in this demo. That is the only difference between this demo and the previous one; allowEventDefault is set to false.

 

...

 

Anyway, doing that does indeed seem to fix the issues on almost every browser + device that I tried. Unfortunately, there's still one case where this breaks. I have a Windows 10 touchscreen device called a Nextbook. When I run the demo on the most recent version of Chrome, 75.0.3770.142 (32-bit), the clicks either occur once or twice, randomly. I don't see any pattern to whether there's one click or two happening. I don't know whether it is worth it for your to investigate this particular problem. I did take a look at the Draggable source, and I can see that it must have been quite a nightmare getting it working on many different systems.

As mentioned previously, I was having trouble with my Windows 10 touchscreen device, a Nextbook.

 

On this device, Chrome sometimes fires two clicks, but sometimes only one. I think this is because the Nextbook is pretty old and dodgy, and runs slow. I notice in Draggable there's some code that says "recentlyClicked = (time - clickTime < 40)". It looks to me like if two clicks occur with less than 40 ms between them, GreenSock is going to count this as "recentlyClicked", and call preventDefault on the event. But for my dodgy old Nextbook, it can happen that there's more than 40 ms between these two clicks.

 

BTW the two clicks appear to be coming from GreenSock. If I remove all the draggable code, I never see two clicks. This kinda confused by this. Haven't read all the responses yet, so maybe something is there.

 

It could be that I'm reading the code wrong or that something else is going on. The problem definitely goes away on a brand new iPad when I set allowEventDefault to false, so at least that problem is solved. And the "2-click" issue definitely goes away when I remove Draggable code.

 

I'm going to bite the bullet, and buy a newer Windows tablet for testing to see if my problems go away there. Unfortunately, some users may have ancient devices which could cause this type of problem for them, too...

Link to comment
Share on other sites

7 hours ago, Dipscom said:

Although we can't offer help on full site/application builds, we are more than happy to help with anything GSAP related so, if you find yourself stuck, let us know and I am sure there will always be someone around to lend a hand.

Dipscom, Just so it's clear, I didn't expect anyone to help with anything other than GreenSock issues. If/when I find an issue, I would only bring it to the forum's attention if I could isolate it outside of my application. Already I'm really happy with the support I've gotten using the forum.

Link to comment
Share on other sites

7 hours ago, Sembiance said:

So the whole "double click" thing on iOS may be a general iOS behavior with JavaScript. I've found a couple references to it out on Google.

 

Adding this meta tag fixes the problem in your test case:


<meta name="viewport" content="width=device-width">

 

I think by specifying a "viewport" you are telling iOS that you are going to handle touch events yourself and not to 'simulate' any sort of onClick type events. Or something :)

 

On an somewhat related note, I also find this script very useful when needing to drag things with Greensock Draggable on iOS: https://github.com/lazd/iNoBounce

It prevents the whole screen from bouncing around when dragging things.

That's ... bizarre! I added the viewport meta tag to my demo page, and that did fix the issue for the demo page on the iPad (even with allowEventDefault set to true).

 

My site's index.html page does have a meta viewport tag, though. So, for my card game at least (far more complicated than the demo I created), the "double click" thing is definitely coming from Draggable. When I remove the Draggable code, my other click events fire appropriately. When the Draggable is added, the click event fires twice on the iPad, which causes things to break.

 

But then it got fixed, specifically on the iPad, when I set allowEventDefault to false. So I'm still scratching my head on this one.

 

I will report back once I get the new Win touchscreen device, some time next week, to let people know the behavior on that device.

  • Like 1
Link to comment
Share on other sites

  • 3 weeks later...

Hi,

 

It has taken me a while to get back to testing this issue. As a reminder, my demo has a Draggable applied to a div. The Draggable does not have an onClick method, and it has allowEventDefault:false. The div also has a click EventListener applied to it, separately, and when the div is clicked (or touched), it should increment a counter which is displayed inside it. I was having a problem on an iPad, and the problem went away when I set allowEventDefault:false as suggested above.

 

However, in my post above, I mentioned that I had an old Win touchscreen which was randomly producing double or single clicks for the demo which works everywhere else.

 

I bought a cheap Windows 10 touchscreen device: an "RCA Cambio 10.1" (2-in-1) Windows Tablet & Keyboard" for $99. The only "extra" thing which I did for that device was to install Chrome browser. Because it's brand new, I expect it to behave better than my old Windows touchscreen, which had been dropped a few times.

 

Unfortunately, this new device has the same problem as the old device. I open the demo linked above in Chrome, and click-click-click. Eventually, I get a double count. That is, instead of clicking once, it seems like my "touch" click results in two clicks happening. For example, I just saw this happen: 1-2-4-5-6-7-8-9-10-11-12-14-15-16-17-19... so it's not constantly happening, but it's frequent enough to be reproducible pretty easily.

 

Is there something else I can try to make this work?

 

At this point, I am afraid to go with GreenSock for my drag-n-drop solution because I worry that users with Win touchscreen devices are going to report problems.

Link to comment
Share on other sites

I noticed the topic on Draggable and I just wanted to chime in FWIW.



Just a quick question on why your using click? Click is pseudo event which is created by the browser after a mouse down and corresponding mouse up.

 

If you change you code to use the pointer events then it will work with all types of input and not just clicks..
 

adrag.addEventListener("pointerup", clickHandler);

Then you can modify your click handler like this:
 

if (dragger && dragger.isDragging) return;

You should also store the dragger after you create it where you store nClicks like so:

 

 var nClicks = 0, dragger;

Then assign it after you create arr...

 

var arr = Draggable.create("#adrag", {
...

  dragger = arr[0];

With those changes I don't see any double click issues.

 

See the Pen NQOaLx by juliusfriedman (@juliusfriedman) on CodePen



Also I wouldn't prevent the default as the object was just made draggable there, I would let GSAP handle the bounds with the bounds appropriate.

If you need to preventDefault then you will also need to do so for the mousedown / pointerdown which precedes the click event to cancel it.

 

Let me know if I can help further!

 

You can see an example of how I used Draggable here: 

See the Pen BXqdMv by juliusfriedman (@juliusfriedman) on CodePen



That reduces your example to:

 

var resetClicks = function () {
			nClicks = 0;
			showNumberClicks();
		};
            var clickHandler = function () {
            if (dragger && dragger.isDragging) return;
			nClicks++;
			console.log("clicked " + nClicks);
			showNumberClicks();
		};
            var showNumberClicks = function () {
			var el = document.getElementById("adrag");
			el.innerHTML = nClicks + " click" + (nClicks === 1 ? "" : "s");
		};
            var nClicks = 0, dragger;

		var doOnLoad = function() {
			var adrag = document.getElementById("adrag");
			var arr = Draggable.create("#adrag", {
				zIndexBoost: false,
				cursor: "inherit",
				type: "top,left",
				force3D: false,
				allowEventDefault: true,
				allowContextMenu: true,
				edgeResistance: 0.35,
				bounds: "#container",
				allowEventDefault: false,
				throwProps: false,
				liveSnap: false,
				
				onClick: function (event) {
                    clickHandler();
				}
				
            });
            dragger = arr[0];
		};

doOnLoad();

Which has been verified here:

 

See the Pen NQOaLx by juliusfriedman (@juliusfriedman) on CodePen

 

And has no issues that I can tell..

 

  • Like 3
Link to comment
Share on other sites

Hey guys,

 

Just playing around with things and I wanted you to notice something with @marya's example...

 

edgeResistance: 0.35

 

In my pen @ 

See the Pen NQOaLx by juliusfriedman (@juliusfriedman) on CodePen

 it seems that edgeResistance causes some type of issue when the card goes out of bounds which results in subsequently clicks not being ever fired again...

To replicate just browse to the pen and drag the card out of bounds and then try to click on it and you will see that there are no more click events being fired....

If you change the

 

edgeResistance: 0

 

The problem goes away..

 

So I have renamed my pen (edgeResistance issue).

 

Interested to see why that happens but I only have the minified code..

 

Seems like it is indeed a bug... The only thing I ever encountered like this was due to me having the minX and maxX properties opposite of what one would expect, the minX was greater than the maxX.

 

Not quite sure why edgeResistance would do that but it seems to have something to do with the calculation for hit testing. 

 

If I find anything else I will let you know!

  • Like 2
Link to comment
Share on other sites

Hey Julien, Thanks for the input!

Regarding "click" events, I added a listener for that because it's required for other behaviors in the framework that I'm using for my site, which responds to click events in many, many different places - without any problems up until this "dragging" issue. At least, it hasn't caused any problems that I know about.

I think if I go with pointerup, I'm going to have to mess around with the general framework quite a bit, and I fear this will affect other games at the site. However, I'll take a look to see if I can make that work.

Just a thing to keep in mind: the source for this site is huge, and my little demo was just an attempt to isolate this specific issue. Reworking the entire site to use the GreenSock framework would be a very time-consuming task, and kind of risky (e.g. what if I couldn't reproduce all the current behavior? I think I could, but there's a risk). I was hoping to do a surgical replacement of the current drag-n-drop solution with GreenSock's Draggable. I get the impression that GreenSock plays nice with other frameworks (at least it does with jQuery, but I'm not using jQuery on my site).

Anyway, I haven't given up on GreenSock yet. It may be that I just need to play around with it more.

Thanks for the info on edgeResistance. That would definitely be a problem for my site. I'll look to see if I can reproduce it in the case I've been looking at.

Link to comment
Share on other sites

Hey Julien, I browsed to

See the Pen NQOaLx by juliusfriedman (@juliusfriedman) on CodePen

(please verify the link) and I don't see the issue you mentioned with edgeResistance. Please double-check? I'm using a desktop computer with a mouse for testing this. I went straight to the pen and dragged the div off to the right halfway across the page. When it popped back its container, I clicked it, and it responded by showing 1 click, 2 clicks, 3 clicks, etc.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...