How to detect if a device has mouse support? - javascript

I currently use the following test (taken out of Modernizr) to detect touch support:
function is_touch_device() {
var bool;
if(('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {
bool = true;
} else {
injectElementWithStyles(['#media (',prefixes.join('touch-enabled),('),mod,')','{#modernizr{top:9px;position:absolute}}'].join(''), function(node) {
bool = node.offsetTop === 9;
});
}
return bool;
}
But some devices are both touch and mouse driven, so I want a seperate function to detect if a device has mouse support. What's a good way to do this check?
Ultimately my intention is to be able to do these:
if(is_touch_device())
if(has_mouse_support())
if(is_touch_device() && has_mouse_support())

There's a CSS media just for that!
You can check whether some device has a mouse by getting the value of the pointer CSS media feature:
if (matchMedia('(pointer:fine)').matches) {
// Device has a mouse
}
Because it's CSS you don't even need to use JavaScript:
#media (pointer: fine) {
/* Rules for devices with mouse here */
}

I am currently using the following (jQuery) and I haven't found any flaws yet on specific devices
$(window).bind('mousemove.hasMouse',function(){
$(window).unbind('.hasMouse');
agent.hasMouse=true;
}).bind('touchstart.hasMouse',function(){
$(window).unbind('.hasMouse');
agent.hasMouse=false;
});
Explanation: Mouse devices (also touch screen laptops) first fire mousemove before they can fire touchstart and hasMouse is set to TRUE. Touch devices (also for instance iOS which fires mousemove) FIRST fire touchstart upon click, and then mousemove. Then is why hasMouse will be set to FALSE.
The only catch is that this depends on user interaction, the value will only be correct after mouse move or touchstart so cannot be trusted to use on page load.

As mentioned in the question comments, specifically on https://github.com/Modernizr/Modernizr/issues/869, there is no good answer yet.

Answer by #josemmo is not working for me: on android phone with mouse attached matchMedia('(pointer:fine)').matches does not match.
Fortunately, I've succeeded with another media query: hover.
if (matchMedia('(hover:hover)').matches) {
// Device has a mouse
}

var clickHandler = (isMouseEventSupported('click') ? 'click' : 'touchstart');
function isMouseEventSupported(eventName) {
var element = document.createElement('div');
eventName = 'on' + eventName;
var isSupported = (eventName in element);
if (!isSupported) {
element.setAttribute(eventName, 'return;');
isSupported = typeof element[eventName] == 'function';
}
element = null;
return isSupported;
}
This is code from a friend/coworker of mine and he based it off of: http://perfectionkills.com/detecting-event-support-without-browser-sniffing/

There is no immediate way of knowing, you'll have to wait for a touch event or a mouse event.
Presuming you want to detect either mouse or touch you can do the following: listen for touchstart and mousemove (the latter can fire on touch devices without an actual mouse). Whichever one fires first is 99% bound to be what you're looking for.
This does not take in account devices that actually have both.
document.addEventListener('mousemove', onMouseMove, true)
document.addEventListener('touchstart', onTouchStart, true)
function onTouchStart(){
removeListeners()
// touch detected: do stuff
}
function onMouseMove(){
removeListeners()
// mouse detected: do stuff
}
function removeListeners(){
document.removeEventListener('mousemove', onMouseMove, true)
document.removeEventListener('touchstart', onTouchStart, true)
}

