Accessing touch properties on Windows Chrome #74 - javascript

I was experiencing the issue raised here and here. My d3 app works perfectly on Chrome via a touch display on Mac, but d3.drag failed when I switched to the Windows production machine running Chrome v.74. I applied the solution .touchable(navigator.maxTouchPoints), as suggested by the linked pages above. This allowed me to drag the element in Windows Chrome v.74 using the touch screen, but am now getting:
UncaughtTypeError: Failed to execute 'elementFromPoint' on 'Document': The provided double value is non-finite.
so my drag events aren't firing.
I am using document.elementFromPoint() to detect when the dragged element is over another element:
this.svg.dragCirclesGroup
.call(drag()
.touchable(navigator.maxTouchPoints)
.on("start", this.dragStarted)
.on("drag", this.dragged)
.on("end", this.dragEnded));
dragged() {
select(this).attr("transform","translate("+[event.x,event.y]+")")
let hitZone = select(document.elementFromPoint(event.sourceEvent.clientX, event.sourceEvent.clientY)).attr("id");
if ((hitZone == "yHitZone") || (hitZone == "xHitZone")) {
select('body').classed("plus", true);
} else {
select('body').classed("plus", false);
}
}
This is a touch-only issue, as the drag and document.elementFromPoint work perfectly when I use a mouse.

The solutions offered by Can't get coordinates of touchevents in Javascript on Android devices and Is there an equivalent to e.PageX position for 'touchstart' event as there is for click event? don't apply in Chrome #74 for Windows 10. To access the applicable TouchLists and coordinates for drag event e, you would use
e.sourceEvent.touches.item(0).clientX and e.sourceEvent.touches.item(0).clientY
for dragging and
e.sourceEvent.changedTouches.item(0).clientX and e.sourceEvent.changedTouches.item(0).clientY
for the coordinates upon drag end.
You can see this by logging e.sourceEvent. This SO answer, which details the different Touch Lists, is helpful as well.
So to execute document.elementFromPoint in this environment, you would use
document.elementFromPoint(e.sourceEvent.changedTouches.item(0).clientX, e.sourceEvent.changedTouches.item(0).clientY)

Related

JsPlumb Touch Screen

I have an application that use JsPlumb Framework. It works fine on a desktop, however when using an touch device it fails to work correctly.
I have an object that can be dragged around the screen, this works fine on both a touch device and desktop. However I have also got an action that when a user click on the device it can set a connector to drag a line to another object to join them together. However on the touch devices it fails to set the connector.
The touch device will draw an icon but it will display appear straight away. Is this before the touch device can't tell the difference between the drag/touch option.
I have added some code to see if it has something to do with my code.
$("#container").append(state)
jsPlumb.draggable("state" + i);
jsPlumb.makeSource($('.item'), {
connector: 'StateMachine',
});
jsPlumb.makeTarget($('.item'), {
anchor: 'Continuous',
reattach:true,
isTarget:true,
beforeDrop:function(params) {
'Some code'
}
});
EDIT
JSFiddle
http://jsfiddle.net/8jMqG/6/
EDIT AGAIN
From the docs the following is showed
Tip: use the three-argument addEndpoint method for common data
One thing that happens quite often is that you have an Endpoint whose appearance and behaviour is largely the same between usages on different elements, with just a few differences.
var exampleGreyEndpointOptions = {
endpoint:"Rectangle",
paintStyle:{ width:25, height:21, fillStyle:'#666' },
isSource:true,
connectorStyle : { strokeStyle:"#666" },
isTarget:true
};
Which is what I'm doing on the jsfiddle.
Thanks,
James
I have been using jsPlumb for some time now. My app works both on desktop & mobile. Of course, there's a difference between click & touch.
I use jquery-ui-touchpunch for mobile device support; clicking, dragging objects/connections & creating connections works smooth on mobile too.
http://touchpunch.furf.com/
Update
After creating the endpoints or making certain elements source & target, the next step is to join them for the connection to appear. Of course, if the sole-aim is to just connect, then you can skip creating endpoints/sources/targets & directly join them using
jsPlumb.connect({
source:"element1",
target:"element2",
anchors:["Right", "Left" ],
endpoint:"Rectangle",
endpointStyle:{ fillStyle: "yellow" }
});
DOCS
Currently, you're trying to make the same element(s) .item both source & target.
Also, you're trying to make the same elements source & target with every click (event). Why repeating it ?
APIDOCS
I guess there's some confusion in what you need and what the code says.

Modernizr.touch returns true on firefox browser

I have written a peace of code to get the event based on touch and non-touch. Its working all other browsers and devices, but Firefox. Default FF return the true.
var thumbsEvent, isTouch = Modernizr.touch; // detect the touch
if(isTouch){
thumbsEvent = 'click';//on touch surface, click
}
else {
thumbsEvent = 'mouseover';//on non touch surface, mouseover
}
Is there a way to manage this issue.
Example fiddle
On behalf of Modernizr - We're really sorry about this.
Modernizr.touch has been renamed Modernizr.touchevents in the yet-to-be-released version 3.0, as it is a far more accurate description of the detect. Basically, all this detect is doing is checking for the existence of touch events, and returning true if they are found. Desktop chrome does the same thing if you enable developer tools. It just means that your version of firefox on your laptop is reporting support of touch events, for several possible reasons.
i had the same issue, this fixed it for me: in firefox go to "about:config", then search for the setting "dom.w3c_touch_events.enabled" and reset that one. i then had to restart firefox and afterwards "Modernizr.touch" correctly returned "false" for me.
source: https://github.com/Modernizr/Modernizr/issues/1225
Have same issue: Modernizr.touch returns true at desktop FireFox 33.1, Mac OS.
My solution: using mobile-first approach, apply all touch-related bells-and-whistles by default. If Modernizr detect .no-touch then apply (or disable) some site features for desktop users.
I had the same issue some time ago.
The problem was that some laptop models come with a version with touchscreen. We found out that if one uses such a model, Modernizr.touch returns true even if this person is using the non-touch laptop model version.
Now, click-events do work on touch devices, but not the other way round. So we worked around this limitation by adding an additional check:
var thumbsEvent, isTouch = Modernizr.touch;
var isAndroid = ...; // Android detection code
var isIOs = ...; // iOS detection code
// touch is only supported on iOS and Android
// devices, which have for sure a touch interface
if(isTouch && (isAndroid || isIOs) ){
thumbsEvent = 'click';
}
else {
thumbsEvent = 'mouseover';
}
That's probably not an optimal solution, because you need to do the device detection with user-agent sniffing instead of feature detection, which was probably why you used Modernizr in the first place.
Also, devices which support touch but are neither iOS nor Android will be excluded from touch events.
There is a temporary fix to this. I was having the same problem so what i did was i did a little digging to find an html5 property which gets detected by FF but not on mobile devices and i found flexbox to be one of them. (Read: http://html5please.com/#flexbox).
So below is the code you can use to have your problem fixed:
var isTouch = Modernizr.touch, // detect the touch
isFlex = Modernizr.flexbox; // detect flexbox
if (isTouch) {
// Detect if FF
if (isFlex) ) {
thumbsEvent = 'mouseover'; //on FF, mouseover
} else {
thumbsEvent = 'click';//on non-FF touch surface, click
}
} else {
thumbsEvent = 'mouseover';//on non touch surface, mouseover
}
Please do note that this is temporary, if mobile devices start supporting flexbox it won't work.
It's a know Firefox Bug at least since FF27: https://bugzilla.mozilla.org/show_bug.cgi?id=970346

