Addressing issue with disappearing li elements in navbar (HTML, CSS, JS) - javascript

document.addEventListener("DOMContentLoaded", () => {
let menuBtn = document.querySelector("#menu-button");
let menu = document.querySelector("#menu");
let menuItems = menu.getElementsByTagName("li");
menuBtn.addEventListener("click", e => {
if (e.target.innerText === ("✕")) {
e.target.innerText = "☰";
[...menuItems].forEach(item => item.style.display = "none");
} else if (e.target.innerText === "☰") {
e.target.innerText = "✕";
[...menuItems].forEach(item => item.style.display = "block");
}
});
});
header {
width: 100%;
height: 100px;
}
#menu {
display: flex;
flex-direction: row;
margin: 0 auto;
justify-content: space-around;
font-size: 2rem;
}
#menu-button {
display: none;
}
li {
list-style-type: none;
}
a {
text-decoration: none;
color: rgb(0, 0, 0);
}
#media screen and (max-width: 1050px) {
#menu {
flex-direction: column;
width: 100%;
height: 100%;
}
#menu li {
padding: 1.5rem;
display: none;
}
#menu-button {
display: block;
font-size: 3rem;
cursor: pointer;
outline: none;
border: none;
}
}
<header>
<nav>
<button id="menu-button">☰</button>
<ul id="menu">
<li>Home</li>
<li>About</li>
<li>Contact</li>
</ul>
</nav>
</header>
It's a simple nav menu with desktop-first approach that uses flexbox.
Viewport is less than 1050px and therefore shows hamburger button
Hamburger button is clicked to show menu in dropdown form. Hamburger button changes to "X".
"X" is clicked. The JS code changes all li elements to display: none
PROBLEM: Viewport is increased beyond breakpoint of 1050px. Since li elements were changed to display: none, menu is not shown.
Would appreciate suggestions on how to address this.
Also, smaller but nagging issue.
Viewport is less than 1050px and therefore shows hamburger button
Hamburger button is clicked to show menu in dropdown form. Hamburger button changes to "X".
Viewport is increased beyond breakpoint of 1050p and then back below breakpoint. Since all li elements were set to display: block in JS code, the dropdown menu appears. I'd like to make it so that the hamburger icon appears whenever viewport size is decreased.
Thanks for all and any help!

Add & remove CSS classes instead of setting the inline style. Doing this will give CSS more control. Inline styles can only be overwritten by using !important and that will make it even harder to overwrite.
Instead of showing and hiding every individual <li>, only hide the parent <ul>. That will also hide all the children inside of it.
The example below adds and removes an active class to the #menu element when clicking the menu button. This active class shows the #menu on mobile where it is hidden. On larger screens, #menu is always shown.
document.addEventListener("DOMContentLoaded", () => {
let menuBtn = document.querySelector("#menu-button");
let menu = document.querySelector("#menu");
// First question:
// Add a class instead of inline styles.
menuBtn.addEventListener("click", e => {
if (e.target.innerText === ("✕")) {
e.target.innerText = "☰";
menu.classList.remove('active');
} else if (e.target.innerText === "☰") {
e.target.innerText = "✕";
menu.classList.add('active');
}
});
// Second question:
// Watch a media query, reset the button and hide the
// menu when changing from large to a small size.
const mediaQuery = window.matchMedia('screen and (max-width: 1050px)');
mediaQuery.addEventListener('change', ({ matches }) => {
if (!matches) return;
menuBtn.innerText = "☰";
menu.classList.remove('active');
});
});
header {
width: 100%;
height: 100px;
}
#menu {
display: flex;
flex-direction: row;
margin: 0 auto;
justify-content: space-around;
font-size: 2rem;
}
#menu-button {
display: none;
}
li {
list-style-type: none;
}
a {
text-decoration: none;
color: rgb(0, 0, 0);
}
#media screen and (max-width: 1050px) {
#menu {
display: none;
flex-direction: column;
width: 100%;
height: 100%;
}
#menu.active {
display: flex;
}
#menu li {
padding: 1.5rem;
}
#menu-button {
display: block;
font-size: 3rem;
cursor: pointer;
outline: none;
border: none;
}
<header>
<nav>
<button id="menu-button">☰</button>
<ul id="menu">
<li>Home</li>
<li>About</li>
<li>Contact</li>
</ul>
</nav>
</header>

