PDA

View Full Version : Click/touch activates mouseover/jq mouseenter event?



jscheuer1
03-04-2016, 05:48 AM
I'm usually the one who answers these type of questions. But I'm just starting to become aware of this as a potential issue and have only an emulator at the moment to test on, so it might just be an artifact of the emulator (Chrome Device Emulator).

Here's the basic idea. You have an event that is activated on mouseover, more specifically in this case on a jQuery mouseenter event (which is basically just a regular mouseover event filtered through event propagation subroutines so as not to have numerous enter and leave events on a nested element or its children and to activate only when the specific element is entered.

OK got that. Now I'm emulating an iPhone 6 in Landscape default mode under a Chrome browser under Win 7 on a laptop. If I click on the element, it's mouseover (jq mouseenter) event fires. Is this an artifact of the emulator? Or is this what actually what happens when you touch an element that has a mouseover event?

jscheuer1
03-04-2016, 05:55 AM
OK, here's a very simple test page. The emulator will fire the alert on click, which is supposed to emulate a touch/tap event:


<!DOCTYPE html>
<html>
<head>
<title>WTF?</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

</head>
<body>
<input id="wtf" type="button" value="WTF">
<script type="text/javascript">
document.getElementById('wtf').addEventListener('mouseover', function(){alert('wtf');}, false);
</script>
</body>
</html>

Do actual touch devices do that?

Beverleyh
03-04-2016, 06:35 AM
I've done a quick test on iPhone 5S.

These are the events that fire with a tap;
Onclick
Onmousedown
Onmouseover
Onmouseup

This doesn't fire when tapped;
Onmouseenter

Give me a few hours I'll test on Android when I'm back in the office.

Ps - I hit trouble with this while I've been making popups for my calendar (event management) script. I wanted to use mouseenter for the same reason (propagation) but decided to change to mouseover to get them working on touch.

Beverleyh
03-04-2016, 08:12 AM
Same for Android, except that onmouseenter *sometimes* fires if the event is on a focusable element.

jscheuer1
03-04-2016, 01:44 PM
Thanks Beverley. That's kind of sucky though and seems to confirm what the emulator was doing. What I'm seeing there is that when you click (which again is supposed to emulate a tap), the element's mouseover event fires. But what's not so great about it is that when you remove the mouse either by dragging it away or just releasing it, the mouseout event doesn't fire. That doesn't happen unless or until you tap somewhere outside the element.

In the past I just thought touch devices ignored mouseover/out. Now it seems they fire them in ways that are at least potentially counter to what one might expect.

So now I'm thinking I either need to detect touch and not assign any potentially confusing mouseover events for those devices, or I need to find a way to fire the mouseout sequence of events at the appropriate time even if the touch device doesn't fire them in the way a mouse device would.

BTW - The bit about mouseenter is a bit of a red herring. It's simply mouseover in jQuery with a few extras added*. So, first of all one wouldn't expect it to fire at all on any device unless the page loaded had jQuery, though I'm sure some browsers might fire it that way. Those would be in the minority I would think. But if the page has jQuery on it, any firing of mouseover that isn't merely the result of propagation, should and probably will fire the jQuery mouseenter IF it's assigned to the element.

For now, I'm going with this for touch/mouse detection as virtually all browsers are now touch capable whether the device using them is or not:


jQuery(function($) { // determine upon first interaction if touch or mouse is used (not perfect, but better than any other method I'm currently aware of)
var $body = $('body'); // 'fails' if both mouse and touch are available and the non-preferred method is employed by the user first, or if they always use both about evenly
var detectMouse = function(e){ // but since this isn't critical we can let that go for those few individuals
fadeSlideShow.prototype.mouseortouch = e.type;
$body.off('mousedown touchstart', detectMouse);
}
$body.on('mousedown touchstart', detectMouse);
});

Since that, I'm now trying out capturing the touchstart event on the element itself. It will in most cases fire before the pseudo mouseover event it generates. I can use the touchstart event to set a timer window during which the undesirable parts of mouseover in a touch situation will not happen, example:


$nav.css({opacity: eb.navfade}).on('touchstart mouseenter', function(e){
$nav.stop(true, true).animate({opacity: eb.navfade});
if(e.type === 'touchstart'){ // prevent potentially counterintuitive opacity changes on touch enabled devices
clearTimeout(eb.cancelopchangetimer);
eb.cancelopchange = true;
eb.cancelopchangetimer = setTimeout(function(){eb.cancelopchange = false;}, 600);
}
if(!eb.cancelopchange){ // if this is not a touch enabled device firing on touchstart rather than true mouseover
$(this).stop(true, true).animate({opacity: 1});
}
})

This last has the advantage that, if the device has both mouse and touch, whichever is in use at the moment should determine the outcome.



