Modernizr / Modernizr

Modernizr is a JavaScript library that detects HTML5 and CSS3 features in the user’s browser.
https://www.npmjs.com/package/modernizr
MIT License
25.62k stars 2.99k forks source link

touch event support #84

Closed michaelhixson closed 13 years ago

michaelhixson commented 14 years ago

I contacted Paul Irish already about an issue with how touch support is currently detected in Modernizr 1.2. iPhones and iPads fire a mousemove event when an element gains focus, and currently Modernizr takes that to mean touch is not supported. The reason for that, as I understand it, was that Chrome supports touch events even though it might not be possible for the user to trigger them. I saw that Chrome's developer build does not have this behavior and suggested that the mousemove detection be removed, but apparently that behavior made it into the stable build of Chrome (5.0)? In that case I suggest one of the following, in order of preference:

a) Don't include this feature in Modernizr for now. b) Make it very clear in the documentation that it only tests whether the browser supports touch events, not that touch events can be triggered by the user, and point out Chrome as an example. I don't know that this would be useful - the developer might as well bind his handlers to touch events regardless, and if those events come, do whatever. c) Change the fallback test to require two mousemove events in rapid succession (less than 50ms apart?) before it labels the document as "no-touch". AFAIK focusing on an anchor / input element on an iPhone/iPad only triggers a single mousemove event whereas a typical real mouse movement would trigger a whole bunch of events. I don't especially like this option because if the developer writes different CSS rules based on touch / no-touch, a user might see the UI change as he moves his mouse for the first time. Also, while it might not be typical, who is to say a touch-inputting user wouldn't trigger those mousemove events within the 50ms (or whatever) threshold? I guess the real problem here is iPhones/iPads firing mousemove events in the first place... there is no mouse! Fire a "touchenter" or something. Thirdly, what if the user is capable of generating both touch- and mouse-based events? d) Change the behavior of the test to wait until a touchstart event is observed before it returns positive, don't have a "no-touch" state (only a "touch" state and an "unknown-touch" state) and make it clear in the documentation that property is lazy-initialized. A potential issue with this is the developer might swallow touchstart events on some elements, preventing bubbling, so although a touchstart event has fired, we won't know unless we've bound a handler to every element in the document.

paulirish commented 13 years ago

We had some discussion off-ticket..

I suggested maintaining the current test but using a UA sniff for chrome to disqualify a positive test in Chrome <6, along with a note that points to this ticket and asks for alternatives.

Faruk said "one of the three founding principles behind Modernizr is to bring an end to UA sniffing. Hence, the practice is not permitted under any circumstances in the library itself.

"If we can't find a standards-compliant, reliable way to add a certain feature, we have to omit it and point to the violating browser(s) that force our hand."

So we're going with option A.

We'll keep this ticket open as we want to add this in the future. In the meantime, if you're comfortable with the false positive you can add this in via the plugin architecture:

Modernizr.addTest('touch',function(){
    return !!('ontouchstart' in window);
})
paulirish commented 13 years ago

John David Dalton has a solution for this that is lazy (and has to be lazy).

In all devices it gives accurate results at the point of touch/mousedown. http://dl.dropbox.com/u/513327/touch_event/test2.html

view also: (See: "Issue 2: one file") http://www.quirksmode.org/blog/archives/2010/02/do_we_need_touc.html#link4

Here is his work, adapted as a Modernizr test: http://gist.github.com/492840

Note the warning that Modernizr.touch cannot be checked before the first mousedown/click/touch of the user. Sucks but it's the only way at the moment.

michaelhixson commented 13 years ago

I don't quite understand the purpose of the mousedown handler. If it was not there, Modernizr.touch would be 'undefined' as opposed to 'false' for mouse users, correct?

If you intend for people to differentiate between those two states, then there is a minor issue with iPhones. If upon loading the page, the user's first action is to touch an input type="text", a mousedown event is fired but not a touchstart. At that point Modernizr.touch would be 'false' rather than 'undefined'.