Related

HTML navigation of tabs broken

I have my portfolio website in English and translated it in to German with a combination of JSON and Javascript. I have a dropdown menu to pick a language, and once a language is picked a javascript script switches the content of every indicated id with the content of the other language.
I also have a navigation menu which gets underlined when you hover over it and when you click it, it takes you to its respective area on the website. However, the moment the user switches the language, both of these functions do not work anymore i.e the href="#header# as well as nav ul li a:hover::after{} break.
You can mimic this behaviour at alexverheecke.com. Before selecting a language, you can hover over "Home", "About" and it will become underlined and upon clicking, will take you to the section. Once you switch language, this breaks.
I'm assuming this will be a bit time-consuming for someone to look at but I would appreciate any ideas that could help in fixing this.
const jsonDE = {
"_Home": "Startseite",
// ...
}
document.querySelector('#language').addEventListener("change", function() {
if (this.value == "🏴󠁧󠁢󠁥󠁮󠁧󠁿 ENG") {
for (let key in jsonEN) {
document.querySelector('#' + key).textContent = jsonEN[key]
}
else if (this.value == "🇩🇪 DE") {
for (let key in jsonDE) {
document.querySelector('#' + key).textContent = jsonDE[key]
}
}
});
nav {
display: flex;
/* so image and links side-by-side */
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
}
nav ul li {
display: inline-block;
/* so horizontally aligned */
list-style: none;
margin: 10px 20px;
/* space between links */
}
nav ul li a {
color: white;
text-decoration: none;
font-size: 18px;
position: relative;
/* because abolute in :after */
}
nav ul li a::after {
content: '';
width: 0%;
height: 3px;
background: #3a65ed;
position: absolute;
left: 0;
bottom: -6px;
transition: 0.2s;
}
nav ul li a:hover::after {
width: 100%;
}
<nav>
<ul id="sidemenu">
<li id="_Home">Home </li>
<select id="language" class="language">
<option>🏴󠁧󠁢󠁥󠁮󠁧󠁿 ENG</option>
<option>🇩🇪 DE</option>
</select>
</ul>
</nav>
If you check your site with the DOM inspector you can see that after you change language the a elements have been removed from within the li of your navigation bar.
I would assume this is because your JSON content holds HTML, yet you're updating the textContent of the element. Change textContent to innerHTML and try again.
Also note that you can simplify the language switching logic by putting the language code as a property within a single object of the JSON. Then you only need one loop to work with every language. Note the use of a value attribute on the option elements to avoid the need to have to cater for the subscript language codes which have been added to the text within the UI of the option.
Below is a working example with both of the above issues corrected:
// mock JSON object...
const translations = {
"DE": {
"_Home": "Startseite"
},
"EN": {
"_Home": "Home"
},
"IT": {
"_Home": "Casa"
}
}
// content switching logic
document.querySelector('#language').addEventListener("change", function() {
for (let key in translations[this.value]) {
document.querySelector('#' + key).innerHTML = translations[this.value][key]
}
});
nav {
display: flex;
/* so image and links side-by-side */
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
}
nav ul li {
display: inline-block;
/* so horizontally aligned */
list-style: none;
margin: 10px 20px;
/* space between links */
}
nav ul li a {
/* color: white; removed so white text is visible */
text-decoration: none;
font-size: 18px;
position: relative;
/* because abolute in :after */
}
nav ul li a::after {
content: '';
width: 0%;
height: 3px;
background: #3a65ed;
position: absolute;
left: 0;
bottom: -6px;
transition: 0.2s;
}
nav ul li a:hover::after {
width: 100%;
}
<nav>
<ul id="sidemenu">
<li id="_Home">Home </li>
</ul>
</nav>
<select id="language" class="language">
<option value="EN">🏴󠁧󠁢󠁥󠁮󠁧󠁿 ENG</option>
<option value="DE">🇩🇪 DE</option>
<option value="IT">ᴵᵀ IT</option>
</select>

Automatically scroll through navigation based on active element

I'm about to give up, so I'm rewriting this question one more time. I just can't make it work, I have too much left to learn for this to work out. I really tried my best but I need help.
As you can see on my Codepen I managed to make it work "somehow". But I know the calculation is wrong and can't even think about how it could work when scrolling back up.
https://codepen.io/Nimyr/pen/ZEaWpzb
All I want is a navigation like here:
https://www.lieferando.de/speisekarte/bella-italia-bunde
As you can see the navigation sticks to the top and moves the navigation-element thats fitting the currently active section automatically to the left as you scroll down.
I've set up a WordPress Site where I am trying to make the navigation work just like that. Any ideas? :( I'm really lost.
The usecase for this is pretty simple: I want to make a (especially mobile-)user-friendly page that shows a restaurant's menu.
Please don't hesitate to ask any questions if my explanation is weak, I don't know how to formulate it more precisely.
Thanks in advance!
I will also post the code from the Codepen:
HTML
<body>
<nav>
<div class="nav-test">
<ul class="nav-test2">
<li>About</li>
<li>Team</li>
<li>Gallery</li>
<li>Testimonials</li>
<li>Contact</li>
</ul>
</div>
</nav>
<section class="about">
<h1>About</h1>
</section>
<section class="team">
<h1>Team</h1>
</section>
<section class="gallery">
<h1>Gallery</h1>
</section>
<section class="testimonials">
<h1>Testimonials</h1>
</section>
<section class="contact">
<h1>Contact</h1>
</section>
</body>
CSS
* {
margin: 0;
padding: 0;
}
body {
font-family: Verdana, serif;
color: #fff;
}
nav {
position: fixed;
top: 0;
left: 0;
width: 100%;
text-align: center;
background: #515151;
}
.nav-test {
display: flex;
justify-content: center;
}
nav ul {
width: 500px;
overflow: scroll hidden;
display: flex;
flex-wrap: nowrap;
}
nav ul li {
list-style: none;
display: inline-block;
}
nav ul li a {
display: block;
padding: 20px 40px;
text-decoration: none;
color: #fff;
transition: background-color .4s ease;
}
nav ul li a.active {
background-color: #FF0000;
}
section {
padding: 30% 0;
text-align: center;
}
section.about {
background: #888;
}
section.team {
background: skyblue;
}
section.gallery {
background: #aaa;
}
section.testimonials {
background: orange;
}
section.contact {
background: #ccc;
}
JavaScript (please note that this project also contains highlighting the active navigation element through a change in background color. This is purely optional, I need the automatic scroll. But I started with a Codepen created by Kon Kim as a basic structure to figure the rest out. I failed. gg
(function(){
var navLinks = $('nav ul li a'),
navH = $('nav').height(),
section = $('section'),
documentEl = $(document);
documentEl.on('scroll', function() {
var currentScrollPos = documentEl.scrollTop();
section.each(function(){
var self = $(this);
if ( self.offset().top < (currentScrollPos + navH) && (currentScrollPos + navH) < (self.offset().top + self.outerHeight() ) ) {
var
targetClass = '.' + self.attr('class') + '-marker',
width = $(targetClass).outerWidth();
navLinks.removeClass('active'),
$(targetClass).addClass('active');
if ( $(targetClass).offset().left > $('.nav-test2').offset().left)
{
var
newWidth = $(targetClass).offset().left - $('.nav-test2').offset().left;
$('.nav-test2').animate(
{scrollLeft: newWidth},
{duration: 150}
);
return false;
}
}
});
});
})();

Toggle An Element And Also Remove Its Visibility When Clicking Outside Of The Element - JavaScript

I have a navigation with two items that have submenus. I currently have a class that toggles on and off that shows these submenus when clicked.
I would like it so when I click anywhere on the page they disappear if they are visible.
At the moment I think my code is a bit long-winded for what it currently achieves and perhaps it would be better to use e.target when clicking?
You can currently toggle the menus off-and-on by clicking either menu-item (this includes clicking the visible menu item a second time).
I thought to remove the 'visible' class by clicking outside of the menu-item I could do a simple document.addEventListener('click', function(e) {}) on the entire document to remove the 'visible' class if it was showing, but that doesn't seem to work.
Note: I need to do this without using a blur event listener
Codepen: https://codepen.io/emilychews/pen/bGWVVpq
var menu_item_1 = document.getElementById('item-1'),
menu_item_2 = document.getElementById('item-2'),
sub_menu_item_1 = document.getElementById('sub-item-1'),
sub_menu_item_2 = document.getElementById('sub-item-2')
if (menu_item_1) {
menu_item_1.addEventListener('click', function(e){
sub_menu_item_1.classList.toggle('visible')
// hide submenu 2
sub_menu_item_2.classList.remove('visible')
}, false)
}
if (menu_item_2) {
menu_item_2.addEventListener('click', function(e){
sub_menu_item_2.classList.toggle('visible')
// hide submenu 1
sub_menu_item_1.classList.remove('visible')
}, false)
}
body {
display: flex;
justify-content: center;
margin: 0;
height: 100vh;
width: 100%;
}
header {
margin-top: 2rem;
display: flex;
width: 50%;
justify-content: space-evenly;
align-items: center;
padding: 1rem;
background: red;
height: 2rem;
}
.menu-item {
position: relative;
padding: 1rem;
background: yellow;
cursor: pointer;
}
.submenu {
display: none; /* changes to 'block' with javascript */
padding: 1rem;
background: lightblue;
position: absolute;
top: 4rem;
left: 0;
width: 6rem;
}
.submenu.visible {
display:block;
}
<header>
<div id="item-1" class="menu-item menu-item-1">ITEM 1
<div id="sub-item-1" class="submenu submenu-1">SUB-ITEM-1</div>
</div>
<div id="item-2" class="menu-item menu-item-2">ITEM 2
<div id="sub-item-2" class="submenu submenu-2">SUB-ITEM-2</div>
</div>
</header>
There are a few different ways to achieve this, not all of which involve JS, I'll outline a few possible approaches below:
Pure CSS:
The first (and most likely easiest) is to use css-only. This again uses tabindex="-1" like Samuel's answer to make your menu item buttons focusable. Once a button is focused, you can apply some CSS to the focused item's associated submenu using the :focus pseudo-class selector:
.menu-item:focus > .submenu { /* select the focused menu-item's child elements with the class submenu */
display: block;
}
See example below:
body {
display: flex;
justify-content: center;
margin: 0;
height: 100vh;
width: 100%;
}
header {
margin-top: 2rem;
display: flex;
width: 50%;
justify-content: space-evenly;
align-items: center;
padding: 1rem;
background: red;
height: 2rem;
}
.menu-item {
position: relative;
padding: 1rem;
background: yellow;
cursor: pointer;
}
.submenu {
display: none; /* changes to 'block' with CSS */
padding: 1rem;
background: lightblue;
position: absolute;
top: 4rem;
left: 0;
width: 6rem;
}
.menu-item:focus > .submenu {
display: block;
}
<header>
<div id="item-1" class="menu-item menu-item-1" tabindex="-1">ITEM 1
<div id="sub-item-1" class="submenu submenu-1">SUB-ITEM-1</div>
</div>
<div id="item-2" class="menu-item menu-item-2" tabindex="-1">ITEM 2
<div id="sub-item-2" class="submenu submenu-2">SUB-ITEM-2</div>
</div>
</header>
The main drawback to this is that we're using :focus, meaning that if you click on a menu-item again, it will remain focused rather than bluring, which as a result will keep the menu item in-view rather than hiding it. The below approaches that use JS handle this case though:
Adding an event-listener to the document:
Another possible solution is to update your JS. This involves selecting all menu items and submenu items using querySelectorAll(). You can then add event listeners to your menu-items by looing through the NodeList returned by the call to .querySelectorAll(). When you click on a menu-item, you can grab its associated submenu item using .querySelector() on the current menuItem. In order to hide the items when you click elsewhere on the screen, you can listen for click events on the document by adding an event listener to that, and hide your submenu items accordingly. Within your event listeners that you add to your menu items, you can call .stopPropagation() to prevent the click event on the menu items from bubbling up to the document and causing the document event-listener to execute (and hide all items).
const menuItems = document.querySelectorAll(".menu-item"); // Get all menu items in an array-like structure (NodeList)
const submenuItems = document.querySelectorAll(".submenu"); // select all submenu items
const hideMenus = (menus, ignore) => menus.forEach(menu => { // loop through all items (use: [...menus].forEach((menu) => {) for better browser support)
if (menu !== ignore) // if we encounter an element that we want to keep visible, skip it, otherwise, remove its visibility
menu.classList.remove("visible");
});
menuItems.forEach(menuItem => { // loop through the NodeList menu items
menuItem.addEventListener("click", (e) => {
e.stopPropagation(); // stop event from bubbling up to the document and executing the below `document.addEventListener()` when menu item is clicked
if (e.target === menuItem) { // don't hide when we click on a sub-menu-item (e.target = child sub-menu-item if that is clicked)
const thisSubmenu = menuItem.querySelector(".submenu");
thisSubmenu.classList.toggle('visible'); // toggle visibility of submenu under our item
hideMenus(submenuItems, thisSubmenu); // hide all other submenus
}
});
});
document.addEventListener("click", (e) => {
hideMenus(submenuItems);
});
body {
display: flex;
justify-content: center;
margin: 0;
height: 100vh;
width: 100%;
}
header {
margin-top: 2rem;
display: flex;
width: 50%;
justify-content: space-evenly;
align-items: center;
padding: 1rem;
background: red;
height: 2rem;
}
.menu-item {
position: relative;
padding: 1rem;
background: yellow;
cursor: pointer;
}
.submenu {
display: none;
/* changes to 'block' with javascript */
padding: 1rem;
background: lightblue;
position: absolute;
top: 4rem;
left: 0;
width: 6rem;
}
.submenu.visible {
display: block;
}
<header>
<div id="item-1" class="menu-item menu-item-1">ITEM 1
<div id="sub-item-1" class="submenu submenu-1">SUB-ITEM-1</div>
</div>
<div id="item-2" class="menu-item menu-item-2">ITEM 2
<div id="sub-item-2" class="submenu submenu-2">SUB-ITEM-2</div>
</div>
</header>
Using event delegation:
You can update the above example to use event delegation, which allows you to only use one event listener on the document rather than adding one per menu item (thus helping limit the resources used by your browser). You can then use e.target and .closest() to determine what element you clicked on (see code comments for details):
const submenuItems = document.querySelectorAll(".submenu"); // select all submenu items
const hideMenus = (menus, ignore) => menus.forEach(menu => { // loop through all items (use: [...menus].forEach((menu) => {) for better browser support)
if(menu !== ignore) // if we encounter an element that we want to keep visible, skip it, otherwise, remove its visibility
menu.classList.remove("visible");
});
document.addEventListener("click", (e) => {
const clickedItem = e.target, menuItem = clickedItem.closest(".menu-item");
// v-- use `= menuItem && menuItem.querySelector(...)` for better browser support
const thisSubmenu = menuItem?.querySelector(".submenu"); // grab the submenu from the menuItem we clicked on (or parent menuItem if we clicked on a submenu item)
if(clickedItem === menuItem) // we clicked on a menu-item
thisSubmenu.classList.toggle('visible'); // toggle visibility of submenu under our menu-item
hideMenus(submenuItems, thisSubmenu);
});
body {
display: flex;
justify-content: center;
margin: 0;
height: 100vh;
width: 100%;
}
header {
margin-top: 2rem;
display: flex;
width: 50%;
justify-content: space-evenly;
align-items: center;
padding: 1rem;
background: red;
height: 2rem;
}
.menu-item {
position: relative;
padding: 1rem;
background: yellow;
cursor: pointer;
}
.submenu {
display: none; /* changes to 'block' with javascript */
padding: 1rem;
background: lightblue;
position: absolute;
top: 4rem;
left: 0;
width: 6rem;
}
.submenu.visible {
display:block;
}
<header>
<div id="item-1" class="menu-item menu-item-1">ITEM 1
<div id="sub-item-1" class="submenu submenu-1">SUB-ITEM-1</div>
</div>
<div id="item-2" class="menu-item menu-item-2">ITEM 2
<div id="sub-item-2" class="submenu submenu-2">SUB-ITEM-2</div>
</div>
</header>
I also added console.log on each submenu to make sure they are interactive before the menu closes.
const menus = Array.from(document.querySelectorAll('.menu-item'));
function handleOnClickOutsideMenu(e) {
const target = menus.filter(menu => menu.contains(e.target));
if (target.length) {
// user is clicking inside a menu: don't do anything.
// this is handled by handleOnMenuToggle.
return;
}
// close all the menus in the page
menus.forEach(menu => menu.classList.remove('expanded'));
// we don't need it anymore (it is added dynamically in the handleOnMenuToggle)
document.removeEventListener('click', handleOnClickOutsideMenu);
}
function handleOnMenuToggle(e) {
// close other menus
menus
.filter(menu => menu !== e.currentTarget)
.forEach(menu => menu.classList.remove('expanded'));
// toggle current menu
e.currentTarget.classList.toggle('expanded');
// Important optimization:
// we want the click event on the document only when a menu is expanded
if (e.currentTarget.classList.contains('expanded')) {
document.addEventListener('click', handleOnClickOutsideMenu);
} else {
document.removeEventListener('click', handleOnClickOutsideMenu);
}
}
menus.forEach(menu => {
menu.addEventListener('click', handleOnMenuToggle);
});
body {
display: flex;
justify-content: center;
margin: 0;
height: 100vh;
width: 100%;
}
header {
margin-top: 2rem;
display: flex;
width: 50%;
justify-content: space-evenly;
align-items: center;
padding: 1rem;
background: red;
height: 2rem;
}
.menu-item {
position: relative;
padding: 1rem;
background: yellow;
cursor: pointer;
}
.submenu {
display: none;
padding: 1rem;
background: lightblue;
position: absolute;
top: 4rem;
left: 0;
width: 6rem;
}
/* adding .expanded on menu-item so it can handle multiple sub menu */
.menu-item.expanded .submenu {
display: block;
}
<header>
<div id="item-1" class="menu-item menu-item-1">ITEM 1
<div id="sub-item-1" class="submenu submenu-1" onclick="console.log(this)">SUB-ITEM-1</div>
</div>
<div id="item-2" class="menu-item menu-item-2">ITEM 2
<div id="sub-item-2" class="submenu submenu-2" onclick="console.log(this)">SUB-ITEM-2</div>
</div>
</header>
One way to solve this would be to take advantage of focus and blur events. div elements do not receive focus by default, but we can add the tabindex attribute to fix that.
When you click the div it becomes focused, so we simply listen for a blur event and hide the div.
var menu_item_1 = document.getElementById('item-1'),
menu_item_2 = document.getElementById('item-2'),
sub_menu_item_1 = document.getElementById('sub-item-1'),
sub_menu_item_2 = document.getElementById('sub-item-2')
if (menu_item_1) {
menu_item_1.addEventListener('click', function(e){
sub_menu_item_1.classList.toggle('visible')
// hide submenu 2
sub_menu_item_2.classList.remove('visible')
}, false)
}
if (menu_item_2) {
menu_item_2.addEventListener('click', function(e){
sub_menu_item_2.classList.toggle('visible')
// hide submenu 1
sub_menu_item_1.classList.remove('visible')
}, false)
}
// listen for blur events
menu_item_1.addEventListener('blur', function(e){ sub_menu_item_1.classList.remove('visible')
})
menu_item_2.addEventListener('blur', function(e){ sub_menu_item_2.classList.remove('visible')
})
body {
display: flex;
justify-content: center;
margin: 0;
height: 100vh;
width: 100%;
}
header {
margin-top: 2rem;
display: flex;
width: 50%;
justify-content: space-evenly;
align-items: center;
padding: 1rem;
background: red;
height: 2rem;
}
.menu-item {
position: relative;
padding: 1rem;
background: yellow;
cursor: pointer;
}
.submenu {
display: none; /* changes to 'block' with javascript */
padding: 1rem;
background: lightblue;
position: absolute;
top: 4rem;
left: 0;
width: 6rem;
}
.submenu.visible {
display:block;
}
<header>
<div id="item-1" class="menu-item menu-item-1" tabindex="-1">ITEM 1
<div id="sub-item-1" class="submenu submenu-1" tabindex="-1">SUB-ITEM-1</div>
</div>
<div id="item-2" class="menu-item menu-item-2" tabindex="-1">ITEM 2
<div id="sub-item-2" class="submenu submenu-2" tabindex="-1">SUB-ITEM-2</div>
</div>
</header>

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 change label into dropdown when size reduce?

hello
I am trying to change the layout of my screen when my large screen move to small screen .When I have large screen I show my menu option on header as a label .which i am able to make .But when I reduce the screen size to 600px width I need to show this on dropdown the menu options
how will I do this ?
I search on google and find using media query it is possible .I try to implement this . but I got event but how it is possible
here is my code and images
http://codepen.io/anon/pen/LpGKYO
#menubar li {
display: inline;
padding:0.5em;
font-size:1.5em;
color :red
}
#media screen and (max-width: 600px) {
body {
background-color: lightblue;
}
}
#menubar{
float:right;
display: block;
position:relative;
}
#menubarCont {
width: 100%;
float: right ;
right:10em;
position:relative;
}
You'll just need media queries to hide the menu you don't want to show. Here's a simple example: (resize browser window to test)
.menu {
width: 100%;
height: 60px;
background-color: orange;
}
#media screen and (max-width: 600px) {
.large {
display: none;
}
}
#media screen and (min-width: 600px) {
.small {
display: none;
}
}
<div class="menu">
<div class="large">
Large screen menu
</div>
<div class="small">
<select>
<option>menu item 1</option>
<option>menu item 2</option>
</select>
</div>
</div>
And here's a Fiddle too
Just throwing my two cents. The answer from #Schlaus is preferred way to do it and the way I would also do it. Here is a code pen I found of a way to do it without having to code the menu twice in the markup - it's a good reference for those that are curious for other alternative ways of getting it done using JQuery.
http://codepen.io/ericrasch/pen/GlBed
HTML
<nav>
<h1>This menu turns into a <code><select></code> when window is less than 960px to conserve space.</h1>
<ul>
<li>Home</li>
<li>Books</li>
<li>Blog</li>
<li>About Us</li>
<li>Support</li>
</ul>
</nav>
CSS
* {
margin: 0;
padding: 0;
}
h1 {
font: 300 21px "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
width: 500px;
margin: 0 auto 15px;
}
nav {
display: block;
width: 960px;
margin: 100px auto;
text-align: center;
ul {
list-style: none;
}
li {
display: inline-block;
}
a {
display: inline-block;
background: #333;
color: white;
padding: 5px 15px;
border: 1px solid white;
text-decoration: none;
&:hover {
border: 1px solid red;
background: red;
}
&:active {
background: blue;
}
}
select {
display: none;
}
}
#media (max-width: 960px) {
nav {
ul {
display: none;
}
select {
display: inline-block;
}
}
}
JQuery
// DOM ready
$(function() {
// Create the dropdown base
$("<select />").appendTo("nav");
// Create default option "Go to..."
$("<option />", {
"selected": "selected",
"value" : "",
"text" : "Go to..."
}).appendTo("nav select");
// Populate dropdown with menu items
$("nav a").each(function() {
var el = $(this);
$("<option />", {
"value" : el.attr("href"),
"text" : el.text()
}).appendTo("nav select");
});
// To make dropdown actually work
// To make more unobtrusive: http://css-tricks.com/4064-unobtrusive-page-changer/
$("nav select").change(function() {
window.location = $(this).find("option:selected").val();
});
});
Hope this clears up a few things for others and hopefully helps someone else in the future
Cheers

Categories

Resources