Responsive menu's sub-menus not forcing scrolling when flowing offscreen - javascript

Edit: Problem solved by a user on another site. I have summarised this as a separate answer.
=====
Edit: I have tried various overflow settings for the nav but still nothing. This must be something simple, but I can't find it. Anyone?
=====
I have a responsive menu that is sticky when reaching the top of the page in larger windows, but fixed to the top permanently when displaying the hamburger. The menu is using Todd Motto's flaunt.js menu: https://toddmotto.com/labs/flaunt-js/
In the original demo (above) the menu is not fixed. Changing the CSS to make the menu fixed has the unfortunate effect of preventing menu scrolling.
I would like to prevent the body scrolling behind, which I can do successfully with jQuery toggleClass, but need to make the menu scrollable as it will often be extending off-screen in hamburger mode on smaller devices.
I have tried adding overflow-y: scroll to various parts of the menu structure; this creates the scroll track, but still there is no bar inside and no scrolling even when the menu extends off-screen.
The problem occurs when changing the nav's position in the CSS class .nav from position: relative in the default style, to position: fixed with the media query (max-width: 768px).
Is there a way of enabling the menu to scroll, ideally with the main browser's scrollbar (with the body not scrolling behind)?
My JS for preventing body scrolling and potentially enabling menu scrolling, while adding an overlay to the body, is:
$(document).ready(function(){
$( ".nav-mobile" ).click(function() {
$( "#overlay" ).toggle();
$( "#mybody").toggleClass("scroll no-scroll");
$( "#SomeIDinTheNavSomewhere").toggleClass("scroll no-scroll");
});
});
with CSS:
#overlay {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 100%;
height: 100%;
background: rgba(40,40,40, .75);
}
.no-scroll {
overflow-y: scroll;
position: fixed;
}
.scroll {
overflow-y: scroll;
}
I have chosen to keep the scroll-track to avoid the burger jumping side to side when toggling as a result of enabling / disabling the body scrolling, rather than using overflow: hidden, but both create the same unwanted behaviour regardless anyway.
A cropped down and minimal version of the flaunt.js menu's html, css and js is below.
jsfiddle position: fixed here: https://jsfiddle.net/m_d_a/6gtjpvck/1/
jsiddle position: relative here: https://jsfiddle.net/m_d_a/6gtjpvck/ don't worry about the layout, just to prove scrolling exists when .nav is not fixed.
HTML
<nav class="nav">
<ul class="nav-list">
<li class="nav-item">
Home
<ul class="nav-submenu">
<li class="nav-submenu-item">
Submenu item 1
</li>
<li class="nav-submenu-item">
Submenu item 2
</li>
<li class="nav-submenu-item">
Submenu item 3
</li>
<li class="nav-submenu-item">
Submenu item 4
</li>
</ul>
</li>
<li class="nav-item">
About
</li>
<li class="nav-item">
Services
<ul class="nav-submenu">
<li class="nav-submenu-item">
Submenu item 1
</li>
<li class="nav-submenu-item">
Submenu item 2
</li>
<li class="nav-submenu-item">
Submenu item 3
</li>
<li class="nav-submenu-item">
Submenu item 4
</li>
</ul>
</li>
<li class="nav-item">
Portfolio
</li>
<li class="nav-item">
Testimonials
</li>
<li class="nav-item">
Contact
</li>
</ul>
</nav>
CSS
a, a:hover {
text-decoration:none;
}
ul, ol {
list-style:none;
}
.nav {
position:relative;
display:inline-block;
font-size:14px;
font-weight:900;
}
.nav-list {
text-align:left;
}
.nav-item {
float:left;
*display:inline;
zoom:1;
position:relative;
}
.nav-item a {
display:block;
color:#FFF;
padding:15px 20px;
background:#7A1E61;
border-bottom:1px solid #8B2870;
}
.nav-item > a:hover {
background:#822368;
}
.nav-item:hover .nav-submenu {
display:block;
}
.nav-submenu {
display:none;
position:absolute;
left:0;
width:180px;
}
.nav-submenu-item a {
background:#8B2770;
border-bottom:1px solid #9A337E;
display:block;
padding:15px;
}
.nav-submenu-item a:hover {
background:#932C77;
}
.nav-mobile {
display:none; /* Hide from browsers that don't support media queries */
cursor:pointer;
position:fixed;
top:0;
right:0;
background:#3D3D3B;
background-size:18px;
height:50px;
width:50px;
}
.nav-click {
position:absolute;
top:0;
right:0;
display:none;
border-left:1px solid #8B2870;
height:49px;
width:50px;
cursor:pointer;
}
.nav-click i {
display:block;
height:48px;
width:48px;
background:url(https://toddmotto.com/labs/flaunt-js/img/drop.svg) no-repeat center center;
background-size:20px;
}
.nav-click:hover {
background-color:#6E1656;
}
.nav-rotate {
-webkit-transform:rotate(180deg);
-moz-transform:rotate(180deg);
-ms-transform:rotate(180deg);
-o-transform:rotate(180deg);
transform:rotate(180deg);
}
/*-----------*\
Media Queries
\*-----------*/
#media only screen and (min-width: 320px) and (max-width: 768px) {
.nav-mobile {
display:block;
position: fixed;
}
.nav {
width:100%;
padding:50px 0 0;
}
.nav-list {
display:none;
}
.nav-item {
width:100%;
float:none;
}
.nav-item > a {
padding:15px;
}
.nav-click {
display:block;
}
.nav-mobile-open {
border-radius:5px 5px 0 0;
-webkit-border-radius:5px 5px 0 0;
-moz-border-radius:5px 5px 0 0;
}
.nav-item:hover .nav-submenu {
display:none;
}
.nav-submenu {
position:static;
width:100%;
}
}
JS
;(function($) {
// DOM ready
$(function() {
// Append the mobile icon nav
$('.nav').append($('<div class="nav-mobile"></div>'));
// Add a <span> to every .nav-item that has a <ul> inside
$('.nav-item').has('ul').prepend('<span class="nav-click"><i class="nav-arrow"></i></span>');
// Click to reveal the nav
$('.nav-mobile').click(function(){
$('.nav-list').toggle();
});
// Dynamic binding to on 'click'
$('.nav-list').on('click', '.nav-click', function(){
// Toggle the nested nav
$(this).siblings('.nav-submenu').toggle();
// Toggle the arrow using CSS3 transforms
$(this).children('.nav-arrow').toggleClass('nav-rotate');
});
});
})(jQuery);
Thank you. I realise there may be a bit to wade through here.