Firefox seems to ignore event.preventDefault()

I'm working on a page where I have a draggable item (a map inside a container). When you click and drag with the mouse, the map moves and everything is fine. I then wanted to add the same functionality for touch devices (such as a smartphone or a tablet). I searched the net and found a working script that "changes" touch input to mouseinput and thus making it possible to drag the map without dragging the entire page (which is standard behaviour). This is done using the line
event.preventDefault()
It is even possible to have click events as well by timing how long apart the touches are. I am no coding genius and all this programming was done by others. You can see the discussion at Javascript Drag and drop for touch devices (I have used the original code in the top answer as well as the timing code in the answer just below the top answer).
So far, so good. It is now possible to drag the map around and click any links on it or the page just as you would, if you were using a mouse. This works on all touch browsers I have tried (I haven't tried a lot, but the ones I've tried work). The only problem is, that you cannot drag the page itself around, since the default behaviour of the touch has been disabled. This is a problem when the content of the page (for instance the size of the map container) is larger than the browser window.
Luckily an answer was provided for this as well (it is the bottom-most answer on the page I linked to above): replacing
event.preventDefault()
with
if (touches.length > 1) event.preventDefault();
the default scrolling/resizing etc. of a touch works if you use one finger, but if you use more than one finger, the default behaviour is prevented. In other words: If you use two fingers, you can drag the map around without dragging the page (just as before), but if you use one finger, you can drag the page around! I really like this solution as it seems quite elegant to me.
Well, I added the line and tested on the default browser on my HTC Incredible phone (way to small screen but it is what I have). The default browser is just called "Internet". Everything works perfectly!
So, I test on Firefox and Opera, and unfortunately, they are not working perfectly. It seems that once the
event.preventDefault()
is inside the "if" statement, it is completely ignored, so when I drag the map, it IS dragged (as the touch is still converted to a drag of the mouse), but the page itself is also dragged, regardless of the number of fingers I use. In short: The page behaves as if no
event.preventDefault()
is triggered.
I have looked around for several hours and have come to suspect that the event variable needs to be initialized or imported for Firefox and Opera to be able to use it, as described here: jQuery event.preventDefault() not working in Firefox (JSFiddle included)
My question (at long last): Could this be right, and how do I go about "importing" the event into the "if" statement?
The code is here (the init() function is triggered by body onload)
<script type="text/javascript">
var clickms = 400;
var lastTouchDown = -1;
function touchHandler(event)
{
var touches = event.changedTouches,
first = touches[0],
type = "";
var d = new Date();
switch(event.type)
{
case "touchstart": type = "mousedown"; lastTouchDown = d.getTime(); break;
case "touchmove": type="mousemove"; lastTouchDown = -1; break;
case "touchend": if(lastTouchDown > -1 && (d.getTime() - lastTouchDown) < clickms){lastTouchDown = -1; type="click"; break;} type="mouseup"; break;
default: return;
}
var simulatedEvent = document.createEvent("MouseEvent");
simulatedEvent.initMouseEvent(type, true, true, window, 1,
first.screenX, first.screenY,
first.clientX, first.clientY, false,
false, false, false, 0/*left*/, null);
first.target.dispatchEvent(simulatedEvent);
if (touches.length > 1)
{
event.preventDefault();
}
}
function init()
{
document.addEventListener("touchstart", touchHandler, true);
document.addEventListener("touchmove", touchHandler, true);
document.addEventListener("touchend", touchHandler, true);
document.addEventListener("touchcancel", touchHandler, true);
}
</script>
Edit: I should add that I have tried inserting an alert statement into the if statement, just to see whether Firefox or Opera actually register that a two-finger gesture is performed. The alert is triggered without problems, so perhaps the problem is that once Firefox or Opera has started a two-finger gesture, the standard behaviour of the gesture (draggring or resizing the page) cannot be stopped, at least not in this way.
I seem to have solved the problem (at least on the browsers at my disposal for testing)
What I did was changing
if (touches.length > 1) {
event.preventDefault();
}
to
if (event.touches.length > 1) {
event.preventDefault();
}
It's now possible to drag the draggable element around using two fingers without having the rest of the page move. It's still possible to drag the map with one finger on Firefox and Opera, but this is a minor thing, as soon as you adopt a "use only two fingers when you want to drag" attitude.
The only small issue is that both Firefox and Opera seem to jump back and forth between which fingertip they choose to focus on, making the draggable element jitter somewhat, especially when the fingers are a bit apart. This behaviour is not displayed in the "Internet" browser.

Any way to know if user has touchscreen? [duplicate]

