2

I'm working on a website with a simple and pure css dropdown menu. That website is supposed to be used on desktop and on Ipads. My dropdown menu uses :hover pseudo-class and issue appears on the touch screen: the menu expands well but it never collapse. The only way to close it is to open another submenu from the same dropdown menu. My goal is that my menu collapse when I touch anywhere in the DOM (except in the menu of course). After many researches, it appears that we can not do this with , is obligatory. I am a beginner and I have no skill in JS, is it possible to do it with only few vanilla lines ? I don't want to use . Here is my code:

/* ========================================================================= */
/* Global styles                                                             */
/* ========================================================================= */
html {
    box-sizing: border-box;
    font-size: 62.5%;
}

*, *:before, *:after {
    margin: 0;
    padding: 0;
    box-sizing: inherit;
}

body, input {
    font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}

img {
    border: none;
}


/* ========================================================================= */
/* Layout styles                                                             */
/* ========================================================================= */
body > header, body > main {
    margin: auto;
}

body > header {
    padding-top : 20px;
    width: 768px;
}

body > header > img {
    width: 150px;
    margin-left: 10px;
}


/* ========================================================================= */
/* Nav styles                                                                */
/* ========================================================================= */
body > header > nav {
    min-width: 768px;
    margin: 0 auto;
    padding-top: 20px;
    font-size: 1.5em;
    text-align: center;
}

nav > ul ul {
    position: absolute;
    display: none;
    text-align: left;
}

nav li {
    width: 150px;
}

nav > ul > li {
    display: inline-block;
}

nav a {
    display: block;
    text-decoration: none;
}

nav > ul > li > a {
    padding: 10px 0;
    color: #44546A;
}

nav > ul ul li {
    background-color: #333F50;
    list-style-type: none;
}

nav > ul ul li a {
    padding: 10px 0 10px 30px;
    color: #FAFAFA;
    font-size: 0.9em;
}

nav > ul li:hover ul {
    display: block;
}

nav > ul ul li:hover {
    background-color: #51647f;
}
<!DOCTYPE html>
<html>
    <head>
        <base href="/"/>
        <meta charset="UTF-8"/>
        <title>Test Title</title>
        <meta name="viewport" content="width=device-width, initial-scale=1"/>
        <link rel="stylesheet" type="text/css" href="css/normalize.css"/>
        <link rel="stylesheet" type="text/css" href="css/styles.css"/>
    </head>
    <body>
        <header>
            <img src="img/test.svg" alt="test"/>
            <nav>
                <ul>
                    <li>
                        <a href="#">Menu 1</a>
                        <ul class="subMenu">
                            <li>
                                <a href="#">SubMenu 1.1</a>
                            </li>
                        </ul>
                    </li>
                    <li>
                        <a href="#">Menu 2</a>
                        <ul>
                            <li>
                                <a href="#">SubMenu 2.1</a>
                            </li>
                            <li>
                                <a href="#">SubMenu 2.2</a>
                            </li>
                            <li>
                                <a href="#">SubMenu 2.3</a>
                            </li>
                        </ul>
                    </li>
                    <li>
                        <a href="#">Menu 3</a>
                        <ul>
                            <li>
                                <a href="#">SubMenu 3.1</a>
                            </li>
                        </ul>
                    </li>
                    <li>
                        <a href="#">Menu 4</a>
                        <ul class="subMenu">
                            <li>
                                <a href="#">SubMenu 4.1</a>
                            </li>
                        </ul>
                    </li>
                </ul>
            </nav>
        </header>
    </body>
</html>

Edit : That code works well on tablets but not on Ipads

Ch1ken
  • 81
  • 8
  • Isn't what you describe default behaviour? I use a hover menu as well. When I touch a link, it becomes active/focussed and hence triggers the `:hover` css. Clicking anywhere outside the menu makes that link lose focus and hence, collapse normally. The problem was that on mobile my menu took up the entire screen, so there was nowhere else to click that wasn't inside the menu. So we just changed the styling so it didn't take up the entire screen. If I c/p your code into a page and open it with my phone, i can touch under the menu to make it disappear. I guess I'm misunderstanding the problem? – Shilly Mar 02 '17 at 12:36
  • @Shilly I tested it on an other tablet (Nexus) 1 min ago and it works well. It appears that my issue is about Ipads only. – Ch1ken Mar 02 '17 at 12:48
  • 2
    Possible duplicate of [Fix CSS hover on iPhone/iPad/iPod](http://stackoverflow.com/questions/18047353/fix-css-hover-on-iphone-ipad-ipod) – Alec von Barnekow Mar 02 '17 at 12:51
  • Oh, I didn't know that yet, thanks! – Shilly Mar 02 '17 at 12:55

3 Answers3

2

Here a solution, ask if you want explanation.

<nav id='nav'>
...
<script>
function hideDropDownMenu(e) {
  var element = e.target;
  var parent = element.parentNode;
  var mustHide = false;
  while (parent != null && !mustHide) {
    mustHide = element.id === 'nav';
    element = element.parentNode;
  }
  var subMenus = document.getElementsByClassName('subMenu');
  var i = 0;
  for (i = 0; i < subMenus.length; i++) {
    var subMenu = subMenus[i];
    subMenu.style = mustHide ? 'none !important' : 'block'; // not sure if the !import is optionnal
  }
}

document.body.addEventListener('click', hideDropDownMenu);
</script>
Alexandre Nicolas
  • 1,851
  • 17
  • 19
2

The :hover pseudo-class behaves differently on touch screen devices. When the user touches an element, the browser triggers and keeps the state :hover until another pseudo-class is triggered. Thus, when the user touches another element on the page, a different pseudo-class is triggered by the browser and the drop-down menu becomes hidden. Most of the time, it is the :active pseudo-class that is triggered.

However, as explained on the Safari Developer Library, Mobile Safari doesn't trigger the :active pseudo-class unless a touch event is attached to the element:

On iOS, mouse events are sent so quickly that the down or active state is never received. Therefore, the :active pseudo state is triggered only when there is a touch event set on the HTML element—for example, when ontouchstart is set on the element...

To fix this, you can add a touchstart listener to your document in order to trigger the :active pseudo-class:

document.addEventListener('touchstart', function() {});
Igor Gassmann
  • 182
  • 1
  • 1
  • 8
1

As I posted in another question:

I solved this problem by adding a tabindex to the <body> tag, like this:

<body tabindex="0">

This little trick allowed iPad Safari to focus on the body when it's tapped on, and remove the focus from the dropdown menu.

No Javascript required.

Simon East
  • 55,742
  • 17
  • 139
  • 133