Dropdown menu w/ custom hover handler, mouse move to submenu - javascript

I handle hover event by my own to add a triangle pointer and a horizontally aligned submenu bar. It's working fine for showing and navigating to the submenu when the top menu is clicked. The problem is during hover, the submenu bar will disappear when trying to move mouse to the submenu.
TIA
screenshot
Code:
$("#menubar li.dropdown>a.nav-link").hover(function() {
if ($("#menubar li.dropdown").filter(function() {
return $(this).data("show")
}).length === 0)
$(this).closest("li").addClass("active show");
},
function() {
/*when top menu is clicked, don't hide submenu*/
if ($("#menubar li.dropdown").filter(function() {
return $(this).data("show")
}).length === 0)
$(this).closest("li").removeClass("active show");
});
$("#menubar li>a.nav-link").on("click", function(e) {
e.stopPropagation();
var $li = $(this).closest("li");
if (!$li.hasClass("dropdown")) {
$("#menubar li.dropdown").removeClass("active show");
$("#menubar li.dropdown").data("show", false);
} else {
if ($li.data("show")) {
$li.removeClass("active show");
$li.data("show", false);
} else {
$("#menubar li.dropdown").removeClass("active show");
$li.addClass("active show");
$li.data("show", true);
}
}
});
$("#menubar ul.dropdown-menu>li>a").on("click", function(e) {
$("#menubar li.dropdown").removeClass("active show");
$("#menubar li.dropdown").data("show", false);
});
#menubar ul {
height: 44px;
padding-top: 5px;
}
#menubar .nav-link {
font-size: 14px;
padding: 8px 50px 0 0 !important;
color: white !important;
}
#menubar .nav-link:focus,
#menubar .nav-link:hover,
#menubar .nav-link:visited {
color: white !important;
}
#menubar .navbar {
padding-left: 0 !important;
}
.navbar.navbar-dark {
height: 44px;
margin: 0 15%;
}
nav.navbar .navbar-nav li.nav-item.active:after {
content: "";
position: relative;
margin-left: -31px;
left: 50%;
bottom: 15px;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid white;
}
/*submenu*/
#menubar li.dropdown.show {
position: static;
}
#menubar li.dropdown.show .dropdown-menu {
display: table;
width: 100%;
text-align: center;
left: 0;
right: 0;
margin: 0;
}
.dropdown-menu>li {
display: table-cell;
padding-top: 6px;
}
.dropdown-menu>li a {
font-weight: 600;
}
.dropdown-menu>li a:hover {
text-decoration: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<nav class="navbar navbar-expand-md navbar-dark ">
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#menubar">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse" id="menubar">
<ul class="nav navbar-nav">
<li class="nav-item ">
<a class="nav-link" href="#">Menu1</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link" data-toggle="dropdown" href="#">Menu2</a>
<ul class="dropdown-menu" role="menu">
<li>Submenu1</li>
<li>Submenu2</li>
<li>Submenu3</li>
</ul>
</li>
</ul>
</div>
</nav>

You need to change hover event from link to list item, it is a common practice for your case:
$("#menubar li.dropdown").hover(function() {
...
});

Related

jQuery On Click Child Element does not remove the parent Element Class

This snippet I supposed to work but it is not working. On click it adds the class to the parent element, but on click of a child element it doesn't remove the class. I also added JS Fiddle to test for you. Thanks in advance.
JS Fiddle: https://jsfiddle.net/fghjqepv/1/
jQuery(document).ready(function($) {
$(".menu-item-has-children").addClass("openLevel js-openLevel");
var $menuTrigger = $('.js-menuToggle');
var $topNav = $('.js-topPushNav');
var $openLevel = $('.js-openLevel');
var $closeLevel = $('.js-closeLevel');
var $closeLevelTop = $('.js-closeLevelTop');
var $navLevel = $('.js-pushNavLevel');
function openPushNav() {
$topNav.addClass('isOpen');
//$('body').addClass('pushNavIsOpen');
}
function closePushNav() {
$topNav.removeClass('isOpen');
$openLevel.siblings().removeClass('isOpen');
//$('body').removeClass('pushNavIsOpen');
}
$menuTrigger.on('click touchstart', function(e) {
e.preventDefault();
if ($topNav.hasClass('isOpen')) {
closePushNav();
} else {
openPushNav();
}
});
$openLevel.on('click touchstart', function() {
$(this).find($navLevel).addClass('isOpen');
});
$closeLevel.on('click touchstart', function() {
$(this).closest($navLevel).removeClass('isOpen');
});
$closeLevelTop.on('click touchstart', function() {
closePushNav();
});
});
#media screen and (max-width: 600px) {
.pushNav {
width: 75%;
right: -75%;
}
}
#media screen and (min-width: 601px) {
.pushNav {
width: 350px;
right: -350px;
}
}
ul.pushNav {
padding: 0;
margin: 0;
list-style-type: none;
}
.pushNav {
height: 100%;
position: fixed;
top: 0;
z-index: 100;
overflow-x: hidden;
overflow-y: auto;
background: #2e2f35;
transition: ease-in-out 0.5s;
}
.pushNav hr {
border: 1px solid #555;
}
.pushNav,
.pushNav a {
font-size: 1em;
font-family: helvetica, sens-serif;
font-weight: 100;
color: #fff;
text-decoration: none;
}
.pushNavIsOpen {
overflow: auto;
height: 100%;
}
.js-topPushNav.isOpen,
.pushNav_level.isOpen {
right: 0;
}
.closeLevel,
.openLevel {
cursor: pointer;
}
.openLevel,
.closeLevel,
.pushNav a {
padding: 1em 0;
display: block;
text-indent: 20px;
transition: background 0.4s ease-in-out;
}
.openLevel:hover,
.closeLevel:hover,
.pushNav a:hover {
background: #494a50;
}
.hdg {
background-color: #1e1e24;
}
.closeLevel,
closelevel>i {
font-size: 1em;
color: #a5a5a4;
}
.burger {
position: absolute;
top: 24px;
right: 48px;
}
.burger i {
font-size: 3em;
}
.screen {
position: fixed;
background: rgba(0, 0, 0, 0.7);
width: 100%;
height: 0;
top: 0;
bottom: 0;
right: 0;
opacity: 0;
transition: opacity 0.5s ease-in-out;
}
.pushNavIsOpen .screen {
height: 100%;
opacity: 1;
}
.fa {
display: inline;
padding: 5px;
}
.wrapper {
max-width: 625px;
margin: 120px auto;
padding: 0 20px;
color: #fff;
font-family: "Trebuchet MS", Helvetica, sans-serif;
font-weight: 100;
font-size: 1.1em;
line-height: 1.4em;
}
.wrapper a {
color: #20c270;
text-decoration: none;
}
.wrapper button {
background-color: #20c270;
margin: 50px auto;
display: block;
padding: 10px 40px;
border: none;
}
.wrapper button:hover {
background-color: #18a960;
}
.wrapper button a {
color: #fff;
font-size: 2em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<div class="js-menuToggle">Click Me To Open</div>
<div class="pushNav js-topPushNav">
<div class="closeLevel js-closeLevelTop hdg">
<i class="fa fa-close"></i>
</div>
<div id="sidebar-menu-container" class=" meain-container">
<ul id="menu-header-menu" class="header_menu menu-level-1">
<li id="menu-item-6064" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-has-children menu-item-6064 openLevel js-openLevel">
Sub Menu
<div class="pushNav level-2 pushNav_level js-pushNavLevel">< Go Back
<h4>Sub Menu Title</h4>
<h2></h2>
<ul class="sub-menu">
<li id="menu-item-6019" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-6019">Checkout</li>
</ul>
</div>
</li>
<li id="menu-item-6022" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-6022">Sample Page</li>
<li id="menu-item-6063" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-has-children menu-item-6063 openLevel js-openLevel">
Sub Menu Con
<div class="pushNav level-2 pushNav_level js-pushNavLevel">< Go Back
<h4>Sub Menu Title</h4>
<h2></h2>
<ul class="sub-menu">
<li id="menu-item-6025" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-6025">Blog – Masonry Boxed
</li>
</ul>
</div>
</li>
<li id="menu-item-6065" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-6065">3rd menu</li>
</ul>
</div>
</div>
When we click on "GO Back" Button, it does not remove the class (isOpen). Go back button is on Level 2 after clicking the Sub Menu Item.
When using .on() it can be a little finicky with the way event bubble up.
https://jsfiddle.net/Twisty/2xf7rk6g/9
JavaScript
jQuery(document).ready(function($) {
$(".menu-item-has-children").addClass("openLevel js-openLevel");
var $menuTrigger = $('.js-menuToggle');
var $topNav = $('.js-topPushNav');
var $openLevel = $('.js-openLevel');
var $closeLevel = $('.js-closeLevel');
var $closeLevelTop = $('.js-closeLevelTop');
var $navLevel = $('.js-pushNavLevel');
function openPushNav() {
$topNav.addClass('isOpen');
//$('body').addClass('pushNavIsOpen');
}
function closePushNav() {
$topNav.removeClass('isOpen');
$openLevel.siblings().removeClass('isOpen');
//$('body').removeClass('pushNavIsOpen');
}
$menuTrigger.on('click touchstart', function(e) {
e.preventDefault();
if ($topNav.hasClass('isOpen')) {
closePushNav();
} else {
openPushNav();
}
});
$openLevel.on('click touchstart', function() {
console.log("Open");
$(this).find($navLevel).addClass('isOpen');
});
$(".pushNav")
.on('click touchstart', ".js-closeLevel", function() {
console.log("Close");
$(this).parent().removeClass('isOpen');
})
.on('click touchstart', ".js-closeLevelTop", function() {
console.log("cLose Top");
closePushNav();
});
});
This is less ambiguous and the event is being triggered as expected.
replace this function
$closeLevel.on('click touchstart', function() {
$(this).closest($navLevel).removeClass('isOpen');
});
with this
$closeLevel.on('click touchstart', function() {
$(this).closest($navLevel).removeClass('pushNav_level isOpen');
});

Navigation Bar retains class when scrolling despite having a function to remove the class

I'm currently trying to create a navigation bar that sticks when you scroll past. I've gotten to the point where the bar will stick when I scroll past, but when I scroll back to the top the navbar is still sticking. I've been able to troubleshoot to realize that the navbar.offsetTop is being set to 0 when scrolling past, which causes the class "sticky" to never be removed. How can I fix this so that the navbar retains its original offset while being "stuck" to the top of the page?
HTML
<div style="height: 40px">
<ul class="navbar" id="navbar">
<a class="navbutton left" href="about.html"><b>About</b></a>
<a class="navbutton left" href="Games.html"><b>Games</b></a>
<a class="navbutton left" href="#"><b>Btn 1</b></a>
<a class="navbutton left" href="#"><b>Btn 2</b></a>
<a class="navbutton left" href="#"><b>Btn 3</b></a>
<a class="navbutton left" href="#"><b>Btn 4</b></a>
<a class="navbutton" href="#" style="float: right"><b>Btn 5</b></a>
</ul>
</div>
CSS
body {
font-family: Monaco;
background-color: white;
color: #f0dcca;
transition-duration: 0.4s;
}
.navbar {
list-style-type: none;
margin: 0 auto;
padding: 0;
overflow: hidden;
background-color: #333;
}
.navbutton {
display: inline-block;
color: white;
text-align: center;
text-decoration: none;
/* padding: length|initial|inherit; */
padding: 10px 12px;
transition-duration: 0.4s;
}
.navbutton:hover {
background-color: #f0dcca;
color: black;
}
.sticky {
position: fixed;
top:0;
width: 100%;
}
.content {
padding: 16px;
}
.sticky + .content {
padding-top: 60px;
}
Javascript
function stickyNav() {
var navbar = document.getElementById("navbar");
var navTop = navbar.offsetTop;
console.log('navTop = ' + navTop);
console.log('scrollY = ' + window.scrollY);
if (window.scrollY >= navTop) {
navbar.classList.add("sticky");
}
else {
navbar.classList.remove("sticky");
}
}
window.addEventListener('scroll', stickyNav);
The problem is in the function definition of stickyNav.
What I'm seeing is stickyNav function is registered as a callback for the scroll event. But the variables navbar and navTop are inside the function which is assigning values to them every time you scroll. And navTop is getting assigned 0 every time. And the sticky class is never removed.
Try avoiding reassigning values. This worked for me.
<!DOCTYPE html>
<html>
<head>
<style>
body {
font-family: Monaco;
background-color: white;
color: #f0dcca;
transition-duration: 0.4s;
height: 1000px;
}
.navbar {
list-style-type: none;
margin: 0 auto;
padding: 0;
overflow: hidden;
background-color: #333;
}
.navbutton {
display: inline-block;
color: white;
text-align: center;
text-decoration: none;
/* padding: length|initial|inherit; */
padding: 10px 12px;
transition-duration: 0.4s;
}
.navbutton:hover {
background-color: #f0dcca;
color: black;
}
.sticky {
position: fixed;
top: 0;
width: 100%;
}
.content {
padding: 16px;
}
.sticky+.content {
padding-top: 60px;
}
</style>
<script>
function stickyNav() {
var navbar = document.getElementById("navbar");
var navTop = navbar.offsetTop;
window.addEventListener('scroll', function() {
console.log('navTop = ' + navTop);
console.log('scrollY = ' + window.scrollY);
if (window.scrollY >= navTop) {
navbar.classList.add("sticky");
} else {
navbar.classList.remove("sticky");
}
});
}
</script>
</head>
<body onload="stickyNav()">
<h1>dummy content</h1>
<h1>dummy content</h1>
<h1>dummy content</h1>
<div style="height: 40px">
<ul class="navbar" id="navbar">
<a class="navbutton left" href="about.html"><b>About</b></a>
<a class="navbutton left" href="Games.html"><b>Games</b></a>
<a class="navbutton left" href="#"><b>Btn 1</b></a>
<a class="navbutton left" href="#"><b>Btn 2</b></a>
<a class="navbutton left" href="#"><b>Btn 3</b></a>
<a class="navbutton left" href="#"><b>Btn 4</b></a>
<a class="navbutton" href="#" style="float: right"><b>Btn 5</b></a>
</ul>
</div>
</body>
</html>

When click on second dropdown black box should remain visible

In this dropdown nav I'm building if a dropdown is opened and you click to open a second one, the black box should remain visible. At the moment the black box disappears when you click on a second dropdown and reappears after the dropdown is completely opened.
Update
I also noticed the black box shows after a dropdown is open and it should open at the same time.
I hope this makes sense and thank you for your help!
$(document).ready(function() {
$(".click").on("click", function(e) {
var menu = $(this);
toggleDropDown(menu);
});
$(document).on('mouseup',function(e) {
var container = $("nav .top-bar-section ul");
// if the target of the click isn't the container nor a descendant of the container
if (!container.is(e.target) && container.has(e.target).length === 0) {
$('a.active').parent().find('.showup').stop(true, true).slideUp(500, function() {
$(".main-container").removeClass("black-bg");
if ($('a.active').hasClass('active')) {
$('a.active').removeClass('active');
}
});
}
});
});
function toggleDropDown(menu) {
var isActive = $('a.active').length;
$('a.active').parent().find('.showup').stop(true, true).slideUp(500, function() {
$(".main-container").removeClass("black-bg");
if (menu.hasClass('active')) {
menu.removeClass('active');
} else {
$('a.active').removeClass('active');
menu.addClass('active');
menu.parent().find('.showup').stop(true, true).slideDown(500, function() {
$(".main-container").addClass("black-bg");
});
}
});
if (!isActive) {
menu.addClass('active');
menu.parent().find('.showup').stop(true, true).slideDown(500, function() {
$(".main-container").addClass("black-bg");
});
}
}
* {
box-sizing: border-box;
}
body {
margin: 0;
}
.nav-wrapper {
width: 100%;
overflow: hidden;
background: #424242;
}
nav {
width: 1024px;
margin: auto;
overflow: hidden;
background: #424242;
}
.nav-content {
width: 100%;
z-index: 999;
background: #ccc;
}
.top-bar-section {
float: left;
}
.top-bar-section a.active {
background: #f00;
}
.showup {
display: none;
background: #ccc;
position: absolute;
width: 100%;
top: 70px;
left: 0;
z-index: 99;
padding: 30px 15px 30px 20px;
}
p {
font-size: 14px;
line-height: 1.4;
}
li.nav-item {
display: inline-block;
background: #f5f5f5;
}
li.nav-item a {
display: block;
text-decoration: none;
padding: 10px;
}
.main-container {
width: 80%;
height: 400px;
margin: auto;
}
.black-bg {
background: #000;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="nav-wrapper">
<nav>
<div class="top-bar-section">
<ul>
<li class="nav-item">
Nav item 1
<div class="showup">
<div class="nav-content">
<p>
Dropdown for Nav Item 1.
</p>
</div>
</div>
</li>
<li class="nav-item">
Nav item 2
<div class="showup">
<div class="nav-content">
<p>
Dropdown for Nav Item 2.
</p>
</div>
</div>
</li>
<li class="nav-item">
Nav item 3
<div class="showup">
<div class="nav-content">
<p>
Dropdown for Nav Item 3.
</p>
</div>
</div>
</li>
<li class="nav-item">
Nav item 4
<div class="showup">
<div class="nav-content">
<p>
Dropdown for Nav Item 4.
</p>
</div>
</div>
</li>
</ul>
</div>
</nav>
</div>
<div class="main-container">
</div>
If you want black-bg being added once the menu is clicked, then do not remove and add black-bg class on every click event. Simply add it once if the menu have active class and remove it when menu do not active class. If you remove and add class on every click event then black-bg will first disappear and again it will appear. To black-bg at the time drop-down is open then remove $(".main-container").addClass("black-bg"); from callback function of slideDown() because a callback function is executed after the current effect is finished.
$(document).ready(function() {
$(".click").on("click", function(e) {
var menu = $(this);
toggleDropDown(menu);
});
$(document).on('mouseup',function(e) {
var container = $("nav .top-bar-section ul");
// if the target of the click isn't the container nor a descendant of the container
if (!container.is(e.target) && container.has(e.target).length === 0) {
$('a.active').parent().find('.showup').stop(true, true).slideUp(500, function() {
$(".main-container").removeClass("black-bg");
if ($('a.active').hasClass('active')) {
$('a.active').removeClass('active');
}
});
}
});
});
function toggleDropDown(menu) {
var isActive = $('a.active').length;
$('a.active').parent().find('.showup').stop(true, true).slideUp(500, function() {
if (menu.hasClass('active')) {
menu.removeClass('active');
$(".main-container").removeClass("black-bg");
} else {
$('a.active').removeClass('active');
menu.addClass('active');
menu.parent().find('.showup').stop(true, true).slideDown(500, function() {
});
}
});
if (!isActive) {
menu.addClass('active');
menu.parent().find('.showup').stop(true, true).slideDown(500);
$(".main-container").addClass("black-bg");
}
}
* {
box-sizing: border-box;
}
body {
margin: 0;
}
.nav-wrapper {
width: 100%;
overflow: hidden;
background: #424242;
}
nav {
width: 1024px;
margin: auto;
overflow: hidden;
background: #424242;
}
.nav-content {
width: 100%;
z-index: 999;
background: #ccc;
}
.top-bar-section {
float: left;
}
.top-bar-section a.active {
background: #f00;
}
.showup {
display: none;
background: #ccc;
position: absolute;
width: 100%;
top: 70px;
left: 0;
z-index: 99;
padding: 30px 15px 30px 20px;
}
p {
font-size: 14px;
line-height: 1.4;
}
li.nav-item {
display: inline-block;
background: #f5f5f5;
}
li.nav-item a {
display: block;
text-decoration: none;
padding: 10px;
}
.main-container {
width: 80%;
height: 400px;
margin: auto;
}
.black-bg {
background: #000;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="nav-wrapper">
<nav>
<div class="top-bar-section">
<ul>
<li class="nav-item">
Nav item 1
<div class="showup">
<div class="nav-content">
<p>
Dropdown for Nav Item 1.
</p>
</div>
</div>
</li>
<li class="nav-item">
Nav item 2
<div class="showup">
<div class="nav-content">
<p>
Dropdown for Nav Item 2.
</p>
</div>
</div>
</li>
<li class="nav-item">
Nav item 3
<div class="showup">
<div class="nav-content">
<p>
Dropdown for Nav Item 3.
</p>
</div>
</div>
</li>
<li class="nav-item">
Nav item 4
<div class="showup">
<div class="nav-content">
<p>
Dropdown for Nav Item 4.
</p>
</div>
</div>
</li>
</ul>
</div>
</nav>
</div>
<div class="main-container">
</div>
Just move $(".main-container").removeClass("black-bg"); into if (menu.hasClass('active')) {
$(document).ready(function() {
$(".click").on("click", function(e) {
var menu = $(this);
toggleDropDown(menu);
});
$(document).on('mouseup',function(e) {
var container = $("nav .top-bar-section ul");
// if the target of the click isn't the container nor a descendant of the container
if (!container.is(e.target) && container.has(e.target).length === 0) {
$('a.active').parent().find('.showup').stop(true, true).slideUp(500, function() {
$(".main-container").removeClass("black-bg");
if ($('a.active').hasClass('active')) {
$('a.active').removeClass('active');
}
});
}
});
});
function toggleDropDown(menu) {
var isActive = $('a.active').length;
$('a.active').parent().find('.showup').stop(true, true).slideUp(500, function() {
//$(".main-container").removeClass("black-bg"); FROM HERE
if (menu.hasClass('active')) {
menu.removeClass('active');
$(".main-container").removeClass("black-bg"); // TO HERE
} else {
$('a.active').removeClass('active');
menu.addClass('active');
menu.parent().find('.showup').stop(true, true).slideDown(500, function() {
$(".main-container").addClass("black-bg");
});
}
});
if (!isActive) {
menu.addClass('active');
menu.parent().find('.showup').stop(true, true).slideDown(500, function() {
$(".main-container").addClass("black-bg");
});
}
}
* {
box-sizing: border-box;
}
body {
margin: 0;
}
.nav-wrapper {
width: 100%;
overflow: hidden;
background: #424242;
}
nav {
width: 1024px;
margin: auto;
overflow: hidden;
background: #424242;
}
.nav-content {
width: 100%;
z-index: 999;
background: #ccc;
}
.top-bar-section {
float: left;
}
.top-bar-section a.active {
background: #f00;
}
.showup {
display: none;
background: #ccc;
position: absolute;
width: 100%;
top: 70px;
left: 0;
z-index: 99;
padding: 30px 15px 30px 20px;
}
p {
font-size: 14px;
line-height: 1.4;
}
li.nav-item {
display: inline-block;
background: #f5f5f5;
}
li.nav-item a {
display: block;
text-decoration: none;
padding: 10px;
}
.main-container {
width: 80%;
height: 400px;
margin: auto;
}
.black-bg {
background: #000;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="nav-wrapper">
<nav>
<div class="top-bar-section">
<ul>
<li class="nav-item">
Nav item 1
<div class="showup">
<div class="nav-content">
<p>
Dropdown for Nav Item 1.
</p>
</div>
</div>
</li>
<li class="nav-item">
Nav item 2
<div class="showup">
<div class="nav-content">
<p>
Dropdown for Nav Item 2.
</p>
</div>
</div>
</li>
<li class="nav-item">
Nav item 3
<div class="showup">
<div class="nav-content">
<p>
Dropdown for Nav Item 3.
</p>
</div>
</div>
</li>
<li class="nav-item">
Nav item 4
<div class="showup">
<div class="nav-content">
<p>
Dropdown for Nav Item 4.
</p>
</div>
</div>
</li>
</ul>
</div>
</nav>
</div>
<div class="main-container">
</div>
Is this what you are looking for?
$(document).ready(function() {
$(".click").on("click", function(e) {
var menu = $(this);
toggleDropDown(menu);
});
$(document).on('mouseup',function(e) {
var container = $("nav .top-bar-section ul");
// if the target of the click isn't the container nor a descendant of the container
if (!container.is(e.target) && container.has(e.target).length === 0) {
$('a.active').parent().find('.showup').stop(true, true).slideUp(500, function() {
$(".main-container").removeClass("black-bg");
if ($('a.active').hasClass('active')) {
$('a.active').removeClass('active');
}
});
}
});
});
function toggleDropDown(menu) {
var isActive = $('a.active').length;
$('a.active').parent().find('.showup').stop(true, true).slideUp(500, function() {
if (menu.hasClass('active')) {
menu.removeClass('active');
} else {
$('a.active').removeClass('active');
menu.addClass('active');
menu.parent().find('.showup').stop(true, true).slideDown(500, function() {
$(".main-container").addClass("black-bg");
});
}
});
if (!isActive) {
menu.addClass('active');
menu.parent().find('.showup').stop(true, true).slideDown(500, function() {
$(".main-container").addClass("black-bg");
});
}
The black box will remain there in this case. What you were previously doing was that you were removing black-box explicitly.

Migrating from vue1 to vue2

I'm trying to migrate this sample dropdown menu from vue1 to vue2.
http://vuejsexamples.com/vue-dropdown-menu/
I've changed the ready method to mounted, removed the json filter and rewrote the for loop in close because it caused errors in the console.
Right now there aren't any errors while I run this, but still, the dropdown doesn't work properly - it does not close when I click outside of the menu.
Can anybody help with migrating this?
new Vue({
el: '#menu',
mounted: function()
{
var self = this
window.addEventListener('click', function(e){
if (! e.target.parentNode.classList.contains('menu__link--toggle'))
{
self.close()
}
}, false)
},
data: {
dropDowns: {
ranking: { open: false}
}
},
methods: {
toggle: function(dropdownName)
{
this.dropDowns[dropdownName].open = !this.dropDowns[dropdownName].open;
},
close: function()
{
for (var i = 0; i < this.dropDowns.length; i++) {
this.dropDowns[dd].open = false;
}
}
}
})
body {
margin: 2rem 0;
}
ul {
list-style: none;
}
.menu {
display: flex;
}
.menu__item {
position: relative;
padding-right: 3rem;
}
.menu__link {
text-transform: uppercase;
}
.menu__icon {
margin: 0 !important;
}
.open .dropdown-menu {
display: block;
}
.dropdown-menu {
font-size: 0.9rem;
position: absolute;
min-width: 130px;
top: 2.2rem;
display: none;
box-shadow: 0px 6px 12px rgba(0, 0, 0, 0.2);
border-radius: 4px;
}
.dropdown-menu__item:first-child .dropdown-menu__link {
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.dropdown-menu__item:last-child .dropdown-menu__link {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
}
.dropdown-menu__link {
display: block;
padding: 1rem;
color: blue;
background-color: #fafafa;
}
.dropdown-menu__link:hover {
color: green;
background-color: #ccc;
}
<script src="https://unpkg.com/vue#2.4.2/dist/vue.min.js"></script>
<div id="menu"class="row">
<ul class="menu">
<li class="menu__item">
<a class="menu__link" href="#">Home</a>
</li>
<li class="menu__item menu__item--dropdown" v-on:click="toggle('ranking')" v-bind:class="{'open' : dropDowns.ranking.open}">
<a class="menu__link menu__link--toggle" href="#">
<span>Rangliste</span>
<i class="menu__icon fa fa-angle-down"></i>
</a>
<ul class="dropdown-menu">
<li class="dropdown-menu__item">
<a class="dropdown-menu__link" href="#">Aktuelle Runde</a>
</li>
<li class="dropdown-menu__item">
<a class="dropdown-menu__link" href="#">Siegerliste</a>
</li>
</ul>
</li>
<li class="menu__item">
<a class="menu__link" href="#">Belegungskalender</a>
</li>
</ul>
<pre>{{ dropDowns }}</pre>
</div>
To fix this I had to make two changes:
First is in the event listener. When I tested it, I got an exception since e.target.parentNode.classList was undefined (when element has no classes). The error is when trying to access function contains of undefined.
So I made a variable and checked for truthy value on it. If it is undefined, it wouldn't contain the desired class, so you could close the menu.
The next and main problem was this part of the code:
for (var i = 0; i < this.dropDowns.length; i++) {
this.DropDowns is an Object, not an Array. Objects don't have length, so you can't use dropDowns.length.
Instead, you'd extract the object keys and loop them, then access the values using the key name.
I used Array.prototype.forEach instead of the loop for a more functional approach, and this is the final result:
new Vue({
el: '#menu',
mounted: function() {
var self = this
window.addEventListener('click', function(e) {
var classList = e.target.parentNode.classList;
//Check if classList is a valid value. If classList is undefined, contains would throw an error.
if (!classList || !classList.contains('menu__link--toggle')) {
self.close()
}
}, false)
},
data: {
dropDowns: {
ranking: {
open: false
}
}
},
methods: {
toggle: function(dropdownName) {
this.dropDowns[dropdownName].open = !this.dropDowns[dropdownName].open;
},
close: function() {
var dropDowns = this.dropDowns;
Object.keys(dropDowns).forEach(function(key, index) {
dropDowns[key].open = false;
});
}
}
})
body {
margin: 2rem 0;
}
ul {
list-style: none;
}
.menu {
display: flex;
}
.menu__item {
position: relative;
padding-right: 3rem;
}
.menu__link {
text-transform: uppercase;
}
.menu__icon {
margin: 0 !important;
}
.open .dropdown-menu {
display: block;
}
.dropdown-menu {
font-size: 0.9rem;
position: absolute;
min-width: 130px;
top: 2.2rem;
display: none;
box-shadow: 0px 6px 12px rgba(0, 0, 0, 0.2);
border-radius: 4px;
}
.dropdown-menu__item:first-child .dropdown-menu__link {
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.dropdown-menu__item:last-child .dropdown-menu__link {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
}
.dropdown-menu__link {
display: block;
padding: 1rem;
color: blue;
background-color: #fafafa;
}
.dropdown-menu__link:hover {
color: green;
background-color: #ccc;
}
<script src="https://unpkg.com/vue#2.4.2/dist/vue.min.js"></script>
<div id="menu" class="row">
<ul class="menu">
<li class="menu__item">
<a class="menu__link" href="#">Home</a>
</li>
<li class="menu__item menu__item--dropdown" v-on:click="toggle('ranking')" v-bind:class="{'open' : dropDowns.ranking.open}">
<a class="menu__link menu__link--toggle" href="#">
<span>Rangliste</span>
<i class="menu__icon fa fa-angle-down"></i>
</a>
<ul class="dropdown-menu">
<li class="dropdown-menu__item">
<a class="dropdown-menu__link" href="#">Aktuelle Runde</a>
</li>
<li class="dropdown-menu__item">
<a class="dropdown-menu__link" href="#">Siegerliste</a>
</li>
</ul>
</li>
<li class="menu__item">
<a class="menu__link" href="#">Belegungskalender</a>
</li>
</ul>
<pre>{{ dropDowns }}</pre>
</div>
I'd recommend that you turn this menu into a component. As it is right now, that is non-reusable instance, and you'll surely need to use this dropdown in more places in your project.

Added $(box).bind("click touchstart", function() { to my logic but not successful on iPad

I have added '$(box).bind("click touchstart", function() {' to my script but still not running on iPad. I'm trying to get this script to work across all devices so that user can select the box nunbers 1-8 and recommendation with a border will show below. any idea why this is breaking on iPad? is there a better way to structure this?
var numbers = document.querySelectorAll(".clicked");
var letters = document.querySelectorAll(".border");
numbers.forEach(function(box, index) {
$(box).bind("click touchstart", function() {
letters.forEach(function(box) {
box.classList.remove("showBorder");
});
var info = document.getElementsByClassName('box-tip')[0];
if (index > 2) {
info.style.left = 11 + ((index - 3) * 45) + 'px';
}
else {
info.style.left = 0 + 'px';
}
info.style.visibility = 'visible';
letters[index].classList.add("showBorder");
});
$(document).on("click", '.clicked', function(){
$('.clicked').removeClass('selected');
$(this).addClass('selected');
});
});
.list-box li {display: inline-block;list-style-type: none;padding: 1em;background:red;}
.list-box {margin:15px auto;padding:0;}
.box-sleeve li {display: inline-block;list-style-type: none;padding: 1em;background:red;}
.box-sleeve {margin:15px auto;padding:0;}
.showBorder { outline: 1px dashed #233354; outline-offset:1px;}
.box-tip {
display:inline;
margin: auto;
position: relative;
visibility: hidden;
padding-left:10px;
}
.numberCircle {
border-radius: 90%;
font-size: 12px;
border: 2px solid #000;
color: #fff;
background: #000;
padding: 0 4px;
}
.numberCircle span {
text-align: center;
display: block;
}
li.selected {color:#fff;background-color:#000;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span>Test some logic one</span>
<ul class="list-box">
<li class="clicked">1</li>
<li class="clicked">2</li>
<li class="clicked">3</li>
<li class="clicked">4</li>
<li class="clicked">5</li>
<li class="clicked">6</li>
<li class="clicked">7</li>
<li class="clicked">8</li>
</ul>
<div>
<span>Test some logic two</span>
</div>
<div class="box-tip">
<span>Regular length for your collar size</span>
<span class="numberCircle">?</span>
</div>
<ul class="box-sleeve">
<li class="border">a</li>
<li class="border">b</li>
<li class="border">c</li>
<li class="border">d</li>
<li class="border">e</li>
<li class="border">f</li>
<li class="border">g</li>
<li class="border">h</li>
</ul>
Try the following tweaks:
Detect & store user agent first ua = navigator.userAgent;
Then match it with iPad's and store it event = (ua.match(/iPad/i)) ? "touchstart" : "click";.
Use as $(box).bind(event, function() { //do stuff });.
Working on iPad Air.
var numbers = document.querySelectorAll(".clicked"),
letters = document.querySelectorAll(".border"),
ua = navigator.userAgent,
event = (ua.match(/iPad/i)) ? "touchstart" : "click";
numbers.forEach(function(box, index) {
$(box).bind(event, function() {
letters.forEach(function(box) {
box.classList.remove("showBorder");
});
var info = document.getElementsByClassName('box-tip')[0];
if (index > 2) {
info.style.left = 11 + ((index - 3) * 45) + 'px';
} else {
info.style.left = 0 + 'px';
}
info.style.visibility = 'visible';
letters[index].classList.add("showBorder");
});
$(document).on("click", '.clicked', function() {
$('.clicked').removeClass('selected');
$(this).addClass('selected');
});
});
.list-box li {
display: inline-block;
list-style-type: none;
padding: 1em;
background: red;
}
.list-box {
margin: 15px auto;
padding: 0;
}
.box-sleeve li {
display: inline-block;
list-style-type: none;
padding: 1em;
background: red;
}
.box-sleeve {
margin: 15px auto;
padding: 0;
}
.showBorder {
outline: 1px dashed #233354;
outline-offset: 1px;
}
.box-tip {
display: inline;
margin: auto;
position: relative;
visibility: hidden;
padding-left: 10px;
}
.numberCircle {
border-radius: 90%;
font-size: 12px;
border: 2px solid #000;
color: #fff;
background: #000;
padding: 0 4px;
}
.numberCircle span {
text-align: center;
display: block;
}
li.selected {
color: #fff;
background-color: #000;
}
/* add these to prevent text move below 768 viewports */
#media (max-width: 768px) {
.box-tip span {
position: fixed;
left: 10px;
}
.box-tip span.numberCircle {
position: fixed;
left: 236px;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span>Test some logic one</span>
<ul class="list-box">
<li class="clicked">1</li>
<li class="clicked">2</li>
<li class="clicked">3</li>
<li class="clicked">4</li>
<li class="clicked">5</li>
<li class="clicked">6</li>
<li class="clicked">7</li>
<li class="clicked">8</li>
</ul>
<div>
<span>Test some logic two</span>
</div>
<div class="box-tip">
<span>Regular length for your collar size</span>
<span class="numberCircle">?</span>
</div>
<ul class="box-sleeve">
<li class="border">a</li>
<li class="border">b</li>
<li class="border">c</li>
<li class="border">d</li>
<li class="border">e</li>
<li class="border">f</li>
<li class="border">g</li>
<li class="border">h</li>
</ul>

Categories

Resources