Thanks to a user on another site the problem I was having was because I hadn't specified a height for the nav.
For scrollbars to work with overflow, a height must be set.
max-height: calc(100% - 50px);
overflow-y: auto;
This gets the scrollbars working. However, as I would like the main window to have the scrollbars, I will remove the actual nav from being in position: fixed; to being embedded in a full screen overlay instead. This aside, the CSS above solves the problem and answers the question asked.

Related

How do I make my responsive hamburger menu show up and hide with JS

I'm just trying to teach myself some (vanilla) Javascript at the moment, so I'm assuming this is probably a pretty basic question.
At the moment, I've already displayed and hidden the hamburger menu with CSS media queries, so when the page is loaded up for the first time, it works exactly as it should.
The problem:
After the JS script is run (on click) to open the hamburger menu and I widen my window again, the hamburger menu stays on the screen along with the nav that's supposed to show up past the breakpoint.
What I have tried is to use an if statement to basically do some sort of Javascript media query magic.
if (window.matchMedia('screen and (max-width: 48.62rem)').matches) {document.getElementById("mobile-nav").style.display = "block";
} else{
document.getElementById("mobile-nav").style.display = "none";
};
What ends up happening is the same thing as before where the hamburger menu stays on screen if I go from a smaller window to a wider one BUT clicking it again makes it vanish. Good stuff, but now when I make my window smaller again, the menu has completely vanished.
I'm fairly new to JS, so what I understand is that the script is run, it overrides the css media queries and keeps the page like that. I'm thinking a loop might be my solution (maybe a while loop?), but I'm not really sure how to go about doing this.
Here's all the HTML, CSS and Javascript:
function openNav(){
document.getElementById("mobile-nav").style.display = "none";
document.getElementById("menu-items").style.width = "100%";
};
function closeNav(){
document.getElementById("menu-items").style.width = "0";
if (window.matchMedia('screen and (max-width: 48.62rem)').matches) {document.getElementById("mobile-nav").style.display = "block";
} else{
document.getElementById("mobile-nav").style.display = "none";
};
};
#nav {
display: none;
}
#mobile-nav{
float:right;
}
.open-nav {
display:block;
cursor: pointer;
margin: 0.5rem 4rem 0 0;
font-size: 35px;
line-height: 70px;
}
.menu-items{
text-align: center;
width: 0%;
overflow-x: hidden;
height: 100vh;
z-index: 50;
position:fixed;
background: rgba(24,24,24,0.9);
transition:0.5s;
display:block;
}
.menu-items a{
clear:right;
display:block;
font-size: 1.25;
padding:1em 0;
transition:0.3s
}
.close-nav{
float:right;
margin:0.5rem 1em 0 0;
font-size: 50px;
color:rgb(206, 206, 206);
}
/* Media Queries*/
#media (min-width: 48.6rem) {
/* Nav */
#nav-bar{
display:flex;
}
#nav {
float:right;
margin:0 5rem 0 0;
display: flex;
}
#nav li {
margin-right: 1em;
}
#mobile-nav {
display: none;
}
<div id="menu-items" class="menu-items">
×
...
...
...
</div>
<nav id="nav-bar">
<!--LOGO-->
<div id="logo"><img src="./img/logo.png" alt="" /></div>
<!--Mobile Nav-->
<div id="mobile-nav">
<span class="open-nav" onclick="openNav()">☰</span>
</div>
<!--Main Nav-->
<ul id="nav">
<li>
...
</li>
<li>
...
</li>
<li>
...
</li>
</ul>
</nav>
Toggling CSS classes is way more cleaner and easier than using JavaScript style attribute. You should use it that way :
var btn = document.querySelector("#responsive-menu");
var nav = document.querySelector("nav");
btn.onclick = function() {
nav.classList.toggle("expand");
}
nav {
display: none;
background-color: #ed0;
}
nav.expand {
display: block;
}
<button id="responsive-menu">Click me</button>
<nav>
Menu
</nav>
And to specify different CSS for some media size, use CSS media queries.

