admin管理员组

文章数量:1332881

I have a menu which looks like this:

|Home|Options|Settings|Tools|Preferences|Edit|

That's fine when a phone has lots of horizontal space, but when a device with a narrow viewport accesses the page, I want the menu to look like

|Home|Options|Settings|+MORE+|

Where clicking the "MORE" menu displays the other items in a vertical drop down.

I don't want to set manual breakpoints, because I have no idea how wide the individual menu items will be when displayed.

My menu is currently just a set of <li> in a <ul>

The CSS for horizontal layout is

#menu ul, #menu li { margin: 0; padding: 0; list-style: none; }
#menu ul { overflow: auto; }
#menu li { float: left; }
#menu  a { display: block; padding: 0.5em; text-decoration: none; border-right: 1px solid #fff; font-size: 110%; }

I'm reluctant to use something like jQuery - even when minimized, it's still a significant overhead for older mobile browsers. Media Queries are also problematic for some phones, so I would like to avoid relying on those.

Any thoughts on CSS and (simple) JavaScript to automatically hide elements depending on the browser's width?

I have a menu which looks like this:

|Home|Options|Settings|Tools|Preferences|Edit|

That's fine when a phone has lots of horizontal space, but when a device with a narrow viewport accesses the page, I want the menu to look like

|Home|Options|Settings|+MORE+|

Where clicking the "MORE" menu displays the other items in a vertical drop down.

I don't want to set manual breakpoints, because I have no idea how wide the individual menu items will be when displayed.

My menu is currently just a set of <li> in a <ul>

The CSS for horizontal layout is

#menu ul, #menu li { margin: 0; padding: 0; list-style: none; }
#menu ul { overflow: auto; }
#menu li { float: left; }
#menu  a { display: block; padding: 0.5em; text-decoration: none; border-right: 1px solid #fff; font-size: 110%; }

I'm reluctant to use something like jQuery - even when minimized, it's still a significant overhead for older mobile browsers. Media Queries are also problematic for some phones, so I would like to avoid relying on those.

Any thoughts on CSS and (simple) JavaScript to automatically hide elements depending on the browser's width?

Share Improve this question asked Sep 18, 2012 at 15:32 Terence EdenTerence Eden 14.3k3 gold badges53 silver badges100 bronze badges 3
  • 2 Media Queries actually have quite good support. You can check out some navigation patterns here: bradfrostweb./blog/web/… & bradfrostweb./blog/web/responsive-nav-patterns – carpenumidium Commented Sep 18, 2012 at 16:19
  • On new devices, yes. But the majority of the world uses old mobile devices - see slideshare/bryanrieger/rethinking-the-mobile-web-by-yiibu – Terence Eden Commented Sep 19, 2012 at 8:07
  • I mend you on your concerns for accessibility, but the majority of devices that are over 5 years old, from Nokia, Sony Ericsson, LG etc. can run Java applications like Opera Mini 4+ just fine. I've been using the same Nokia Supernova 7210 S40 phone for the past 3 years; the browser I use, Opera Mini 4.4 (it's actually an old version; the later versions can even run a little JS...live! Which is quite impressive imo) is more than capable of rendering plex web pages pretty well. I mainly use it in 'single-column' mode which is pretty self explanatory and in many cases an excellent feature. – carpenumidium Commented Sep 19, 2012 at 8:33
Add a ment  | 

1 Answer 1

Reset to default 7

Actually, you can do it with no JavaScript at all, just with media queries (which really have excellent support + this solution I am presenting is a mobile first one) and :nth-last-child (which again is even supported by Opera Mini).

demo

(resize to see how it works)

You'll need to have a structure like this:

<nav id='menu'>
    <ul>
        <li><a href='#'>Home</a></li>
        <li><a href='#'>Options</a></li>
        <li><a href='#'>Settings</a></li>
        <li><a href='#'>Tools</a></li>
        <li><a href='#'>Preferences</a></li>
        <li><a href='#'>Edit</a></li>
        <li><a href='#'>+ MORE +</a></li>
    </ul>
</nav>

Then you'll need to select the Tools, Preferences and Edit and set their display to none:

#menu li:nth-last-child(-n+4):not(:last-child) { display: none; }

li:nth-last-child(-n+4) selects only the first four list items from the end. You add the :not(:last-child) condition to that because you want the + MORE + list item to be shown.

In order to better understand structural pseudo-classes, you can play around with this tool.

Finally, you'll need to use a media query to change the display settings for larger screens:

@media (min-width: 30em) {
    #menu li:nth-last-child(-n+4):not(:last-child) { display: block; }
    #menu li:last-child { display: none; }
}

I am using an em based media query and not a px based one for two reasons:

  • one, this article;
  • two, my own site looks like crap on zoom because I used px based media queries on it a year ago;

EDIT: In order to make the menu expand on click and to make the number of menu elements shown vary with screen width, I have changed the structure a bit more:

<nav id='menu'>
    <a tabindex=1 class='ctrl' href='#'>+ MORE +</a>
    <ul>
        <li><a href='#' class='menu-link'>Home</a></li>
        <li><a href='#' class='menu-link'>Options</a></li>
        <li><a href='#' class='menu-link'>Settings</a></li>
        <li><a href='#' class='menu-link'>Tools</a></li>
        <li><a href='#' class='menu-link'>Preferences</a></li>
        <li><a href='#' class='menu-link'>Edit</a></li>
    </ul>
</nav>

And also changed the CSS a bit:

#menu .ctrl { float: right; }
#menu ul, #menu li { margin: 0; padding: 0; list-style: none; }
#menu ul { overflow: auto; }
#menu li { float: left; }
#menu li:nth-last-child(-n+5) { display: none; }
#menu a {
    padding: 0.5em;
    text-decoration: none;
    border-right: 1px solid #fff;
    font-size: 110%;
}
#menu li a { display: block; }
#menu li:first-child a { border-left: 1px solid #fff; }
#menu .ctrl:focus, #menu .ctrl:active { display: none; outline: 0; }
#menu .ctrl:focus ~ ul li, #menu .ctrl:active ~ ul li { display: block; }

@media (min-width: 15em) {
    #menu li:nth-child(2) { display: block; }
}
@media (min-width: 20em) {
    #menu li:nth-child(3) { display: block; }
}
@media (min-width: 25em) {
    #menu li:nth-child(4) { display: block; }
}
@media (min-width: 30em) {
    #menu .ctrl ~ ul li { display: block; }
    #menu .ctrl { display: none; }
}

demo

(I've also added a background with vertical lines at every 5em just to make it clear how wide the screen is when resizing the browser window)

This method should work without JavaScript - tested that in desktop browsers, Opera Mobile, Android browser and iOS Safari. I don't know about Opera Mini though - I'll have to test that.

EDIT#2: No, it doesn't work in Opera Mini for me (the menu is collapsed, but clicking the + MORE + link does not expand it). Tried to make it work with JavaScript (no library), but that also doesn't work in Opera Mini (though it works on desktop browsers).

EDIT#3: Also tried to do the same thing using jQuery. This time it also works in Opera Mini. Really slow (at least for me), but it works. This is what I used:

$('.ctrl').click(function() {
    $(this).css({'display': 'none'}).next().children().css({'display': 'block'});
});

EDIT#4: Now tried the :target method - demo (also CSS-only). Works fine on my laptop using Chrome (haven't tested in another desktop browser), doesn't work in Opera Mini (menu is collapsed, clicking the + MORE + link does not expand it). Works in Opera Mobile though.

本文标签: javascriptCollapse a horizontal menu for response designStack Overflow