How to know the logic of multilevel menu event bubbling - javascript

I'm trying to understand the logic happening in my basic multilevel menu click event. I understood what happening on clicking on "About" menu in the navigation. And it works as per my expecation of code. But when i click on "Profile" menu (Submenu of "About" menu), JS makes it's sublevel menu "display:none". I tried to think in the aspect of even bubbling. But eventhough the bubbling happens here, it should not be working like this. Actually for me, its really complicated to understand how JS works here. It would be a Great Help if anyone can explain with a simple and understandable way. Thank You Very Much in Advance!!!
let menus = document.querySelectorAll(".main-navigation ul li a");
menus.forEach((item) => {
if (item.parentElement.querySelector("ul")) {
item.parentElement.classList.add("has-submenu");
}
});
let submenu = document.querySelectorAll(".has-submenu");
submenu.forEach((item) => {
item.addEventListener("click", (e) => {
e.preventDefault();
let ul = e.target.parentElement.querySelector("ul");
let cs = window.getComputedStyle(ul).display;
if (cs === "none") {
ul.style.cssText = "display:block";
}
else {
ul.style.cssText = "display:none";
}
});
});
.main-navigation ul {list-style:none;margin:0;padding:0;font-family:arial;}
.main-navigation ul li {padding:.35rem;background:#f9f9f9;}
.main-navigation ul li ul {padding-left:1rem;display:none;}
.main-navigation ul li a {display:block;text-decoration:none;}
<div class="main-navigation">
<ul>
<li>Home</li>
<li>About +
<ul>
<li>Profile +
<ul>
<li>History</li>
<li>Management</li>
</ul>
</li>
<li>Vision</li>
<li>Mission</li>
</ul>
</li>
<li>Services +
<ul>
<li>Web Design</li>
<li>Web Development</li>
</ul>
</li>
<li>Contact</li>
</ul>
</div>

Solution
If you add a console.log inside your click handler you will notice that the event for the nested item is called twice.
You probably knew that it could happen and you used preventDefault.
However, preventDefault is for the browser's default effects (for example, it prevents your page to refresh as you put an href attribute) but in your case the double behaviour is from your own custom listener.
This means, you need to add stopPropagation that prevents further propagation of the current event in the capturing and bubbling phases.
Working Demo
let menus = document.querySelectorAll(".main-navigation ul li a");
menus.forEach((item) => {
if (item.parentElement.querySelector("ul")) {
item.parentElement.classList.add("has-submenu");
}
});
let submenu = document.querySelectorAll(".has-submenu");
submenu.forEach((item) => {
item.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
let ul = e.target.parentElement.querySelector("ul");
let cs = window.getComputedStyle(ul).display;
if (cs === "none") {
ul.style.cssText = "display:block";
} else {
ul.style.cssText = "display:none";
}
});
});
.main-navigation ul {
list-style: none;
margin: 0;
padding: 0;
font-family: arial;
}
.main-navigation ul li {
padding: .35rem;
background: #f9f9f9;
}
.main-navigation ul li ul {
padding-left: 1rem;
display: none;
}
.main-navigation ul li a {
display: block;
text-decoration: none;
}
<div class="main-navigation">
<ul>
<li>Home</li>
<li>About +
<ul>
<li>Profile +
<ul>
<li>History</li>
<li>Management</li>
</ul>
</li>
<li>Vision</li>
<li>Mission</li>
</ul>
</li>
<li>Services +
<ul>
<li>Web Design</li>
<li>Web Development</li>
</ul>
</li>
<li>Contact</li>
</ul>
</div>

Related

Close jQuery menu on mouseLeave