How to make a CSS onhover menu vanish when clicking in mobile

So I'm using a hoverdown menu at the top of my good free photos site.
On desktop, it works just fine when you hover down on it, the menu appears.
When you hover off of it, it goes away.
However, mobile does not have the hover behaviour so when on mobile you click on the menu to make it appear, you can't get rid of it.
I need it to go away if you click on the menu (but not the sublinks) or anywhere else on the page.
Here is the HTML code I am using:
<div class="menu-wrap">
<nav class="menu">
<ul class="clearfix">
<li>
Good Free Photos Menu<span class="arrow">▼</span>
<ul class="sub-menu">
<li>Free Stock Photos</li>
<li>Public Domain Images</li>
<li>Featured</li>
<li>Last 100 images</li>
<li>News</li>
<li>Tutorials</li>
</ul>
</li>
</ul>
</nav>
</div>
and the css:
.clearfix:after {
display:block;
clear:both;
}
/*----- Menu Outline -----*/
.menu-wrap {
width:100%;
box-shadow:0px 1px 3px rgba(0,0,0,0.2);
background:#3e3436;
}
.menu {
width:1000px;
margin:0px auto;
}
.menu li {
margin:0px;
list-style:none;
font-family:'Ek Mukta';
}
.menu a {
transition:all linear 0.15s;
color:#919191;
}
.menu li:hover > a, .menu .current-item > a {
text-decoration:none;
color:#be5b70;
}
.menu .arrow {
font-size:11px;
line-height:0%;
}
/*----- Top Level -----*/
.menu > ul > li {
float:left;
display:inline-block;
position:relative;
font-size:19px;
}
.menu > ul > li > a {
padding:10px 40px;
display:inline-block;
text-shadow:0px 1px 0px rgba(0,0,0,0.4);
}
.menu > ul > li:hover > a, .menu > ul > .current-item > a {
background:#2e2728;
}
/*----- Bottom Level -----*/
.menu li:hover .sub-menu {
z-index:1;
opacity:1;
}
.sub-menu {
width:160%;
padding:5px 0px;
position:absolute;
top:100%;
left:0px;
z-index:-1;
opacity:0;
transition:opacity linear 0.15s;
box-shadow:0px 2px 3px rgba(0,0,0,0.2);
background:#2e2728;
}
.sub-menu li {
display:block;
font-size:16px;
}
.sub-menu li a {
padding:10px 30px;
display:block;
}
.sub-menu li a:hover, .sub-menu .current-item a {
background:#3e3436;
}
I know this may require some JavaScript, but I don't know what code.
You should read the following post :
CSS :hover behaviour on touchscreen devices
The problem with your current design is that it relies on the :hover state to work, which would not always work (for keyboard navigation, and causes problem with touchscreens).
A lot of accessible implementations for dropdown menus are well documented
Your current code may work within your current configuration, all you have to do is set the focus outside of your menu, for instance:
close menu
<div class="menu-wrap">
<nav class="menu">
But this won't make accessible your menu and won't make it work in the browsers where the :hover state is not recognized as :focus
Here's what I would do:
I would start by transferring the hover state from the css hover to a class name like .dropdown-open.
Using jquery:
if the website is viewed on a computer, control this class on mouseover and mousout.
else, toggle the class on mousedown of the element.
The jQuery would look something like this:
$(document).ready(function(){
function checkWidthForMenu(){
if($(window).width() > 760) {
$('.sub-menu').mouseover(function(){
$(this).addClass('dropdown-open');
});
$('.sub-menu').mouseout(function(){
$(this).removeClass('dropdown-open');
});
} else {
$(document).mousedown(function(){
$('.sub-menu.dropdown-open').removeClass('dropdown-open');
});
$('.sub-menu').mousedown(function(){
$(this).toggleClass('dropdown-open');
});
}
}
checkWidthForMenu();
$(window).resize(checkWidthForMenu());
});
Edit:
Adam has a good point for accessibility; so in your css, add the styles for the opened menu to .sub-menu.dropdown-open, .sub-menu:focus {...} This will make it open on hover and on focus (using the tab key as navigation)