*In javascript there's this big deal about if you mouseover something, it's mouseout event fires when you subsequently mouseover a child element, at least sometimes. And there are different ways to trap that and ignore it when you only want the one that fires when you actually leave the element and visa versa with mouseout events. These were normalized across browsers using jQuery mouseenter/leave. But none of that has taken into account the behavior of the mouseover/out events on touch devices. Apparently that is left to the coder for now. I'm sure some sort of normalization or cancellation routine will be available at some point. Either that or they (mouseover/out) will simply not be used due to attrition.

jscheuer1
03-04-2016, 10:14 PM
Here's a simplified version of the best solution I think so far:


<!DOCTYPE html>
<html>
<head>
<title>WTF? II - A Solution</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

</head>
<body>
<input id="wtf" type="button" value="WTF">
<script type="text/javascript">
var alertcancel, alertcanceltimer;
function amitouchingormousing(e){
if(e.type === 'touchstart'){
clearTimeout(alertcanceltimer);
alertcancel = true;
alertcanceltimer = setTimeout(function(){alertcancel = false;}, 600);
}
if(!alertcancel){
alert('wtf');
}
}
document.getElementById('wtf').addEventListener('touchstart', amitouchingormousing, false);
document.getElementById('wtf').addEventListener('mouseover', amitouchingormousing, false);
</script>
</body>
</html>

If a mouse moves over, we should get the alert. If a touchevent happens on the element (even though that fires a pseudo mouseover event), we should not. If a device has both a mouse and a touchscreen, the result should depend upon which input device is used.