I am building a small drop-down container which appears when You hover on top of a menu item. When I hover on top of the menu item (e.g. Tools) the dropdown appears, I can move my mouse inside, but when the cursor leaves the dropdown menu, it does not go away. How am I able to achieve this?
I only managed to make it dissapear when you click somewhere outside of it.
Here is a Fiddle.
var dropdown = $('.nav-dropdown');
dropdown.hide();
$('#dropdownToggle').hover(function(e) {
e.preventDefault();
dropdown.show(200);
dropdown.addClass('active');
$(window).click(function() {
dropdown.slideUp();
});
e.stopPropagation();
});
SOLUTION by anima_incognita:
var dropdown = $('.nav-dropdown');
dropdown.hide();
$('#dropdownToggle').hover(function(e) {
e.preventDefault();
dropdown.show(200);
dropdown.addClass('active');
$(window).click(function() {
dropdown.slideUp();
});
$(".nav-dropdown").on('mouseleave',function(){
dropdown.slideUp();
});
e.stopPropagation();
});
here is edit in your code worked fine with me...added methods
var dropdown = $('.nav-dropdown');
dropdown.hide();
$('#dropdownToggle').mouseenter(function(e) {
e.preventDefault();
dropdown.show(200);
dropdown.addClass('active');
$(window).click(function() {
dropdown.slideUp();
});
$('#dropdownToggle').mouseleave(function(e) {
dropdown.slideUp();
});
e.stopPropagation();
});
Add this to end of your code:
$(".nav-dropdown").on('mouseleave',function(){
dropdown.hide();
});
Update your JS:
var dropdown = $('.nav-dropdown');
dropdown.hide();
$('#dropdownToggle').hover(function(e) {
e.preventDefault();
dropdown.show(200);
dropdown.addClass('active');
$(window).click(function() {
dropdown.slideUp();
});
e.stopPropagation();
});
$(".nav-dropdown").on('mouseleave', function() {
dropdown.slideUp('fast');
});
.nav-list {
.nav-list-item {
float: left;
list-style: none;
padding: 2rem;
background: tomato;
font-family: 'Helvetica', 'Arial', sans-serif;
a {
text-decoration: none;
text-transform: uppercase;
font-weight: bold;
color: #fff;
}
.nav-dropdown {
position: absolute;
background: turquoise;
padding: 2rem;
li {
margin-bottom: 2rem;
}
}
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="nav-list">
<li class="nav-list-item">
Services
</li>
<li class="nav-list-item dropdown-wrapper">
<a href="#" id="dropdownToggle" class="nav-link tools">Tools
</a>
<!-- dropdown -->
<ul class="nav-dropdown active" style="display: block;">
<li class="nav-dropdown-item">
Buyer Cost Sheet
</li>
<li class="nav-dropdown-item">
Seller Net Sheet
</li>
<li class="nav-dropdown-item">
Mortage Calculator
</li>
<li class="nav-dropdown-item">
Title Fees
</li>
<li class="nav-dropdown-item">
Refi Calculator
</li>
<li class="nav-dropdown-item">
Real Estate Forms
</li>
</ul>
</li>
<li class="nav-list-item">
Buyers & Sellers
</li>
</ul>
As you are using the hover function, the hover function specifies two function to trigger mouseenter and mouseleave event
You have defined only the mouseenter function and not defined the mouseleave function. So below is the updated JS code:
$('#dropdownToggle').hover(function(e) {
e.preventDefault();
dropdown.show(200);
dropdown.addClass('active');
e.stopPropagation();
}, function(e){
e.preventDefault();
dropdown.slideUp();;
dropdown.removeClass('active');
});

Dropdown Menu Closes On Hover (Error)

Whhenever I hover over the menu it works fine. But, when I try to get to the submenu links and children, the menu closes
/*----------------------------------------------------
/* Dropdown menu
/* ------------------------------------------------- */
jQuery(document).ready(function($) {
function mtsDropdownMenu() {
var wWidth = $(window).width();
if (wWidth > 865) {
$('#navigation ul.sub-menu, #navigation ul.children').hide();
var timer;
var delay = 100;
$('#navigation li').hover(
function() {
var $this = $(this);
timer = setTimeout(function() {
$this.children('ul.sub-menu, ul.children').slideDown('fast');
}, delay);
},
function() {
$(this).children('ul.sub-menu, ul.children').hide();
clearTimeout(timer);
}
);
} else {
$('#navigation li').unbind('hover');
$('#navigation li.active > ul.sub-menu, #navigation li.active > ul.children').show();
}
}
mtsDropdownMenu();
$(window).resize(function() {
mtsDropdownMenu();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<li id="menu-item-513" class="menu-item "><i class="fa fa-calculator"></i> OFFERTE AANVRAGEN
<ul class="sub-menu">
<li id="menu-item-1146" class="menu-item">Zonnepanelen installatie (Belgiƫ)
</li>
<li id="menu-item-1144" class="menu-item">Zonnepanelen reinigen (Belgiƫ)
</li>
<li id="menu-item-1145" class="menu-item">Zonnepanelen installatie (Nederland)
</li>
</ul>
</li>
The code you posted works just fine; here's a plnkr to prove it: https://plnkr.co/edit/IFaueUhKE3J1K9vY1NkQ?p=preview
(simply wrapped a <div id='navigation'><ul> round the top li).
If you still loose the hover over the child-elements, it's caused by something you're not showing in the original question. E.g. adding this css:
li.menu-item {
position: relative;
top: 50px;
left: 300px;
}
would make it difficult to reach the child items because you, briefly, lose the parent-hover while moving to the child.

jquery dynamic filter list

I'm trying to make a filter list on keypress. For example if I write in input "It", the elements that doesn't match this input value are hidden. I'm not sure if the idea I have with code below does the job. Any tips will be highly appreciated!
$('ul li ul li').addClass('displayNone');
var geInputValue = $('input').val();
var getInputLength = $('input').length;
function sortDynamically(){
$('input').on('keypress', function(){
for(var i=0; i < getInputLength; i++){
if(getInputValue === $('li').text){
// remove everything that doesnt match input value
$('li').siblings().addClass('displayNone');
}
else{
$('li').siblings().removeClass('displayNone');
});
}
sortDynamically();
ul, li{
list-style-type: none;
margin: 0;
padding: 0;
}
.displayNone{
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" />
<ul class="list">
<li>Item</li>
<li>Product
<ul>
<li>Bike</li>
</ul>
</li>
<li>About</li>
</ul>
This code filters based on what you type. If there is nothing in the text input then everything is shown.
$('input').on('keypress keyup', function(){
var value = $(this).val().toLowerCase();
if (value != '') {
$('.list > li').each(function () {
if ($(this).text().toLowerCase().indexOf(value) > -1) {
$(this).removeClass('displayNone');
} else {
$(this).addClass('displayNone');
}
});
} else {
$('.list > li').removeClass('displayNone');
}
});
ul, li{
list-style-type: none;
margin: 0;
padding: 0;
}
.displayNone{
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" />
<ul class="list">
<li>Item</li>
<li>Product
<ul>
<li>Bike</li>
</ul>
</li>
<li>About</li>
</ul>
jQuery provides filters and javascript implements toLowerCase() and includes() methods that you can use to improve your code
<body>
<style>
.displayNone
{
display: none;
}
</style>
<input type="text" id="input-filter"/>
<ul class="list">
<li>Item</li>
<li>Product
<ul>
<li>Bike</li>
</ul>
</li>
<li>About</li>
</ul>
<script>
var items = $('ul.list li');
$('#input-filter').on('input', function ($event)
{
items.addClass('displayNone').filter(function (item)
{
return $(this).text().toLowerCase().includes($($event.target).val().toLowerCase());
}).removeClass('displayNone');
});
</script>
</body>

Change background color on anchor in listitem when clicked

I have menu constructed by ul li with anchor tags in each. Css is applied to the anchor
and anchor:hover however I want the selected item to show that it is selected be changing the background a different color. anchor:active does not work.
I am trying javascript but not yet successful. Can this be soley done through css? I have looked at so many examples, but none actually worked right.
JAVASCRIPT
<script type="text/javascript">
function ChangeColor(obj) {
var li = document.getElementById(obj.id);
li.style.background = "#bfcbd6";
}
</script>
HTML
<div id="navigation">
<ul>
<li><a onclick="changecolor(this);" href="Default.aspx">Home</a></li>
<li><a onclick="changecolor(this);" href="View.aspx">View</a></li>
<li><a onclick="changecolor(this);" href="About.aspx">About</a></li>
</ul>
</div>
CSS - Simplified
#navigation ul {
list-style-type: none;
}
#navigation li
{
float: left;
}
#navigation a
{
background-color: #465c71;
}
#navigation a:hover
{
background-color: #bfcbd6;
}
you don't need to get id again for handling element. obj references the actual element.
<script type="text/javascript">
function ChangeColor(obj) {
obj.style.backgroundColor = "#bfcbd6";
}
</script>
Edit: And javaScript is case sensitive, so you should check your function names.
Here is a jsFiddle Demo
I have found a way to use JavaScript to solve this situation. This works for having MasterPage. Changing the id of the selected tab will then reference the css for that
selected tab only while setting the other tabs id's to null.
HTML
<div id="navbar">
<div id="holder">
<ul id="menulist">
<li><a onclick="SelectedTab(this);" href="#" id="onlink" >Home</a></li>
<li><a onclick="SelectedTab(this);" href="#" id="" >Products</a></li>
<li><a onclick="SelectedTab(this);" href="#" id="">Services</a></li>
<li><a onclick="SelectedTab(this);" href="#" id="">Gallery</a></li>
<li><a onclick="SelectedTab(this);" href="#" id="" >Contact</a></li>
</ul>
</div>
</div>
JavaScript
function SelectedTab(sender) {
var aElements = sender.parentNode.parentNode.getElementsByTagName("a");
var aElementsLength = aElements.length;
var index;
for (var i = 0; i < aElementsLength; i++)
{
if (aElements[i] == sender) //this condition is never true
{
index = i;
aElements[i].id="onlink"
} else {
aElements[i].id=""
}
}
}
Css for changing the background color after tab has been selected
#holder ul li a#onlink
{
background: #FFF;
color: #000;
border-bottom: 1px solid #FFF;
}

drag and drop from one column to another using jquery

I need an example of dragging an item from one column and dropping it into another using jquery
Are there any such examples out there?
You can do this with jquery sortable: http://jqueryui.com/demos/sortable/#connect-lists
Here I have done complete bins using jquery UI sortable. i think it should be helpful to you.
Demo: http://codebins.com/bin/4ldqp9g
HTML:
<div class="demo">
<ul id="sortable1" class="connectedSortable">
<li class="ui-state-default">
Item 1
</li>
<li class="ui-state-default">
Item 2
</li>
<li class="ui-state-default">
Item 3
</li>
<li class="ui-state-default">
Item 4
</li>
<li class="ui-state-default">
Item 5
</li>
</ul>
<ul id="sortable2" class="connectedSortable">
<li class="ui-state-highlight">
Item 1
</li>
<li class="ui-state-highlight">
Item 2
</li>
<li class="ui-state-highlight">
Item 3
</li>
<li class="ui-state-highlight">
Item 4
</li>
<li class="ui-state-highlight">
Item 5
</li>
</ul>
</div>
<!-- End demo -->
<div class="demo-description">
<p>
Sort items from one list into another and vice versa, by passing a selector
into the
<code>
connectWith
</code>
option. The simplest way to do this is to
group all related lists with a CSS class, and then pass that class into the
sortable function (i.e.,
<code>
connectWith: '.myclass'
</code>
).
</p>
</div>
CSS:
#sortable1, #sortable2
{
list-style-type: none;
margin: 0;
padding: 0 0 2.5em;
float: left;
margin-right: 10px;
}
#sortable1 li, #sortable2 li
{
margin: 0 5px 5px 5px;
padding: 5px;
font-size: 1.2em;
width: 120px;
overflow:visible;
display:block;
}
JQuery:
$(function() {
var itemclone, idx;
$("#sortable1, #sortable2").sortable({
start: function(event, ui) {
//create clone of current seletected li
itemclone = $(ui.item).clone();
//get current li index position in list
idx = $(ui.item).index();
//If first li then prepend clone on first position
if (idx == 0) {
itemclone.css('opacity', '0.5');
$(this).prepend(itemclone);
}
//Else Append Clone on its original position
else {
itemclone.css('opacity', '0.7');
$(this).find("li:eq(" + (idx - 1) + ")").after(itemclone);
}
},
change: function(event, ui) {
//While Change event set clone position as relative
$(this).find("li:eq(" + idx + ")").css('position', 'relative');
},
stop: function() {
//Once Finish Sort, remove Clone Li from current list
$(this).find("li:eq(" + idx + ")").remove();
},
connectWith: ".connectedSortable"
}).disableSelection();
});
Demo: http://codebins.com/bin/4ldqp9g

Categories