Dynamically Removing Element from menu according to screen size

The other day I saw on a website it had several tabs A|B|C|D|E|MORE now as soon as you zoom in desktop browser it changes to A|B|C|MORE and then further zooming changes it to A|MORE and vice versa on zooming out.
I'm populating my menu using AngularJS so I was wondering how to write a directive for that?
I would do this using CSS and Mediaqueries for that. Actually this can be done in CSS only.
Mediaqueries
Mediaqueries are rules that "activate" CSS selectors and theire style definitions upon device/screen attributes. Among others: mediatype, screen width/height, device width/height, resolution (pixel per inch), device pixel ratio, orientation, ...
Details:
http://www.w3.org/TR/css3-mediaqueries/
https://en.wikipedia.org/wiki/Media_Queries
My solution
My solution uses flex-box to extend the column of primarily visible buttons to the full available width where as the "More..." button is floating right and being fixed width.
When the decreasing width "triggers" a breakpoint, the mediaquery will match, hiding the primary buttons and showing the buttons within the submenu.
The submenu opens on :hover, no Javascript required.
JsFiddle Playground
(Should work in all modern Browsers, including IE 11, tested only in Chrome ~33, FF ~27)
Details
Flex
HTML
<div id="menu">
<ul class="primary">
...
</ul>
<div class="more">
...
</div>
</div>
CSS
#menu {
display: flex;
...
}
#menu .primary {
margin: 0 50px 0 0;
flex: 1;
}
Mediaqueries
/* hide the buttons that are visibile in the primary menu */
#menu .more li:nth-child(1),
#menu .more li:nth-child(2),
#menu .more li:nth-child(3) {
display: none;
}
/* 1st breakpoint: hide primary button, show the correstponding one in the submenu */
#media screen and (max-width: 350px) {
#menu .primary li:nth-child(3) {
display: none;
}
#menu .more li:nth-child(3) {
display: block;
}
}
/* 2nd breakpoint: ... and so on ... */
#media screen and (max-width: 300px) {
#menu .primary li:nth-child(2) { /* the same procedure ... */ }
#menu .more li:nth-child(2),
#menu .more li:nth-child(3) { /* the same procedure ... */ }
}
Full code
HTML
<div id="menu">
<ul class="primary">
<li>AAA</li>
<li>BBB</li>
<li>CCC</li>
</ul>
<div class="more">
More...
<ul>
<li>AAA</li>
<li>BBB</li>
<li>CCC</li>
<li>DDD</li>
<li>EEE</li>
</ul>
</div>
</div>
CSS
/* Make the children elements flex */
#menu {
display: flex;
height: 30px;
}
#menu ul li {
list-style: none;
}
/* The button wraper will flex to full remaining width */
#menu .primary {
margin: 0 50px 0 0;
padding: 0;
display: inline-block;
flex: 1;
}
#menu .primary li {
padding: 5px 10px;
min-width: 50px;
text-align: center;
display: inline-block;
}
/* Place the "more..." button right */
#menu .more {
padding: 5px 10px;
width: 50px;
display: inline-block;
float: right;
}
/* Hide hoverable submenu by default */
#menu .more ul {
display: none;
padding: 0;
margin: 0;
}
/* Show on hover */
#menu .more:hover ul {
display: block;
}
/* Hidden by default, reactivated by the following Mediaqueries */
#menu .more li:nth-child(1),
#menu .more li:nth-child(2),
#menu .more li:nth-child(3) {
display: none;
}
#media screen and (max-width: 350px) {
#menu .primary li:nth-child(3) {
display: none;
}
#menu .more li:nth-child(3) {
display: block;
}
}
#media screen and (max-width: 300px) {
#menu .primary li:nth-child(2) {
display: none;
}
#menu .more li:nth-child(2), #menu .more li:nth-child(3) {
display: block;
}
}
JsFiddle
This can be easily done using Bootstrap 3. As long as you don't intend to just primarily use Javascript, Bootstrap3's .hidden-xs, .hidden-sm, .hidden-md, .hidden-lg should suffice.
<div class="container">
<ul class="nav nav-pills nav-stacked">
<li class="active">Shown 1
</li>
<li>Shown 2
</li>
<li>Show 3
</li>
<li class="hidden-xs xs">Hidden XS
</li>
<li class="hidden-sm sm">Hidden SM
</li>
<li class="hidden-md md">Hidden MD
</li>
<li class="hidden-lg lg">Hidden LG
</li>
</ul>
</div>
http://jsfiddle.net/jLxXz/
Ok this goes long way to the Update of Bootstrap 3.1.1 from 3.0 by the introduction of "hidden-xs,visible-lg" classes.
Now all I did was create a new object class:"hidden-sm hiddem-xs" and ask my ng-class="{{item.class}} in AngularJS to fetch and add class dynamically which solves the problem by setting different classes accordingly for different menu items.