jdalton commented 13 years ago

Ok, I fixed the issue. It should now work even when clicking on an input field first:

http://dl.dropbox.com/u/513327/touch_event/test2.html

michaelhixson commented 13 years ago

Nice. Is there a reason you can't run that code in the try/catch immediately and call that the results of the test, as opposed to waiting for a mousedown/touchstart from the user? I don't have an Android device to test with, but that seems to work for iPhone, iPad, and desktop browsers including Chrome 5.

<script type="text/javascript"> 
(function() {
  var e, div = document.createElement('div'), hasTouchEvents = false;
  try {
    div.addEventListener('touchstart', function() { hasTouchEvents = true; }, true);
    e = document.createEvent('TouchEvent');
    e.initTouchEvent('touchstart');
    div.dispatchEvent(e);
  }
  catch (e) { }

  if (hasTouchEvents) alert('Yes I support touch events');
  else alert('Nope, I don\'t support um :(');
})();
</script> 
jdalton commented 13 years ago

Yah, some versions of Andriod won't throw events until you touch the screen once.

michaelhixson commented 13 years ago

Well, that stinks. :)

paulirish commented 13 years ago

Chrome 6 stable is coming soon (with the touch false positive fixed). When it's out I'm going to add back in the test. 7-day upgrade cycle, wassup. :)

KuraFire commented 13 years ago

A lazy test for touch won't be good enough for quite a few cases where one might want to change the interface (or parts of it) depending on whether the user is on a desktop with a mouse (that includes regular laptops), or a touch-only device like the iPad.

While the lazy test is a good implementation for the next interim release, we'll need to have a better approach for 2.0. Any and all ideas welcome.

P.S. The reason why the iOS devices fire mouseX events on touchdown is undoubtedly because too many sites relied on those events for critical functionality. In other words, if the mobileWebKit team hadn't done that, many sites wouldn't be navigable on the iPhone and iPad. The blame lies in the middle, because it's hard to argue that developers did the wrong thing here.

devongovett commented 13 years ago

How about:

Modernizr.addTest('touchevents', function() {
    return "createTouch" in document;
});
KuraFire commented 13 years ago

Given that Chrome 5 is rapidly dwindling in popularity now that 6 has been out for a while, and that Palm-HP is coming up with WebOS 2.0 soon, it seems a good time to start producing a table of browser & device support for Touch Events in Window, as my original test used for this would determine.

Something like the HTML5/CSS3 Litmus test, but more detailed & extensive.

paulirish commented 13 years ago

so Chrome 6 stable fixed the touch event false positive so we can forget about that now.

Here are the solutions i've seen for touch detection in the wild...

('ontouchstart' in window);

and

try {
  document.createEvent("TouchEvent");
  return true;
} catch (e) {
  return false;
}

and

'createTouch' in document

and

(typeof TouchEvent != "undefined")

and

(typeof Touch == "object")

Sooooo

which one would we use?

also it appears checking MozTouchMove would also catch FF4's multitouch support. though i'm unsure if that's only available if that's a actually a touch device. :/ I'm pretty OK with punting on FF support for now.

KuraFire commented 13 years ago

I'll make a test case page and get as many people as possible to try it out with their touch device and let them submit the results. I suspect we may need to do some combination of tests to have the widest support.

I am also okay with punting on FF support for now.

paulirish commented 13 years ago

Sweet. rip the browserscope code out of http://modernizr.github.com/Modernizr/output.html and adapt it for instant reporting.

(after getting a new user test ID, that is)

paulirish commented 13 years ago

earlier test uncommented in 6cc48e720cef450aaa4e3f6f12dcac670e451620

@kurafire do we have any results from the test page?

paulirish commented 13 years ago

we finalized our testing strategy based on these results http://modernizr.github.com/Modernizr/output.html

and landed it in 1.6

that test also includes Firefox's unique multitouch API... which.. is interesting if people branch their event listeners based on input events..

thanks for your help everyone