molendijk
03-05-2016, 11:22 PM
So now I'm thinking I either need to detect touch and not assign any potentially confusing mouseover events for those devices, or I need to find a way to fire the mouseout sequence of events at the appropriate time even if the touch device doesn't fire them in the way a mouse device would.
Mobile devices are 'made' for touchscreen (tap) events, not for mouse events. So instead of trying to make mobile devices understand what an arbitrary mouse instruction is supposed to perform, I would prefer to distinguish between tap and mouse by mobile device / desktop detection (http://www.abeautifulsite.net/detecting-mobile-devices-with-javascript/).

jscheuer1
03-06-2016, 12:22 AM
Mobile devices are 'made' for touchscreen (tap) events, not for mouse events. So instead of trying to make mobile devices understand what an arbitrary mouse instruction is supposed to perform, I would prefer to distinguish between tap and mouse by mobile device / desktop detection (http://www.abeautifulsite.net/detecting-mobile-devices-with-javascript/).

If you read more carefully, I actually had already moved on from the earlier position of mine that you quote:


So now I'm thinking I either need to detect touch and not assign any potentially confusing mouseover events for those devices, or I need to find a way to fire the mouseout sequence of events at the appropriate time even if the touch device doesn't fire them in the way a mouse device would.

No big deal, I admit this thread is confusing. And I will have a look at your linked resource. That said, for now I've solved my original problem - which was by the way that touch devices generally fire a mouseover event when a user taps, but fail to fire the mouseout event when they stop tapping, only to fire it, if at all, when the user taps outside the element.

So in simpler terms, I wanted a way to either prevent this pseudo mouseover event from firing, or a way to know when it had and to clean up after it. I ended up with neither. I now let it happen, but detect if it was precipitated by a touchstart event, if so I set a timer that prevents what are now the undesired parts of the genuine mouseover effect until that timer expires (600 milliseconds) because it is possible the user tapping also has a mouse and might use it later on the same page prior to reloading. I believe the concept is called "input agnostic". Or maybe not. In any case I don't know if there is a mouse or not. I don't know if there is a touch device or not. And I don't need to. Response is determined by the event.type property of the first in whatever chain of events are triggered upon the element. Which I like, because, on the same device, it could be a touch event one minute, and a mouse or a keyboard event later. If the device is more limited, that's OK too. I don't need to know. See this post: http://www.dynamicdrive.com/forums/showthread.php?79791-Click-touch-activates-mouseover-jq-mouseenter-event&p=317007#post317007 for a simplified example of my current approach to this issue as regards a mouseover event that I want to fire if a real mouse enters the element, but not if somebody just taps on it.

Bottom line, in the grand old tradition of "sniffing is usually not the best approach", I just react to the event that happens.

molendijk
03-06-2016, 01:52 PM
What I ment was that we should restrain ourselves from invoking mouse events (except for 'click', maybe) in scripts that are supposed to (also) work on mobile devices. We should separate mobile devices from desktop as much as we can, because mobile devices may understand mouse instructions in an unpredictable way. For instance, the script in #6 does what it is supposed to do on a iPad (no alert), but behaves differently on Android (sometimes there's an alert, sometimes there's not).

I agree that sniffing is not the best practice. Here's a script that doesn't rely on sniffing:
<!DOCTYPE html>
<html>
<head>
<title>WTF? II - A Solution</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<input id="wtf" type="button" value="WTF" >
<script>
function amitouchingormousing()
{
if ('ontouchstart' in window)alert('touching')
else {if (!('ontouchstart' in window))alert('mousing')}
}
if ('ontouchstart' in window)document.getElementById('wtf').addEventListener('touchstart', amitouchingormousing, false);
if (!('ontouchstart' in window))document.getElementById('wtf').addEventListener('mouseover', amitouchingormousing, false);
</script>
</body>
</html>

jscheuer1
03-06-2016, 03:42 PM
Sorry Arie, that:



if ('ontouchstart' in window)

is sniffing regardless of where/when you do it. It makes no difference if you establish that value on dom load and then use it whenever it might apply, or check it each time you might use it, because it will never change.

I wish I had an Android simulator. However, for my application, I think an occasional slip up is OK. If it were something as annoying as an alert, it would not be. However, in this particular case, it's actually highlighting, which would appear slightly counter intuitive on some touch devices on some occasions. In my opinion, that's acceptable for still allowing it whenever a mouse is used. I could sniff android though and block for that, worth considering. Again, if I had one or an emulator to test on, I think I'd like to find another way. What I had read on Android said that it might not always work there, particularly in Firefox and Chrome, due to those browsers not always issuing a delay between touchstart and their pseudo mouse (in this case mouseover) event(s). What I read said this was if a meta tag:


<meta name="viewport" content="user-scalable=no">

or similar viewport meta tag when that and other content values was used. However, with such a simple test page, I imagine the browser might take it upon itself to consider it not scalable, if for some reason the programmers wrote that in. After all, who should need to scale such a simple page with only one standard button on it? I've seen programmers jump to far less tenable conclusions.

In any case, the article I read seem to feel such behavior was likely to be phased out. That's a place I sometimes now take more liberties than in the past. Like, unless someone specifically requests it, I would NOT go out of my way to support IE.

With, what I consider thorny, issues like these, I usually start out sniffing - then toy with just not using the feature, and finally (if I'm lucky) find a way to work with it without sniffing. With more basic features, things central to - say the activation of a menu, I long ago abandoned using mouseover/out. That's just asking for trouble. Speaking of menus, I did have one where even using click activation some one thing was acting weird on some touch devices. So I resorted to sniffing and a different pathway in those cases for that one thing. I actually forget the exact thing it was (it was many years ago). If I ever revisit that menu, knowing what I know now, I may attack the issue in a different manner.

I wonder if using the opposite value meta tag might 'fix' Android, or at least make it more cooperative? If you'd be so kind:


<!DOCTYPE html>
<html>
<head>
<title>WTF? III - A Solution</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="user-scalable=yes">
<!-- The above META tag MIGHT 'fix' the occasional hiccup with this code in Android -->
</head>
<body>
<input id="wtf" type="button" value="WTF">
<script type="text/javascript">
var alertcancel, alertcanceltimer;
function amitouchingormousing(e){
if(e.type === 'touchstart'){
clearTimeout(alertcanceltimer);
alertcancel = true;
alertcanceltimer = setTimeout(function(){alertcancel = false;}, 600);
}
if(!alertcancel){
alert('wtf');
}
}
document.getElementById('wtf').addEventListener('touchstart', amitouchingormousing, false);
document.getElementById('wtf').addEventListener('mouseover', amitouchingormousing, false);
</script>
</body>
</html>

Worth a shot. Thanks in advance.

molendijk
03-06-2016, 05:06 PM
The meta tag does it. No alert with Android. Nice!

jscheuer1
03-06-2016, 08:06 PM
Thanks again Arie, if your interested, I have a distribution archive for the script (extra buttons for ultimate fade in slideshow) that this thread was a little sandbox for one minor aspect of. What happened was, if there was a real mouse being used, each numbered button would highlight as it passed over them. This obviously couldn't work on a touch capable device, unless it also had a mouse and the mouse was being used, not the touchscreen. Since sniffing couldn't reliably establish that - well that's how I eventually was guided to what I came up with. This seemed desirable as style alone was already being employed in a different manner for these buttons. That might be another avenue though. I think I will have a look at that. In any case, since the touch pseudo mouseover fires on touch start and the touch pseudo mouseout doesn't fire unless or until the user taps outside the element, mouseover/out effects were clearly no longer intuitive looking, even though they had no real impact on functionality, it was - just not right looking.

Anyways, if you want to try out the full demo it's available from the top of my blog post (all necessary files included):

http://www.dynamicdrive.com/forums/entry.php?248-Extra-Buttons-for-Ultimate-Fade-in-slideshow

molendijk
03-06-2016, 09:47 PM
Thanks very much, John. I'll take a good look at it.
While viewing the source of extrabuttons_demo.htm, I suddenly remembered having tweaked this very similar DD-script (http://www.dynamicdrive.com/dynamicindex14/fadeinslideshow.htm) about 6 months ago so as to have extra buttons and a description at the top of the images. Here (http://mesdomaines.nu/jochem) it is. Isn't that a coincidence!
I made the show for a friend of mine (Jochem) who celebrated his 65th birthday in August 2015. It contains many images made some 40 years ago, when he and I and some mutual friends (boys and girls, all so unbeatably fresh and young at the time!) crossed the US in an old van. I'll never forget that trip.
The best picture of me is at 23/45 (in the middle: a young guy, who is a little bit drunk and handsome). That's 40 years ago. Now, 40 years later, I'm a little bit older (but that doesn't show, of course; it never shows when you like good music and good beer).
Thanks again.

jscheuer1
03-07-2016, 12:04 AM
That's the same script. I didn't tweak it though, well I did do some bug fixes, but I made up my real tweaks as a separate file - extrabuttons.js.

Looks nice!

jscheuer1
03-09-2016, 07:35 AM
OK! I finally got some web space again, here's a link to a demo:

http://jscheuer1.com/garden/

That's actually different and a little more advanced and better looking than the distro download I pointed you to before. But the same principles of dealing with pseudo events apply.

molendijk
03-09-2016, 08:12 PM
John, thanks again for the demo. As an exercise and also because I want to use the script, I did some tweaking on it.

To make it a bit more 'custom friendly' I removed all values for color, background and font-size in fadeslideshow_bug_fix_caption_resizing_and_imgfrommarkup_options.js. These values can be specified in the main script. Also, I modified the size for restore.png and x.png (hardly visible on mobile devices) and disabled the settings for mouseover.

The dimensions in var mygallery are now specified using (var) gallery_width and (var) gallery_height, which allows us to size the gallery more precisely than what we obtain when we just use percentages for the dimensions. For instance, if there's a header above the gallery and if we do dimensions: ['75%', '75%'], then depending on the header's clientHeight and / or the size of the screen, the buttons beneath the gallery may get out of position or even out of sight. But when we do dimensions: [gallery_width, gallery_height], where gallery_width and gallery_height are specified in terms of the innerHeight of the window and the clientHeight of the header, etc., we won't run into trouble. As gallery_width and gallery_height reference elements in the body, the part of the script starting with var mygallery=new fadeSlideShow({ etc. must now be put at the end of the body section.

Finally, I added a 'window-resize = window-reload' script, because when people change the position of a mobile device (from horizontal to vertical or vice versa), screen properties will change and the images of the gallery won't display correctly anymore. This is now solved by forcing the page to reload when the position of the device changes (reduce window-width to watch it happen).

DEMO here (http://mesdomaines.nu/slideshow_responsive8%28scheuer%29/demo.html).

EDIT:
Sadly, the swipe doesn't work on iPAD with the tweaked script, even when I reinstall your original fadeslideshow_bug_fix_caption_resizing_and_imgfrommarkup_options.js. I'm stumped. It does work with your code and with the tweaked code I gave you here (http://mesdomaines.nu/jochem/). I don't get it!

PROBLEM SOLVED. THERE WAS TYPE ERROR IN THE NAME OF THE SWIPE SCRIPT.

jscheuer1
03-09-2016, 09:14 PM
Sadly, the swipe doesn't work on iPAD

You're missing the touch swipe script (from the console):


http://mesdomaines.nu/slideshow_responsive8%28scheuer%29/jquery.touchSwipe.min.js Failed to load resource: the server responded with a status of 404 (Not Found)

That's probably all it is.

I generally leave in all the color stuff, those things are easily overridden with style. The main script has an on resize function. Seemed to work fine on iPhone emulation, even when switching orientation, did not encounter loss of view of controls either - well unless I zoomed in too much. But simply using a container element for the slideshow should solve that if it were to happen. I mean no way to prevent with zoom. You zoom enough on anything and you will lose things near the edges, top, and bottom. I just mean if it initially shows without - say, the controls, a container alone should solve that. I almost always try to maintain the main script (except for bug fixes and feature additions, the latter I always try to make optional) so that it's as compatible as possible with the script it's meant to replace. So it can be a drop in replacement. That said, highly customizing can be fun and rewarding.

molendijk
03-09-2016, 10:05 PM
Problem solved, John. There was a type error in the name of the touch swipe script.
As for the resize problem I had in my tweaked script (and which you didn't have), it's most certainly due to the fact that you specify the values for the dimensions in var mygallery with the help of straight percentages, whereas I use variables for the dimensions. (But I solved that too). I prefer the variables because they make the show look identical with all screen resolutions. On desktop, the buttons of your show disappear when the window is very narrow and not very tall (vertically).