How to make drop-down menu appear over slideshow?

I have my drop down menu directly above the slideshow. Because of this, the sub-menus get hidden behind the slideshow when I hover over the menu. I would like the sub-menus to appear over the slideshow.
Slideshow Code
var o=String.fromCharCode(60);var c=String.fromCharCode(62)
document.write(o+'iframe sr'+'c="http://slideful.com/vid.htm" frameborder="0" sty'+'le="border:0px;padding:0px;margin:0px;width:900px;height:563px;" allowtransparency="true"'+c+o+'/iframe'+c)
Dropdown Code
.tab {
font-family: arial, verdana, san-serif;
font-size: 14px;
}
.asd {
text-decoration: none;
font-family: arial, verdana, san-serif;
font-size: 13px;
color:#4234ff;
}
/*****remove the list style****/
#nav {
margin:0;
padding:0;
list-style:none;
}
/*****LI display inline *****/
#nav li {
float:left;
display:block;
width:100px;
background:#1E5B91;
position:relative;
z-index:500;
margin:0 1px;
}
/*****parent menu*****/
#nav li a {
display:block;
padding:8px 5px 0 5px;
font-weight:700;
height:23px;
text-decoration:none;
color:#ffffff;
text-align:center;
color:#ffeecc;
}
#nav li a:hover {
color:#470020;
}
#nav a.selected { /* style for default selected value */
color:#4234ff;
}
#nav ul { /* submenu */
position:absolute;
left:0;
display:none;
margin:0 0 0 -1px;
padding:0;
list-style:none;
}
#nav ul li {
width:100px;
float:left;
border-top:1px solid #fff;
}
#nav ul a { /* display block will make the link fill the whole area of LI */
display:block;
height:15px;
padding: 8px 5px;
color:#ff7777;
}
#nav ul a:hover {
text-decoration:underline;
padding-left:15px;
}
Dropdown jQuery
$(document).ready(function () {
$('#nav li').hover(function () {
$('ul', this).slideDown(350); //show its submenu
}, function () {
$('ul', this).slideUp(350); //hide its submenu
});
});
Dropdown HTML
<input type=hidden name=arav value="1*#*#*2">
<ul id='nav'>
<li><a href='#'>SHOP</a>
<ul>
<li style='background-color:#b0c4de;'><a href=http://link.com>Womens</a></li>
<li style='background-color:#bebebe;'><a href=http://link.com>Mens</a></li>
</ul>
</li>
</ul>
</input
I would like my submenus that are shown when you hover over the "Shop" button to show up over the slideshow that is below it. They hide under it.
#nav ul { /* submenu */
position:absolute;
left:0;
display:none;
margin:0 0 0 -1px;
padding:0;
list-style:none;
z-index:9999;
}
#nav ul li {
width:100px;
float:left;
z-index:9999;
border-top:1px solid #fff;
}
Using z-index you can achieve this.
You will want to wrap your iframe and give that container a z-index value of 1 and the navigation container should have a z-index value of 2.
You will also need to set the position property value to relative for both of these containers.
#nav {position: relative; z-index: 2;}
#iframeContainerName {position: relative; z-index: 1;}
Also, you may want to look into other ways of implementing the slideshow. Using document.write has potential issues: Why is document.write considered a "bad practice"?
I'm not sure how this would work if you're viewing your slideshow as in your code above (I'm not very familiar with Javascript or how this would work with iframes).
However, I came here also searching for answers, so I will post my solution in the case that it may help anyone who finds this page as I did.
I had the same issue, although my slideshow code looked like this.
<ul class="bjqs">
<li><img src="kjdsajkdhsja.png"></li>
</ul>
I found that changing the z-index to all the elements relating to the slideshow to lower than the drop down elements would not work - until I made my images background images in divs, as so:
<ul class="bjqs">
<li><div style="background-image:url(kjdsajkdhsja.png);width:200px;height:75px;"></div></li>
</ul>
Crummy workaround, but it did me fine for the project I was on.
For showing dropdown menu over slide show
write the code in css where you have written dropdown menu code
.dropdown_menu { visibility: visible; z-index: 100; }
it will work

Displaying submenus on hover without javascript

I have a webpage that uses jquery to display a submenu div while a user is hovering over an a:link in the main parent menu.
$('.menu ul li').hover(function() {
$(this).find('.dropnav').stop(true, true).fadeTo('fast', 1);
}, function() {
$(this).find('.dropnav').stop(true, true).fadeOut(800, 0);
});
The problem is, I want this webpage's navigation feature to be independent of javascript. So when users do not have javascript enabled, the menu will still display - just without the effects of scrolls or fades.
Thanks.
Use the :hover CSS pseudo-class.
.menu ul li:hover .dropnav {
opacity: 1;
/* display: block; ? */
}
Here is a pretty solid example of a CSS based menu. There is JavaScript that goes with it, if you are looking for backwards compatibility to IE6.
http://qrayg.com/learn/code/cssmenus/
HTML
<ul class="main-nav">
<li>main nav-1
</li>
<li>main nav-2
<ul class="sub-nav">
<li>sub-nav-2.1</li>
<li>sub-nav-2.2</li>
<li>sub-nav-2.3</li>
</ul>
</div>
</li>
<li>main nav-3
<li>main nav-4
</ul>
css
ul.main-nav > li { position: relative; display: block; float: left; margin: 0 15px;}
ul.main-nav > li > a {display: block; line-height: 40px; }
ul.sub-nav { display:none; position: absolute; top: 40px; left: 0; min-width: 200px;}
ul.main-nav > li:hover ul.sub-nav { display: block; z-index: 999; }
check this one for live demo http://jsfiddle.net/q9YZf/

Categories

Resources