As of 2021 pointerevents is implemented in all major browsers.
It gives you the posibility to dynamically detect pointerdevices mouse, touch and pen.
var is_touch_device=(('ontouchstart' in window)||
(navigator.maxTouchPoints > 0)||
(navigator.msMaxTouchPoints > 0));
var has_mouse_support=false;
document.addEventListener("pointermove", function(evt) {
var pointerType=evt.pointerType;
/*** Safari quirk ***/
if(pointerType==="touch"&&evt.height===117.97119140625
&&evt.height===evt.width)pointerType="mouse";
/*** Safari quirk ***/
has_mouse_support=(pointerType==="mouse");
}
It is of course dependent on the user moving the mousepointer.
Even safari on ipadOS 14.4.2 detects it, if AssistiveTouch is activated! But there seems to be some quirks in pointerType detection there. It detects pointerType as mouse first time the mouse is used and no touch has been performed. But if you later use touch, it will not detect and change to pointerType of mouse, if mouse is used after touch! No surprise!
Edit: After some messing around with ipadOS safari i have discovered that, when mouse is used after touch, the pointerevent width and height are the exact same, which in ipadOS 14.4.2 is 117.97119140625 every time mouse is used. This can be used as a not to reliable workaround. Who knows when they will change the width/height? Another peculiarity with pointermove detection in ipadOS, is that mouse move is only detected on buttom press on mouse.
It is not tested with pen on ipad/iphone. Who knows which quirks this will show?

Related

Scroll using Scroll-Bar Only

I am using three.js to allow users to create and edit a 3D model that involves using the scroll-wheel/two finger function, to zoom in and out. I want a second section of the page that is off the screen by default but the user can scroll down to see it. Preferably, this will be done only using the scroll bar, while the scroll-wheel can still be used.
For performance reasons, I'd prefer not to have to use something such as vue.js. Users provide data that remains on their computer that I'm using in both sections. This prevents me from just placing the data on another screen.
Overflow:hidden is out of the question because then I can not scroll to the bottom portion.
I tried using PreventDefault with several different EventListeners but none of them worked properly.
Below is the function that determines the size of the window and should include a function or the code to prevent scrolling.There aren't particular elements that shouldn't scroll, all of them shouldn't.
function onWindowResize() {
var viewWidth;
var viewHeight;
viewHeight=window.innerHeight-315;
//For Mobile
if(!UIactive && innerWidth < 640){
viewWidth= window.innerWidth;
//For Computer & Tablet
} else {
viewWidth= window.innerWidth -317;
if(window.innerHeight < 700){
viewHeight=window.innerHeight-52.67;
//Disable Scrollwheel
window.addEventListener('wheel',function(event){
//mouseController.wheel(event);
event.preventDefault();
}, false);
}
}
camera.aspect = (viewWidth) / (viewHeight);
camera.updateProjectionMatrix();
renderer.setSize(viewWidth, viewHeight);
UI.style.height= (viewHeight+'px');
}
Edit: I tried using the answer to a similar question. This did not achieve the desired result. I changed the code to be both window... and document... and a console.log statement included works but I can still scroll.
this.canvas.addEventListener('wheel',function(event){
mouseController.wheel(event);
return false;
}, false);
I then proceeded to try using preventDefault again and recieved the following error
Unable to preventDefault inside passive event listener due to target being treated as passive
Google Chrome docs say that,
With this intervention wheel/touchpad scrolling won't be blocked on document level wheel event listeners that do not need to call preventDefault() on wheel events.
Thus, you should apply the onmousewheel event on a specific div like so:
<div id="ScrollableDiv" style="height : 900px;background-color : red">
</div>
function stop(){
return false;
}
var div = document.getElementById('ScrollableDiv');
div.onmousewheel= stop;
Please refer this working fiddle.

On Windows Phone's IE Touchstart Event Ends Automatically After Few Seconds

I have a very specific problem. I'm writing a web-page for mobile phones which has a button on it. I'm detecting touchevent on every browser including IE, but on IE it's quite specific. After a few seconds it automatically ends. Can you somehow help me? Here is my code (modified one, but still not working properly):
if (window.navigator.pointerEnabled) {
tapButton.addEventListener("pointerup", function(e) {
e.preventDefault();
addClass(this, 'clicked');
buttonTouched = true;
}, false);
tapButton.addEventListener("pointerdown", function(e) {
e.preventDefault();
removeClass(this, 'clicked');
buttonTouched = false;
}, false);
alert("pointerEnabled");
}
else if (window.navigator.msPointerEnabled) {
tapButton.addEventListener("MSPointerDown", function(e) {
e.preventDefault();
addClass(this, 'clicked');
buttonTouched = true;
}, false);
tapButton.addEventListener("MSPointerUp", function(e) {
e.preventDefault();
removeClass(this, 'clicked');
buttonTouched = false;
}, false);
alert("mspointerEnabled");
}
else {
alert("ordinary touch");
tapButton.addEventListener('touchstart', function(e) {
e.preventDefault();
addClass(this, 'clicked');
buttonTouched = true;
}, false);
tapButton.addEventListener('touchend', function(e) {
e.preventDefault();
removeClass(this, 'clicked');
buttonTouched = false;
}, false);
}
And the html tag has in it:
-ms-touch-action: none !important;
touch-action: none !important;
but that does not help either.
I suspect you are running into a multi-touch issue...
Remember, touch events are not the same as mouse events. You can touch with more than one finger. What happens if you touch with one finger than add a second finger? You get two consecutive touchstart events. The same is probably true for touchend. I suspect user light is right that it's probably triggering a finger release incorrectly...
Please have a look at what is happening to the touches, changedTouches and targetTouches properties of the TouchEvent you get in your listener. I strongly suspect you'll see that there still is a 'finger' left touching... So it went from 2 touches to 1...
Making sure that the (no longer) touching finger is actually the one that's on the button etc is all a lot less simple than the good old mouseup and mousedown events were.
EDIT: I realize your problem is with IE and it's pointer events... However they work mostly the same in that they too support multi-touch (and could thus suffer the same issues). I'm not seeing a property akin to touches, but I do see a pointerId, which can give you the same info (at the cost of some bookkeeping on your end).
This MSDN page has some good info. Especially this code snippet is enlightening I think:
function pointerdownHandler(evt) {
evt.target.setPointerCapture(evt.pointerId);
}
This seems to confirm that, when a finger hits the surface, the contact point gets an ID, which is used to inform you which finger left the surface when you receive the pointerup event.
I'd add some logging that just prints the pointerId on pointerdown and pointerup and I'll bet you will quickly find your solution.

Changing my HTML5 game controls to mouse/touch

So I have my controls set up as space bar to jump but I want my HTML5 game to work everywhere so I need to change it to mouse/touch. Mouse/touch doesnt have a keycode so whats the most optimal way to do it for this code? Do I need to do mouse and touch separately?
if (KEY_STATUS.space && player.dy === 0 && !player.isJumping) {
player.isJumping = true;
player.dy = player.jumpDy;
jumpCounter = 12;
assetLoader.sounds.jump.play();
}
// jump higher if the space bar is continually pressed
if (KEY_STATUS.space && jumpCounter) {
player.dy = player.jumpDy;
}
You are talking about touch events in JavaScript. You will have to add an event listener and catch if the user touches screen. Here you can find a well documented tutorial in how to use it on both touchscreen and mouse.
Check out the first answer, you will have to do it like that. You will have to add an event listener on the canvas itself, like the guy did it on myDiv.
Here is an example I wrote (don't mind me being weird, please):
<div id="fakeCanvas">
Click or touch me!
</div>
<script type="text/javascript">
var fakeCanvas = document.getElementById('fakeCanvas');
function methodToInvokeForClick() {
alert('I have been clicked!');
}
function methodToInvokeForTouch() {
alert('I have been touched!');
}
fakeCanvas.addEventListener("click", methodToInvokeForClick, false);
fakeCanvas.addEventListener("touchstart", methodToInvokeForTouch, false);
</script>
I think you can take it over from now. What I did to the fakeCanvas div, you will have to do the same to your canvas by giving it an id etc. Good luck!

Mobile timed touch event

I'm trying to set up an event to display some sharing options using touchstart & touchend. This is a pretty standard function in native apps but I haven't seen it much on the mobile web. The event will allow users to tap the main content area of the site and it the tap is longer than 1 second then an options box will slide on screen displaying options for sharing the page content.
The function below works for the first event, but if users trigger the event more than onces it fires without the 1 second requirement to fire the event.
Can anyone suggest a better approach or see why the timer isn't working every time?
if ( typeof ontouchstart != 'undefined' && typeof ontouchend != 'undefined' ) {
var touchStartOrClick = 'touchstart', touchEndOrClick = 'touchend';
} else {
var touchStartOrClick = 'click', touchEndOrClick = 'click';
};
function shareTog(){
$('.sharing-pop').animate({width: 'toggle'});
}
var touchTrigger;
$('#content').bind(touchStartOrClick, function(){
setInterval(function(){
touchTrigger = true;
}, 1000);
}).bind(touchEndOrClick, function(){
window.clearInterval();
if(touchTrigger == true){
shareTog();
touchTrigger = false;
}
});
I'm aware this would be much easier to do with jQuery Mobile but unfortunately it's not an option.
zepto is a good choice for you. Its touch module offers a good event handler.
Or you can check the source code of zepto touch
I catch your drift but I dont think it's a good solution in a web browser.
On Android for example, you already get a popup when you're longpressing content on a webpage.
You will have to block that event handler, but that means you're going to get native.

JS: How to prevent the default action on images in browsers?

In IE, for example, when you press the left button on an image and keeping it pressed try to move the mouse, the drag n' drop action is taking place; how could I prevent this default action so that doing that way nothing will happen. I am building an image cropper, so you should understand why I need that. I am not much interested in knowing how to do so with help of jQuery or the like. As I study JavaScript, I prefer coding in plain-vanilla JS. It is important for me to learn how to make it cross-browser if there are any differences for such a thing.
Just like August's, but plain JS:
var imgs = document.getElementById("my_container")
.getElementsByTagName("img");
for (var i = 0; i < imgs.length; i++) {
imgs[i].onmousedown = function () {
return false;
};
}
If you want to do it 'new-style', google for 'addEventListener()' (all browsers but...) and 'attachEvent()' (...IE) methods.
Here's one in jQuery:
$("#my_container img").mousedown(function () {
return false;
});
http://www.google.com/search?q=cross+browser+event+hooking will probably teach you everything you need to know about cross browser event hooking. I don't know how to hook events without a framework, because that's an edge case IMHO. In The Real World (tm), you'll always use a framework.
The core here is that you have to stop the mousedown event from running. This will make drag and drop impossible, if you hook the event on text you won't be able to select that text, and so on.
If you're building an image cropper, you're going to put some kind of overlay on the image, probably a relatively or absolutely positioned div, inside of which you will "draw" a rectangle when the user clicks, holds and drags. This will make it impossible for the user to drag the image itself, so no fix for that is needed.
Even if you do not use an overlay, you are still going to hook the mousedown event - there is no other way to implement a JS cropper as far as I know. Hooking that event will by itself be enough to prevent the browser from initiating a drag and drop action.
I'm using code similar to the following to prevent dragging, which has the advantage of targetting actual drag-related events rather than the generic mousedown (which could conceivably have side-effects). Works in all the mainstream browsers except Opera.
function cancellingEventHandler(evt) {
evt = evt || window.event;
if (evt.preventDefault) {
evt.preventDefault();
} else if (typeof evt.returnValue !== "undefined") {
evt.returnValue = false;
}
return false;
}
function disableDragging(node) {
node.ondragstart = cancellingEventHandler;
node.ondraggesture = cancellingEventHandler;
}
disableDragging( document.getElementById("anImage") );

Categories

Resources