Posting this question largely in the hopes of confirming my suspicions of the behaviour, and thus documenting it for other programmers. (Since I found no record of this anywhere online)
I have a site that I'm building, whose nav bar has the following properties:
The horizontal section is a <ul> of <li>s and some of the <li>s hav both:
A n<a> element taking you to that topic.
A hover CSS selector that triggers display:block on a submenu - a nested <ul> which then drops down vertically.
On a desktop this all behaves as I'd expect: hovering on the key element exposes the submenu, and clicking on it executes the click event (in this case a normal <a> link.
But on an iOS device (tested on Air, Mini, iPhone 6) I found that tapping once would expose the submenu, and tapping a second time (when the submenu is open) will actually invoke the link on the controlling element.
Long-pressing will bring up the "link context menu"
This is exactly what I wanted it to do, which is GREAT! But I don't know WHY it is doing it. Whilst the Menu is Bootstrap based, but I can't find any bootstrap that is doing it.
Currently my best guess is that iOS Safari has some magic code that adds this (obviously desirable) behvaiour by deciding that if you have an element with :hover CSS (or, I imagine, an onhover eventhandler bound) and also a click eventhandler bound then the first tap will invoke, and keep invoked, the hover event, and the second tap will invoke the click event.
Question:
Does anyone know, confidently, what the source of this behaviour is.
Can anyone find any documentation of this behaviour!?
Would people like to contribute other platforms on which this does/doesn't work (Android tablets? Windows tablets? older iOS?)
The behavior triggering a clickable element such as an anchor link to fire only on the second tap in iOS, is described in this post by Nicholas C. Zakas (#slicknet). What triggers the double tap is a:
:hover Rule that either hides or shows another element using
visibility or display.
e.g.
<style>
p span {
display: none;
}
p:hover span {
display: inline;
}
</style>
<p>Tap me<span>You tapped!</span></p>
Apple also provides a documentation on Handling Events for reference.
No other platforms do this. It's iOS specific since at least version 5 (likely since version 1). Because it's not cross-platform, for Android and other touch devices, it has to be handled differently, and requires canceling click events etc... using JS. While I have managed to do this. I think it's fair to say that for CSS navigation menu bars to work with toggle elements that are both links and toggles is very difficult to achieve in a touch-only environment.
Related
I implemented this css dropdown menu system a few years ago and I am just realizing now that it does not work on my iPhone, yet it works fine on my bosses android phone. I was under the impression that since IOS5, i-devices do register a touch event as a hover automatically but I guess I was wrong.
The structure of this menu is a little weird in that the main tabs (other than Home and Contact) are not meant to take the user anywhere, only the submenu items actually go to another page. I have a feeling this is where the issue may lie, but I can't figure it out.
Here is a JS Fiddle.
The :hover is on <li> to cause the dropdown. Maybe this is the issue? or maybe its the way my boss coded the button so it would look like a link but not do anything on a click:
<li id="aboutus" class="blogbutton"><a><span>About Us</span></a>
I tried removing the anchor tags and the whole button goes away. I removed the span tags and the original problem still remains.
Is there a fix without having to rewrite the whole menu system?
Try adding an onclick="return True;" attribute to the base menu that triggers the drop-down. Safari will trigger the hover attributes, but only if it thinks that the element does something when it's tapped, and for a static element like a li, this is the easiest way to achieve that.
I have unfortunately stumbled on the issue where, on iPad, a pop-up menu summoned by way of :hover does not disappear from the screen when the user touches an empty area of the page.
The problem is the same described here:
Hover Behavior on Desktop vs iPad
The menu is part of a template I bought, namely:
http://html.realia.byaviators.com/
But... wait a minute... it WORKS on the template's home page? And only on that page -- it doesn't work on any other page of that same template.
I was able to track the behavior down to the point where I found that the reason why it works is the following: initializing a Google map makes the menu behave properly. Just the simplest of maps, with the default options.
Now my question to the experts is: what is that Google does in the map initialization code in order to fix the :hover behavior?
Thank you very much in advance for your help!
Well, can't tell what is that Google does, however the solution is documented in mobile Safari developer's reference.
For a click event to be generated on an area of the document, there must be a click handler attached. For example, clicking on a div will generate a click event only if an onclick="void(0)" handle is presente:
Clicking here triggers event in mobile Safari
There seem to be many questions/answers on how to change Twitter Bootstrap's dropdowns from appearing on a click to appearing on a hover. The builders of Bootstrap have a good reason for using click instead of hover -- hover doesn't work on most tablets & phones. However, one of the reasons why I am using Bootstrap is for its responsive features. I want one site that can be viewed on desktops, tablets and phones. Although clicking to get the dropdown is necessary for tablets and phones, it is not the expected behavior for desktops. I'd like my site to be responsive, but not at the expense of having to retrain the users!
Is there a way to serve up hover dropdowns for desktops and click dropdowns for tablets and phones?
I created a plugin (working on a couple refinements, but it definitely works as is) that allows dropdowns to work on hover, but it doesn't interfere with Twitter Bootstrap's click event, so you can safely bind both, which essentially means if there is a mouse, it will activate on hover, but if there's not a mouse (i.e. a touchscreen on a tablet), it will still activate when clicked.
I have the plugin hosted on GitHub: https://github.com/CWSpear/twitter-bootstrap-hover-dropdown
It works by explicitly calling it, but I'm going to tweak the code to work with data-attributes in the near future.
You can with "Responsive utility classes"
http://twitter.github.com/bootstrap/scaffolding.html at the bottom of the page
I was searching exactly for this. And I've found better solution ( I think ).
We can easily do this using awesome Modernizr.js library.
We can detect the touch screen device by the below function.
function is_touch_device() {
return !!('ontouchstart' in window);
}
And then we can disable the URL in the hyperlinks dynamically via Javascript either by changing the location to "#" or by preventing the default action of the parent menu items.
Final code would be as below.
$(document).ready(function() {
/* If mobile browser, prevent click on parent nav item from redirecting to URL */
if(is_touch_device()) {
$('#mainmenu li > ul').each(function (index, ev) {
/* Option 1: Use this to modify the href on the <a> to # */
$(ev).prev('a').attr('href' ,'#');
/* OR Option 2: Use this to keep the href on the <a> intact but prevent the default action */
$(ev).prev('a').click(function(event) {
event.preventDefault();
});
});
}
});
For more details, you can see this link
I do believe someone will vote me UP :-)
Thanks
I'm using CSS for a dropdown menu on a site I'm building. When you hover over a parent tab, the dropdown menu fades in using CSS3's transition-property. The problem I'm having is the parent tab links off to another page, so when you tap a parent tab on the iPad, it takes you to the page instead of displaying the dropdown menu - which causes usability issues.
Is there a way to make it so the dropdown appears on the first tap, and the second tap takes you to the parent page?
Here's the HTML I'm using to display the menu:
<nav>
<ul>
<li>Home</li>
<li>About</li>
<li>Team
<ul>
<li>Our Workers</li>
<li>Join Us</li>
</ul>
</li>
<li>Contact</li>
</ul>
</nav>
And here's a jsFiddle link: http://jsfiddle.net/A64QU/197/
Thanks in advance, I appreciate any help.
You're on the right track with a design that invokes the menu on the first tap and the parent page on the second tap. This will work on touchscreen-only or touchscreen-sometimes devices where the user cannot always 'hover', and is critical for users who have difficulty holding the cursor steady over the menu, so I recommend it over a hover-to-show-menu design regardless of whether this is Mobile Safari, Internet Explorer, or any other browser.
To do what you ask, handle the click event on the <a> tags (for example use jQuery: http://api.jquery.com/click/) and hide/show the menu that way (you could use jQuery's toggle, or show/hide.) Then extend the code to consider whether the menu is shown or hidden to determine whether it should prevent <a> tag's default behavior and show the menu (for example jQuery's preventDefault: http://api.jquery.com/event.preventDefault/) or allow the default behavior of click on <a> to occur: navigate to the <a href> URL.
Consider that with this approach you may also need to provide a way for the user to dismiss the menu once it's open (and blocking some part of the page.) Often this is done with a click handler at the document level.
I do not recommend trying to implement a touch-hold instead, as this is not well-known to users. In my experience with user testing most will not try this, even on things that look as if they are a menu.
There is no "hover" in the touch UI metaphor, although there is a counterpart event which has been called touchhold in the jQuery Mobile UI; it is fired when the finger remains pressed down on the touch-screen for a certain amount of time, e.g. 500ms, 700ms, whatever. Something similar happens on the virtual keypad of the iPad when you hold a finger down on certain keys, the [a] key, for example: you get a popup of variant forms of [a] (umlauted, accented, and so forth).
You could wire things up so the menu-open code would be called on touchhold rather than on the tap event, and then have the individual menu-items listen for the tap event. You would either have to override the touchevents of Mobile Safari yourself by writing the required javascript, or install a UI library that implements this behavior.
Context: Web app on MobileSafari/iPad, both full-screen and embedded in iframe
Goal: Technique that provides custom event handlers for Press and Tap but not Pinch (zoom) or Drag (scroll/pan). We want to provide custom behavior for Press, but to let Safari handle Scroll/Pan/Zoom still.
Problem: The two goals seem to be mutually exclusive. To prevent the default behavior for a Press gesture, event.preventDefault must be called in immediate response to ontouchstart. However, to let the default behavior proceed on a Drag, we must not call event.preventDefault on the initial ontouchstart. Once preventDefault is called, there is no way of getting the default behavior back during that gesture sequence (i.e. until all fingers come off). Drag is not recognized until some movement has occurred.
Drag is illustrated as the simplest example, but we care about getting the default behavior for Pinch and Double-tap as well. We never want the default MobileSafari Press behavior of "Copy Image" or selecting text.
Attempts thus far:
Save the ontouchstart event and, after a timeout, call preventDefault on it later (just before Press would be recognized). No effect.
Listen for 'oncontextmenu'. Nope, the default Press behavior is not being signaled or routed through this event.
Prevent the default in all of our ontouch* handlers, and then when we recognize a Drag/Pinch gesture, simulate new events using initTouchEvent and/or initGestureEvent. The programmatically created events hit all of our callbacks, but Safari seems to pay no attention to them, triggering no default behavior.
Break up Safari's Press recognition with a fake move. Safari cancels a press on the slightest move, but we allow for a bit of slop. Simulating a move event (as above) just after the ontouchstart does not cause Safari to fail to recognize Press if the finger is not moved "for real".
We have a fully featured set of gesture recognizers implemented (in Javascript, ontouch*) in the style of Apple's native iOS gesture recognizers. We have no problem recognizing any gestures, we just don't know of a good way to replicate the Zoom/Pan/Double-tap behavior that Safari provides for free.
We don't need code; we're looking for any theoretical solution (beyond "just implement pan/zoom yourself too", though if you have a slick way of doing that we're interested) that leads to reasonable success. We're also interested in learning about other similar attempts to do this - surely we can't have been the first to try this?
Alternate TL;DR: Is there any way to prevent a default Press (also known as tap-and-hold) other than in touchstart?
Success: Prevent "default press" behavior entirely through CSS instead of preventDefault. This answer was provided by Safari Technologies Evangelist Vicki Murley over here (registration required)
I see in your stackoverflow post that
you say your ultimate goal is that:
"We never want the default
MobileSafari Press behavior of "Copy
Image" or selecting text."
These behaviors can be disabled in CSS
(+ one bonus property, since people
often want to turn off the highlight
if they're turning off these other
behaviors):
/* behavior */
-webkit-user-select: none; /* disable cut copy paste */
-webkit-touch-callout: none; /* disable callout, image save panel */
-webkit-tap-highlight-color: transparent; /* "turn off" link highlight */
Commentary: We assumed that Safari's Press behavior was event-based, not driven by CSS. Calling preventDefault ontouchstart does cancel the press behavior (which is what led us astray, really), but that seems to be an unintended side-effect. Really, Mobile Safari does not execute a "press event" so much as "CSS on press".
Disabling the Press behavior with CSS has allowed us to once again call preventDefault only where and when we actually need it.
Ugly Possibility: Don't prevent a press - make a default press do nothing. Try to use a glass-pane div that catches all touches and doesn't prevent anything on touchstart.
Divs do have default Press behavior (some kind of selection), but perhaps that can be turned off, not via preventDevault but via <body style="-webkit-user-select:none">? ref
This would mean we'd have to do our own hit-testing to determine what DOM nodes to pass our recognized events to, since we can't just let events bubble up the DOM ancestor chain.