I've written a jQuery plug-in that's for use on both desktop and mobile devices. I wondered if there is a way with JavaScript to detect if the device has touch screen capability. I'm using jquery-mobile.js to detect the touch screen events and it works on iOS, Android etc., but I'd also like to write conditional statements based on whether the user's device has a touch screen.
Is that possible?
UPDATE 2021
To see the old answers: check the history. I decided to start on a clean slate as it was getting out of hands when keeping the history in the post.
My original answer said that it could be a good idea to use the same function as Modernizr was using, but that is not valid anymore as they removed the "touchevents" tests on this PR: https://github.com/Modernizr/Modernizr/pull/2432 due to it being a confusing subject.
With that said this should be a fairly ok way of detecting if the browser has "touch capabilities":
function isTouchDevice() {
return (('ontouchstart' in window) ||
(navigator.maxTouchPoints > 0) ||
(navigator.msMaxTouchPoints > 0));
}
But for more advanced use cases far more smarter persons than me have written about this subject, I would recommend reading those articles:
Stu Cox: You Can't Detect a Touchscreen
Detecting touch: it's the 'why', not the 'how'
Getting touchy presentation by Patrick H. Lauke
Update: Please read blmstr's answer below before pulling a whole feature detection library into your project. Detecting actual touch support is more complex, and Modernizr only covers a basic use case.
Modernizr is a great, lightweight way to do all kinds of feature detection on any site.
It simply adds classes to the html element for each feature.
You can then target those features easily in CSS and JS. For example:
html.touch div {
width: 480px;
}
html.no-touch div {
width: auto;
}
And Javascript (jQuery example):
$('html.touch #popup').hide();
As Modernizr doesn't detect IE10 on Windows Phone 8/WinRT, a simple, cross-browser solution is:
var supportsTouch = 'ontouchstart' in window || navigator.msMaxTouchPoints;
You only ever need to check once as the device won't suddenly support or not support touch, so just store it in a variable so you can use it multiple times more efficiently.
Since the introduction of interaction media features you simply can do:
if(window.matchMedia("(pointer: coarse)").matches) {
// touchscreen
}
https://www.w3.org/TR/mediaqueries-4/#descdef-media-any-pointer
Update (due to comments): The above solution is to detect if a "coarse pointer" - usually a touch screen - is the primary input device. In case you want to dectect if a device with e.g. a mouse also has a touch screen you may use any-pointer: coarse instead.
For more information have a look here: Detecting that the browser has no mouse and is touch-only
Using all the comments above I've assembled the following code that is working for my needs:
var isTouch = (('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0));
I have tested this on iPad, Android (Browser and Chrome), Blackberry Playbook, iPhone 4s, Windows Phone 8, IE 10, IE 8, IE 10 (Windows 8 with Touchscreen), Opera, Chrome and Firefox.
It currently fails on Windows Phone 7 and I haven't been able to find a solution for that browser yet.
Hope someone finds this useful.
I like this one:
function isTouchDevice(){
return window.ontouchstart !== undefined;
}
alert(isTouchDevice());
If you use Modernizr, it is very easy to use Modernizr.touch as mentioned earlier.
However, I prefer using a combination of Modernizr.touch and user agent testing, just to be safe.
var deviceAgent = navigator.userAgent.toLowerCase();
var isTouchDevice = Modernizr.touch ||
(deviceAgent.match(/(iphone|ipod|ipad)/) ||
deviceAgent.match(/(android)/) ||
deviceAgent.match(/(iemobile)/) ||
deviceAgent.match(/iphone/i) ||
deviceAgent.match(/ipad/i) ||
deviceAgent.match(/ipod/i) ||
deviceAgent.match(/blackberry/i) ||
deviceAgent.match(/bada/i));
if (isTouchDevice) {
//Do something touchy
} else {
//Can't touch this
}
If you don't use Modernizr, you can simply replace the Modernizr.touch function above with ('ontouchstart' in document.documentElement)
Also note that testing the user agent iemobile will give you broader range of detected Microsoft mobile devices than Windows Phone.
Also see this SO question
We tried the modernizr implementation, but detecting the touch events is not consistent anymore (IE 10 has touch events on windows desktop, IE 11 works, because the've dropped touch events and added pointer api).
So we decided to optimize the website as a touch site as long as we don't know what input type the user has. This is more reliable than any other solution.
Our researches say, that most desktop users move with their mouse over the screen before they click, so we can detect them and change the behaviour before they are able to click or hover anything.
This is a simplified version of our code:
var isTouch = true;
window.addEventListener('mousemove', function mouseMoveDetector() {
isTouch = false;
window.removeEventListener('mousemove', mouseMoveDetector);
});
There is something better than checking if they have a touchScreen, is to check if they are using it, plus that's easier to check.
if (window.addEventListener) {
var once = false;
window.addEventListener('touchstart', function(){
if (!once) {
once = true;
// Do what you need for touch-screens only
}
});
}
Working Fiddle
I have achieved it like this;
function isTouchDevice(){
return true == ("ontouchstart" in window || window.DocumentTouch && document instanceof DocumentTouch);
}
if(isTouchDevice()===true) {
alert('Touch Device'); //your logic for touch device
}
else {
alert('Not a Touch Device'); //your logic for non touch device
}
This one works well even in Windows Surface tablets !!!
function detectTouchSupport {
msGesture = window.navigator && window.navigator.msPointerEnabled && window.MSGesture,
touchSupport = (( "ontouchstart" in window ) || msGesture || window.DocumentTouch && document instanceof DocumentTouch);
if(touchSupport) {
$("html").addClass("ci_touch");
}
else {
$("html").addClass("ci_no_touch");
}
}
The biggest "gotcha" with trying to detect touch is on hybrid devices that support both touch and the trackpad/mouse. Even if you're able to correctly detect whether the user's device supports touch, what you really need to do is detect what input device the user is currently using. There's a detailed write up of this challenge and a possible solution here.
Basically the approach to figuring out whether a user just touched the screen or used a mouse/ trackpad instead is to register both a touchstart and mouseover event on the page:
document.addEventListener('touchstart', functionref, false) // on user tap, "touchstart" fires first
document.addEventListener('mouseover', functionref, false) // followed by mouse event, ie: "mouseover"
A touch action will trigger both of these events, though the former (touchstart) always first on most devices. So counting on this predictable sequence of events, you can create a mechanism that dynamically adds or removes a can-touch class to the document root to reflect the current input type of the user at this moment on the document:
;(function(){
    var isTouch = false //var to indicate current input type (is touch versus no touch)
    var isTouchTimer
    var curRootClass = '' //var indicating current document root class ("can-touch" or "")
     
    function addtouchclass(e){
        clearTimeout(isTouchTimer)
        isTouch = true
        if (curRootClass != 'can-touch'){ //add "can-touch' class if it's not already present
            curRootClass = 'can-touch'
            document.documentElement.classList.add(curRootClass)
        }
        isTouchTimer = setTimeout(function(){isTouch = false}, 500) //maintain "istouch" state for 500ms so removetouchclass doesn't get fired immediately following a touch event
    }
     
    function removetouchclass(e){
        if (!isTouch && curRootClass == 'can-touch'){ //remove 'can-touch' class if not triggered by a touch event and class is present
            isTouch = false
            curRootClass = ''
            document.documentElement.classList.remove('can-touch')
        }
    }
     
    document.addEventListener('touchstart', addtouchclass, false) //this event only gets called when input type is touch
    document.addEventListener('mouseover', removetouchclass, false) //this event gets called when input type is everything from touch to mouse/ trackpad
})();
More details here.
I used pieces of the code above to detect whether touch, so my fancybox iframes would show up on desktop computers and not on touch. I noticed that Opera Mini for Android 4.0 was still registering as a non-touch device when using blmstr's code alone. (Does anyone know why?)
I ended up using:
<script>
$(document).ready(function() {
var ua = navigator.userAgent;
function is_touch_device() {
try {
document.createEvent("TouchEvent");
return true;
} catch (e) {
return false;
}
}
if ((is_touch_device()) || ua.match(/(iPhone|iPod|iPad)/)
|| ua.match(/BlackBerry/) || ua.match(/Android/)) {
// Touch browser
} else {
// Lightbox code
}
});
</script>
Actually, I researched this question and consider all situations. because it is a big issue on my project too. So I reach the below function, it works for all versions of all browsers on all devices:
const isTouchDevice = () => {
const prefixes = ['', '-webkit-', '-moz-', '-o-', '-ms-', ''];
const mq = query => window.matchMedia(query).matches;
if (
'ontouchstart' in window ||
(window.DocumentTouch && document instanceof DocumentTouch)
) {
return true;
}
return mq(['(', prefixes.join('touch-enabled),('), 'heartz', ')'].join(''));
};
Hint: Definitely, the isTouchDevice just returns boolean values.
Check out this post, it gives a really nice code snippet for what to do when touch devices are detected or what to do if touchstart event is called:
$(function(){
if(window.Touch) {
touch_detect.auto_detected();
} else {
document.ontouchstart = touch_detect.surface;
}
}); // End loaded jQuery
var touch_detect = {
auto_detected: function(event){
/* add everything you want to do onLoad here (eg. activating hover controls) */
alert('this was auto detected');
activateTouchArea();
},
surface: function(event){
/* add everything you want to do ontouchstart here (eg. drag & drop) - you can fire this in both places */
alert('this was detected by touching');
activateTouchArea();
}
}; // touch_detect
function activateTouchArea(){
/* make sure our screen doesn't scroll when we move the "touchable area" */
var element = document.getElementById('element_id');
element.addEventListener("touchstart", touchStart, false);
}
function touchStart(event) {
/* modularize preventing the default behavior so we can use it again */
event.preventDefault();
}
I would avoid using screen width to determine if a device is a touch device. There are touch screens much larger than 699px, think of Windows 8. Navigatior.userAgent may be nice to override false postives.
I would recommend checking out this issue on Modernizr.
Are you wanting to test if the device supports touch events or is a touch device. Unfortunately, that's not the same thing.
No, it's not possible. The excellent answers given are only ever partial, because any given method will produce false positives and false negatives. Even the browser doesn't always know if a touchscreen is present, due to OS APIs, and the fact can change during a browser session, particularly with KVM-type arrangements.
See further details in this excellent article:
http://www.stucox.com/blog/you-cant-detect-a-touchscreen/
The article suggests you reconsider the assumptions that make you want to detect touchscreens, they're probably wrong. (I checked my own for my app, and my assumptions were indeed wrong!)
The article concludes:
For layouts, assume everyone has a touchscreen. Mouse users can use
large UI controls much more easily than touch users can use small
ones. The same goes for hover states.
For events and interactions, assume anyone may have a touchscreen.
Implement keyboard, mouse and touch interactions alongside each other,
ensuring none block each other.
Many of these work but either require jQuery, or javascript linters complain about the syntax. Considering your initial question asks for a "JavaScript" (not jQuery, not Modernizr) way of solving this, here's a simple function that works every time. It's also about as minimal as you can get.
function isTouchDevice() {
return !!window.ontouchstart;
}
console.log(isTouchDevice());
One last benefit I'll mention is that this code is framework and device agnostic. Enjoy!
I think the best method is:
var isTouchDevice =
(('ontouchstart' in window) ||
(navigator.maxTouchPoints > 0) ||
(navigator.msMaxTouchPoints > 0));
if(!isTouchDevice){
/* Code for touch device /*
}else{
/* Code for non touch device */
}
It looks like Chrome 24 now support touch events, probably for Windows 8. So the code posted here no longer works. Instead of trying to detect if touch is supported by the browser, I'm now binding both touch and click events and making sure only one is called:
myCustomBind = function(controlName, callback) {
$(controlName).bind('touchend click', function(e) {
e.stopPropagation();
e.preventDefault();
callback.call();
});
};
And then calling it:
myCustomBind('#mnuRealtime', function () { ... });
Hope this helps !
All browser supported except Firefox for desktop always TRUE because of Firefox for desktop support responsive design for developer even you click Touch-Button or not!
I hope Mozilla will fix this in next version.
I'm using Firefox 28 desktop.
function isTouch()
{
return !!("ontouchstart" in window) || !!(navigator.msMaxTouchPoints);
}
jQuery v1.11.3
There is a lot of good information in the answers provided. But, recently I spent a lot of time trying to actually tie everything together into a working solution for the accomplishing two things:
Detect that the device in use is a touch screen type device.
Detect that the device was tapped.
Besides this post and Detecting touch screen devices with Javascript, I found this post by Patrick Lauke extremely helpful: https://hacks.mozilla.org/2013/04/detecting-touch-its-the-why-not-the-how/
Here is the code...
$(document).ready(function() {
//The page is "ready" and the document can be manipulated.
if (('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0))
{
//If the device is a touch capable device, then...
$(document).on("touchstart", "a", function() {
//Do something on tap.
});
}
else
{
null;
}
});
Important! The *.on( events [, selector ] [, data ], handler ) method needs to have a selector, usually an element, that can handle the "touchstart" event, or any other like event associated with touches. In this case, it is the hyperlink element "a".
Now, you don't need to handle the regular mouse clicking in JavaScript, because you can use CSS to handle these events using selectors for the hyperlink "a" element like so:
/* unvisited link */
a:link
{
}
/* visited link */
a:visited
{
}
/* mouse over link */
a:hover
{
}
/* selected link */
a:active
{
}
Note: There are other selectors as well...
The problem
Due to hybrid devices which use a combination of touch and mouse input, you need to be able dynamically change the state / variable which controls whether a piece of code should run if the user is a touch user or not.
Touch devices also fire mousemove on tap.
Solution
Assume touch is false on load.
Wait until a touchstart event is fired, then set it to true.
If touchstart was fired, add a mousemove handler.
If the time between two mousemove events firing was less than 20ms, assume they are using a mouse as input. Remove the event as it's no longer needed and mousemove is an expensive event for mouse devices.
As soon as touchstart is fired again (user went back to using touch), the variable is set back to true. And repeat the process so it's determined in a dynamic fashion. If by some miracle mousemove gets fired twice on touch absurdly quickly (in my testing it's virtually impossible to do it within 20ms), the next touchstart will set it back to true.
Tested on Safari iOS and Chrome for Android.
Note: not 100% sure on the pointer-events for MS Surface, etc.
Codepen demo
const supportsTouch = 'ontouchstart' in window;
let isUsingTouch = false;
// `touchstart`, `pointerdown`
const touchHandler = () => {
isUsingTouch = true;
document.addEventListener('mousemove', mousemoveHandler);
};
// use a simple closure to store previous time as internal state
const mousemoveHandler = (() => {
let time;
return () => {
const now = performance.now();
if (now - time < 20) {
isUsingTouch = false;
document.removeEventListener('mousemove', mousemoveHandler);
}
time = now;
}
})();
// add listeners
if (supportsTouch) {
document.addEventListener('touchstart', touchHandler);
} else if (navigator.maxTouchPoints || navigator.msMaxTouchPoints) {
document.addEventListener('pointerdown', touchHandler);
}
Right so there is a huge debate over detecting touch/non-touch devices. The number of window tablets and the size of tablets is increasing creating another set of headaches for us web developers.
I have used and tested blmstr's answer for a menu. The menu works like this: when the page loads the script detects if this is a touch or non touch device. Based on that the menu would work on hover (non-touch) or on click/tap (touch).
In most of the cases blmstr's scripts seemed to work just fine (specifically the 2018 one). BUT there was still that one device that would be detected as touch when it is not or vice versa.
For this reason I did a bit of digging and thanks to this article I replaced a few lines from blmstr's 4th script into this:
function is_touch_device4() {
if ("ontouchstart" in window)
return true;
if (window.DocumentTouch && document instanceof DocumentTouch)
return true;
return window.matchMedia( "(pointer: coarse)" ).matches;
}
alert('Is touch device: '+is_touch_device4());
console.log('Is touch device: '+is_touch_device4());
Because of the lockdown have a limited supply of touch devices to test this one but so far the above works great.
I would appreceate if anyone with a desktop touch device (ex. surface tablet) can confirm if script works all right.
Now in terms of support the pointer: coarse media query seems to be supported. I kept the lines above since I had (for some reason) issues on mobile firefox but the lines above the media query do the trick.
Thanks
var isTouchScreen = 'createTouch' in document;
or
var isTouchScreen = 'createTouch' in document || screen.width <= 699 ||
ua.match(/(iPhone|iPod|iPad)/) || ua.match(/BlackBerry/) ||
ua.match(/Android/);
would be a more thorough check I suppose.
I use:
if(jQuery.support.touch){
alert('Touch enabled');
}
in jQuery mobile 1.0.1
You can install modernizer and use a simple touch event. This is very effective and works on every device I have tested it on including windows surface!
I've created a jsFiddle
function isTouchDevice(){
if(Modernizr.hasEvent('touchstart') || navigator.userAgent.search(/Touch/i) != -1){
alert("is touch");
return true;
}else{
alert("is not touch");
return false;
}
}
I also struggled a lot with different options on how to detect in Javascript whether the page is displayed on a touch screen device or not.
IMO, as of now, no real option exists to detect the option properly.
Browsers either report touch events on desktop machines (because the OS maybe touch-ready), or some solutions don't work on all mobile devices.
In the end, I realized that I was following the wrong approach from the start:
If my page was to look similar on touch and non-touch devices, I maybe shouldn't have to worry about detecting the property at all:
My scenario was to deactivate tooltips over buttons on touch devices as they lead to double-taps where I wanted a single tap to activate the button.
My solution was to refactor the view so that no tooltip was needed over a button, and in the end I didn't need to detect the touch device from Javascript with methods that all have their drawbacks.
The practical answer seems to be one that considers the context:
1) Public site (no login)
Code the UI to work with both options together.
2) Login site
Capture whether a mouse-move occurred on the login form, and save this into a hidden input. The value is passed with the login credentials and added to the user's session, so it can be used for the duration of the session.
Jquery to add to login page only:
$('#istouch').val(1); // <-- value will be submitted with login form
if (window.addEventListener) {
window.addEventListener('mousemove', function mouseMoveListener(){
// Update hidden input value to false, and stop listening
$('#istouch').val(0);
window.removeEventListener('mousemove', mouseMoveListener);
});
}
(+1 to #Dave Burt and +1 to #Martin Lantzsch on their answers)
Extent jQuery support object:
jQuery.support.touch = 'ontouchend' in document;
And now you can check it anywhere, like this:
if( jQuery.support.touch )
// do touch stuff

What's the best way to detect a 'touch screen' device using JavaScript?

I've written a jQuery plug-in that's for use on both desktop and mobile devices. I wondered if there is a way with JavaScript to detect if the device has touch screen capability. I'm using jquery-mobile.js to detect the touch screen events and it works on iOS, Android etc., but I'd also like to write conditional statements based on whether the user's device has a touch screen.
Is that possible?
UPDATE 2021
To see the old answers: check the history. I decided to start on a clean slate as it was getting out of hands when keeping the history in the post.
My original answer said that it could be a good idea to use the same function as Modernizr was using, but that is not valid anymore as they removed the "touchevents" tests on this PR: https://github.com/Modernizr/Modernizr/pull/2432 due to it being a confusing subject.
With that said this should be a fairly ok way of detecting if the browser has "touch capabilities":
function isTouchDevice() {
return (('ontouchstart' in window) ||
(navigator.maxTouchPoints > 0) ||
(navigator.msMaxTouchPoints > 0));
}
But for more advanced use cases far more smarter persons than me have written about this subject, I would recommend reading those articles:
Stu Cox: You Can't Detect a Touchscreen
Detecting touch: it's the 'why', not the 'how'
Getting touchy presentation by Patrick H. Lauke
Update: Please read blmstr's answer below before pulling a whole feature detection library into your project. Detecting actual touch support is more complex, and Modernizr only covers a basic use case.
Modernizr is a great, lightweight way to do all kinds of feature detection on any site.
It simply adds classes to the html element for each feature.
You can then target those features easily in CSS and JS. For example:
html.touch div {
width: 480px;
}
html.no-touch div {
width: auto;
}
And Javascript (jQuery example):
$('html.touch #popup').hide();
As Modernizr doesn't detect IE10 on Windows Phone 8/WinRT, a simple, cross-browser solution is:
var supportsTouch = 'ontouchstart' in window || navigator.msMaxTouchPoints;
You only ever need to check once as the device won't suddenly support or not support touch, so just store it in a variable so you can use it multiple times more efficiently.
Since the introduction of interaction media features you simply can do:
if(window.matchMedia("(pointer: coarse)").matches) {
// touchscreen
}
https://www.w3.org/TR/mediaqueries-4/#descdef-media-any-pointer
Update (due to comments): The above solution is to detect if a "coarse pointer" - usually a touch screen - is the primary input device. In case you want to dectect if a device with e.g. a mouse also has a touch screen you may use any-pointer: coarse instead.
For more information have a look here: Detecting that the browser has no mouse and is touch-only
Using all the comments above I've assembled the following code that is working for my needs:
var isTouch = (('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0));
I have tested this on iPad, Android (Browser and Chrome), Blackberry Playbook, iPhone 4s, Windows Phone 8, IE 10, IE 8, IE 10 (Windows 8 with Touchscreen), Opera, Chrome and Firefox.
It currently fails on Windows Phone 7 and I haven't been able to find a solution for that browser yet.
Hope someone finds this useful.
I like this one:
function isTouchDevice(){
return window.ontouchstart !== undefined;
}
alert(isTouchDevice());
If you use Modernizr, it is very easy to use Modernizr.touch as mentioned earlier.
However, I prefer using a combination of Modernizr.touch and user agent testing, just to be safe.
var deviceAgent = navigator.userAgent.toLowerCase();
var isTouchDevice = Modernizr.touch ||
(deviceAgent.match(/(iphone|ipod|ipad)/) ||
deviceAgent.match(/(android)/) ||
deviceAgent.match(/(iemobile)/) ||
deviceAgent.match(/iphone/i) ||
deviceAgent.match(/ipad/i) ||
deviceAgent.match(/ipod/i) ||
deviceAgent.match(/blackberry/i) ||
deviceAgent.match(/bada/i));
if (isTouchDevice) {
//Do something touchy
} else {
//Can't touch this
}
If you don't use Modernizr, you can simply replace the Modernizr.touch function above with ('ontouchstart' in document.documentElement)
Also note that testing the user agent iemobile will give you broader range of detected Microsoft mobile devices than Windows Phone.
Also see this SO question
We tried the modernizr implementation, but detecting the touch events is not consistent anymore (IE 10 has touch events on windows desktop, IE 11 works, because the've dropped touch events and added pointer api).
So we decided to optimize the website as a touch site as long as we don't know what input type the user has. This is more reliable than any other solution.
Our researches say, that most desktop users move with their mouse over the screen before they click, so we can detect them and change the behaviour before they are able to click or hover anything.
This is a simplified version of our code:
var isTouch = true;
window.addEventListener('mousemove', function mouseMoveDetector() {
isTouch = false;
window.removeEventListener('mousemove', mouseMoveDetector);
});
There is something better than checking if they have a touchScreen, is to check if they are using it, plus that's easier to check.
if (window.addEventListener) {
var once = false;
window.addEventListener('touchstart', function(){
if (!once) {
once = true;
// Do what you need for touch-screens only
}
});
}
Working Fiddle
I have achieved it like this;
function isTouchDevice(){
return true == ("ontouchstart" in window || window.DocumentTouch && document instanceof DocumentTouch);
}
if(isTouchDevice()===true) {
alert('Touch Device'); //your logic for touch device
}
else {
alert('Not a Touch Device'); //your logic for non touch device
}
This one works well even in Windows Surface tablets !!!
function detectTouchSupport {
msGesture = window.navigator && window.navigator.msPointerEnabled && window.MSGesture,
touchSupport = (( "ontouchstart" in window ) || msGesture || window.DocumentTouch && document instanceof DocumentTouch);
if(touchSupport) {
$("html").addClass("ci_touch");
}
else {
$("html").addClass("ci_no_touch");
}
}
The biggest "gotcha" with trying to detect touch is on hybrid devices that support both touch and the trackpad/mouse. Even if you're able to correctly detect whether the user's device supports touch, what you really need to do is detect what input device the user is currently using. There's a detailed write up of this challenge and a possible solution here.
Basically the approach to figuring out whether a user just touched the screen or used a mouse/ trackpad instead is to register both a touchstart and mouseover event on the page:
document.addEventListener('touchstart', functionref, false) // on user tap, "touchstart" fires first
document.addEventListener('mouseover', functionref, false) // followed by mouse event, ie: "mouseover"
A touch action will trigger both of these events, though the former (touchstart) always first on most devices. So counting on this predictable sequence of events, you can create a mechanism that dynamically adds or removes a can-touch class to the document root to reflect the current input type of the user at this moment on the document:
;(function(){
    var isTouch = false //var to indicate current input type (is touch versus no touch)
    var isTouchTimer
    var curRootClass = '' //var indicating current document root class ("can-touch" or "")
     
    function addtouchclass(e){
        clearTimeout(isTouchTimer)
        isTouch = true
        if (curRootClass != 'can-touch'){ //add "can-touch' class if it's not already present
            curRootClass = 'can-touch'
            document.documentElement.classList.add(curRootClass)
        }
        isTouchTimer = setTimeout(function(){isTouch = false}, 500) //maintain "istouch" state for 500ms so removetouchclass doesn't get fired immediately following a touch event
    }
     
    function removetouchclass(e){
        if (!isTouch && curRootClass == 'can-touch'){ //remove 'can-touch' class if not triggered by a touch event and class is present
            isTouch = false
            curRootClass = ''
            document.documentElement.classList.remove('can-touch')
        }
    }
     
    document.addEventListener('touchstart', addtouchclass, false) //this event only gets called when input type is touch
    document.addEventListener('mouseover', removetouchclass, false) //this event gets called when input type is everything from touch to mouse/ trackpad
})();
More details here.
I used pieces of the code above to detect whether touch, so my fancybox iframes would show up on desktop computers and not on touch. I noticed that Opera Mini for Android 4.0 was still registering as a non-touch device when using blmstr's code alone. (Does anyone know why?)
I ended up using:
<script>
$(document).ready(function() {
var ua = navigator.userAgent;
function is_touch_device() {
try {
document.createEvent("TouchEvent");
return true;
} catch (e) {
return false;
}
}
if ((is_touch_device()) || ua.match(/(iPhone|iPod|iPad)/)
|| ua.match(/BlackBerry/) || ua.match(/Android/)) {
// Touch browser
} else {
// Lightbox code
}
});
</script>
Actually, I researched this question and consider all situations. because it is a big issue on my project too. So I reach the below function, it works for all versions of all browsers on all devices:
const isTouchDevice = () => {
const prefixes = ['', '-webkit-', '-moz-', '-o-', '-ms-', ''];
const mq = query => window.matchMedia(query).matches;
if (
'ontouchstart' in window ||
(window.DocumentTouch && document instanceof DocumentTouch)
) {
return true;
}
return mq(['(', prefixes.join('touch-enabled),('), 'heartz', ')'].join(''));
};
Hint: Definitely, the isTouchDevice just returns boolean values.
Check out this post, it gives a really nice code snippet for what to do when touch devices are detected or what to do if touchstart event is called:
$(function(){
if(window.Touch) {
touch_detect.auto_detected();
} else {
document.ontouchstart = touch_detect.surface;
}
}); // End loaded jQuery
var touch_detect = {
auto_detected: function(event){
/* add everything you want to do onLoad here (eg. activating hover controls) */
alert('this was auto detected');
activateTouchArea();
},
surface: function(event){
/* add everything you want to do ontouchstart here (eg. drag & drop) - you can fire this in both places */
alert('this was detected by touching');
activateTouchArea();
}
}; // touch_detect
function activateTouchArea(){
/* make sure our screen doesn't scroll when we move the "touchable area" */
var element = document.getElementById('element_id');
element.addEventListener("touchstart", touchStart, false);
}
function touchStart(event) {
/* modularize preventing the default behavior so we can use it again */
event.preventDefault();
}
I would avoid using screen width to determine if a device is a touch device. There are touch screens much larger than 699px, think of Windows 8. Navigatior.userAgent may be nice to override false postives.
I would recommend checking out this issue on Modernizr.
Are you wanting to test if the device supports touch events or is a touch device. Unfortunately, that's not the same thing.
No, it's not possible. The excellent answers given are only ever partial, because any given method will produce false positives and false negatives. Even the browser doesn't always know if a touchscreen is present, due to OS APIs, and the fact can change during a browser session, particularly with KVM-type arrangements.
See further details in this excellent article:
http://www.stucox.com/blog/you-cant-detect-a-touchscreen/
The article suggests you reconsider the assumptions that make you want to detect touchscreens, they're probably wrong. (I checked my own for my app, and my assumptions were indeed wrong!)
The article concludes:
For layouts, assume everyone has a touchscreen. Mouse users can use
large UI controls much more easily than touch users can use small
ones. The same goes for hover states.
For events and interactions, assume anyone may have a touchscreen.
Implement keyboard, mouse and touch interactions alongside each other,
ensuring none block each other.
Many of these work but either require jQuery, or javascript linters complain about the syntax. Considering your initial question asks for a "JavaScript" (not jQuery, not Modernizr) way of solving this, here's a simple function that works every time. It's also about as minimal as you can get.
function isTouchDevice() {
return !!window.ontouchstart;
}
console.log(isTouchDevice());
One last benefit I'll mention is that this code is framework and device agnostic. Enjoy!
I think the best method is:
var isTouchDevice =
(('ontouchstart' in window) ||
(navigator.maxTouchPoints > 0) ||
(navigator.msMaxTouchPoints > 0));
if(!isTouchDevice){
/* Code for touch device /*
}else{
/* Code for non touch device */
}
It looks like Chrome 24 now support touch events, probably for Windows 8. So the code posted here no longer works. Instead of trying to detect if touch is supported by the browser, I'm now binding both touch and click events and making sure only one is called:
myCustomBind = function(controlName, callback) {
$(controlName).bind('touchend click', function(e) {
e.stopPropagation();
e.preventDefault();
callback.call();
});
};
And then calling it:
myCustomBind('#mnuRealtime', function () { ... });
Hope this helps !
All browser supported except Firefox for desktop always TRUE because of Firefox for desktop support responsive design for developer even you click Touch-Button or not!
I hope Mozilla will fix this in next version.
I'm using Firefox 28 desktop.
function isTouch()
{
return !!("ontouchstart" in window) || !!(navigator.msMaxTouchPoints);
}
jQuery v1.11.3
There is a lot of good information in the answers provided. But, recently I spent a lot of time trying to actually tie everything together into a working solution for the accomplishing two things:
Detect that the device in use is a touch screen type device.
Detect that the device was tapped.
Besides this post and Detecting touch screen devices with Javascript, I found this post by Patrick Lauke extremely helpful: https://hacks.mozilla.org/2013/04/detecting-touch-its-the-why-not-the-how/
Here is the code...
$(document).ready(function() {
//The page is "ready" and the document can be manipulated.
if (('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0))
{
//If the device is a touch capable device, then...
$(document).on("touchstart", "a", function() {
//Do something on tap.
});
}
else
{
null;
}
});
Important! The *.on( events [, selector ] [, data ], handler ) method needs to have a selector, usually an element, that can handle the "touchstart" event, or any other like event associated with touches. In this case, it is the hyperlink element "a".
Now, you don't need to handle the regular mouse clicking in JavaScript, because you can use CSS to handle these events using selectors for the hyperlink "a" element like so:
/* unvisited link */
a:link
{
}
/* visited link */
a:visited
{
}
/* mouse over link */
a:hover
{
}
/* selected link */
a:active
{
}
Note: There are other selectors as well...
The problem
Due to hybrid devices which use a combination of touch and mouse input, you need to be able dynamically change the state / variable which controls whether a piece of code should run if the user is a touch user or not.
Touch devices also fire mousemove on tap.
Solution
Assume touch is false on load.
Wait until a touchstart event is fired, then set it to true.
If touchstart was fired, add a mousemove handler.
If the time between two mousemove events firing was less than 20ms, assume they are using a mouse as input. Remove the event as it's no longer needed and mousemove is an expensive event for mouse devices.
As soon as touchstart is fired again (user went back to using touch), the variable is set back to true. And repeat the process so it's determined in a dynamic fashion. If by some miracle mousemove gets fired twice on touch absurdly quickly (in my testing it's virtually impossible to do it within 20ms), the next touchstart will set it back to true.
Tested on Safari iOS and Chrome for Android.
Note: not 100% sure on the pointer-events for MS Surface, etc.
Codepen demo
const supportsTouch = 'ontouchstart' in window;
let isUsingTouch = false;
// `touchstart`, `pointerdown`
const touchHandler = () => {
isUsingTouch = true;
document.addEventListener('mousemove', mousemoveHandler);
};
// use a simple closure to store previous time as internal state
const mousemoveHandler = (() => {
let time;
return () => {
const now = performance.now();
if (now - time < 20) {
isUsingTouch = false;
document.removeEventListener('mousemove', mousemoveHandler);
}
time = now;
}
})();
// add listeners
if (supportsTouch) {
document.addEventListener('touchstart', touchHandler);
} else if (navigator.maxTouchPoints || navigator.msMaxTouchPoints) {
document.addEventListener('pointerdown', touchHandler);
}
Right so there is a huge debate over detecting touch/non-touch devices. The number of window tablets and the size of tablets is increasing creating another set of headaches for us web developers.
I have used and tested blmstr's answer for a menu. The menu works like this: when the page loads the script detects if this is a touch or non touch device. Based on that the menu would work on hover (non-touch) or on click/tap (touch).
In most of the cases blmstr's scripts seemed to work just fine (specifically the 2018 one). BUT there was still that one device that would be detected as touch when it is not or vice versa.
For this reason I did a bit of digging and thanks to this article I replaced a few lines from blmstr's 4th script into this:
function is_touch_device4() {
if ("ontouchstart" in window)
return true;
if (window.DocumentTouch && document instanceof DocumentTouch)
return true;
return window.matchMedia( "(pointer: coarse)" ).matches;
}
alert('Is touch device: '+is_touch_device4());
console.log('Is touch device: '+is_touch_device4());
Because of the lockdown have a limited supply of touch devices to test this one but so far the above works great.
I would appreceate if anyone with a desktop touch device (ex. surface tablet) can confirm if script works all right.
Now in terms of support the pointer: coarse media query seems to be supported. I kept the lines above since I had (for some reason) issues on mobile firefox but the lines above the media query do the trick.
Thanks
var isTouchScreen = 'createTouch' in document;
or
var isTouchScreen = 'createTouch' in document || screen.width <= 699 ||
ua.match(/(iPhone|iPod|iPad)/) || ua.match(/BlackBerry/) ||
ua.match(/Android/);
would be a more thorough check I suppose.
I use:
if(jQuery.support.touch){
alert('Touch enabled');
}
in jQuery mobile 1.0.1
You can install modernizer and use a simple touch event. This is very effective and works on every device I have tested it on including windows surface!
I've created a jsFiddle
function isTouchDevice(){
if(Modernizr.hasEvent('touchstart') || navigator.userAgent.search(/Touch/i) != -1){
alert("is touch");
return true;
}else{
alert("is not touch");
return false;
}
}
I also struggled a lot with different options on how to detect in Javascript whether the page is displayed on a touch screen device or not.
IMO, as of now, no real option exists to detect the option properly.
Browsers either report touch events on desktop machines (because the OS maybe touch-ready), or some solutions don't work on all mobile devices.
In the end, I realized that I was following the wrong approach from the start:
If my page was to look similar on touch and non-touch devices, I maybe shouldn't have to worry about detecting the property at all:
My scenario was to deactivate tooltips over buttons on touch devices as they lead to double-taps where I wanted a single tap to activate the button.
My solution was to refactor the view so that no tooltip was needed over a button, and in the end I didn't need to detect the touch device from Javascript with methods that all have their drawbacks.
The practical answer seems to be one that considers the context:
1) Public site (no login)
Code the UI to work with both options together.
2) Login site
Capture whether a mouse-move occurred on the login form, and save this into a hidden input. The value is passed with the login credentials and added to the user's session, so it can be used for the duration of the session.
Jquery to add to login page only:
$('#istouch').val(1); // <-- value will be submitted with login form
if (window.addEventListener) {
window.addEventListener('mousemove', function mouseMoveListener(){
// Update hidden input value to false, and stop listening
$('#istouch').val(0);
window.removeEventListener('mousemove', mouseMoveListener);
});
}
(+1 to #Dave Burt and +1 to #Martin Lantzsch on their answers)
Extent jQuery support object:
jQuery.support.touch = 'ontouchend' in document;
And now you can check it anywhere, like this:
if( jQuery.support.touch )
// do touch stuff

Categories