Javascript three-level collapsible menu - javascript

I am a complete amateur to coding; I want to have a three-level collapsible menu, which will expand when clicked. I have found some existing code to use, but it is designed for a two-level menu only. I cannot work out how to alter the script so that the third level will open when clicked too. Any ideas would be most appreciated!
Here is the existing script:
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<!-- <script type="text/javascript" language="javascript" charset="utf-8" src="nav.js"></script> -->
<!--[if lt IE 9]>
<script type="text/javascript" src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<script>
$(document).ready(function(){
$("#nav > li > a").on("click", function(e){
if($(this).parent().has("ul")) {
e.preventDefault();
}
if(!$(this).hasClass("open")) {
// hide any open menus and remove all other classes
$("#nav li ul").slideUp(350);
$("#nav li a").removeClass("open");
// open our new menu and add the open class
$(this).next("ul").slideDown(350);
$(this).addClass("open");
}
else if($(this).hasClass("open")) {
$(this).removeClass("open");
$(this).next("ul").slideUp(350);
}
});
});
</script>

You can achieve the same outcome with relatively little code compared to what you have there.
Using css to handle the display is arguably easier to manage, with javascript simply toggling a class that you can target in your css. It also means that you can have as many levels as you wish.
It works through the use of the next sibling selector in css +
$('.menu-trigger').click(function(){
$(this).toggleClass('active')
// if you really wish to keep the jquery animation you can do this
// $(this).next('ul').slideToggle()
})
/* you can use the sibling selector '+' to target the list */
.menu-trigger + ul {
display: none;
}
.menu-trigger.active + ul {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li>
<a class="menu-trigger">parent</a>
<ul>
<li>
<a class="menu-trigger">child</a>
<ul>
<li>
<a class="menu-trigger">grandchild</a>
</li>
</ul>
</li>
</ul>
</li>
</ul>

I wrote this, it is simple but kind of illustrates what you want to achieve.
$('li').hover(function(){
$(this).children('ul:eq(0)').show();
}, function(){
$(this).children('ul:eq(0)').hide();
});
http://codepen.io/clarenswd/pen/GjBaWp

Related

Javascript/jQuery onclick not working

i made a test.html document to test a script. Somehow it is not working and i can't get why nothing is happening. The Script is in -tags and wrapped with -tag and the css also has it´s -tags. Why shouldn't it work? Here is the code
<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js">
</script>
<script>
$('#menu li a').on('click', function() {
$('li a.current').removeClass('current');
$(this).addClass('current');
});
</script>
<style>
.current {
text-decoration: underline;
}
ul {
list-style-type: none;
}
a {
text-decoration: none;
}
</style>
</head>
<body>
<div id="menu">
<ul>
<li><a id="about-link" class="current" href="#">ABOUT</a></li>
<li><a id="events-link" href="#">EVENTS</a></li>
<li><a id="reviews-link" href="#">REVIEWS</a></li>
<li><a id="contact-link" href="#">CONTACT</a></li>
</ul>
</div>
</body>
</html>
Because your code for binding event handler is executed before the elements is present in DOM, the event is not bound on the element.
To solve this problem, you can either
Wrap your code in ready() callback, so that your code will be executed when DOM is finished loading
Move your script to the end of <body>, so all the elements are present in DOM and you can bind the events on it
Code:
$(document).ready(function () {
$('#menu li a').on('click', function () {
$('li a.current').removeClass('current');
$(this).addClass('current');
});
});
EDIT
You can also use load(not recommended) event callback to bind the event, but this will wait for all the resources to load completely.
If you want to use Vanilla Javascript, you can use DOMContentLoaded event on document.
you are executing the code before the DOM content is loaded. Place your code in a ready block.
You are executing the JavaScript before the <body> tag loads, which contains your <li> elements.
To solve this problem, you have three choices :
1.You can insert the JavaScript code you have written inside a $(document).ready() callback. (see http://www.w3schools.com/jquery/event_ready.asp for more information)
Code :
$(document).ready(function () {
$('#menu li a').on('click', function () {
$('li a.current').removeClass('current');
$(this).addClass('current');
});
});
2.You can use the JavaScript's built in window.onload function. (see https://thechamplord.wordpress.com/2014/07/04/using-javascript-window-onload-event-properly/ for more information)
Code:
window.onload = function () {
$('#menu li a').on('click', function () {
$('li a.current').removeClass('current');
$(this).addClass('current');
});
}
3.Place your <script> tag before </body> tag in your page.
Code:
<head>
<style>
.current {
text-decoration: underline;
}
ul {
list-style-type: none;
}
a {
text-decoration: none;
}
</style>
</head>
<body>
<div id="menu">
<ul>
<li><a id="about-link" class="current" href="#">ABOUT</a></li>
<li><a id="events-link" href="#">EVENTS</a></li>
<li><a id="reviews-link" href="#">REVIEWS</a></li>
<li><a id="contact-link" href="#">CONTACT</a></li>
</ul>
</div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js">
</script>
<script>
$('#menu li a').on('click', function() {
$('li a.current').removeClass('current');
$(this).addClass('current');
});
</script>
</body>
Note: Using $(document).ready() is a better option than using window.onload. (see window.onload vs $(document).ready() for more information)
Your code is fine. Just wrap it in DOM ready and you are good to go.
$(document).ready(function(){
$('#menu li a').on('click', function() {
$('li a.current').removeClass('current');
$(this).addClass('current');
});
})

Failure to edit div on nav click event

So I typed up most of the pertinent information into the code itself
I would love a explanation of whats happening and why, especially between the multiple outcomes of .text .html and not returning what i want
<DOCTYPE! Html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<style>
p{color: white;
background: black;}
</style>
<script>
$('document').ready(function() {
$(document).on('click', '.nav', function() {
document.getElementById('text').innerHTML=$(this).html;
});
});
</script>
</head>
<body>
This is a test html to trouble shoot the functionality of the script, the above script runs and does what it needs to in other aspects but when trying to add in the ability to edit a divs text based on the nav clicked on theres a error.
when using ".text" in the script i get <p> function (a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)} </p> in my div,when i use ".html" in my script i get <p> function (a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fb,""):void 0;if(!("string"!=typeof a||mb.test(a)||!k.htmlSerialize&&gb.test(a)||!k.leadingWhitespace&&hb.test(a)||rb[(jb.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ib,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ub(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)} </p>
<ul id="nav">
<li class="nav"> This is the text that should be placed in the div </li>
</ul>
<div id="text"> this text should be changed </div>
</body>
</html>
The jQuery method .html() is a function you need to call it / use (). Or go vanilla and just do this.innerHTML.
That means:
$(document).on('click', '.nav', function() {
document.getElementById('text').innerHTML = this.innerHTML;
});
Example in the snippet:
$('document').ready(function() {
$(document).on('click', '.nav', function() {
document.getElementById('text').innerHTML=this.innerHTML;
});
});
p{color: white;
background: black;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
This is a test html to trouble shoot the functionality of the script, the above script runs and does what it needs to in other aspects but when trying to add in the ability to edit a divs text based on the nav clicked on theres a error.
when using ".text" in the script i get <p> function (a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)} </p> in my div,when i use ".html" in my script i get <p> function (a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fb,""):void 0;if(!("string"!=typeof a||mb.test(a)||!k.htmlSerialize&&gb.test(a)||!k.leadingWhitespace&&hb.test(a)||rb[(jb.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ib,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ub(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)} </p>
<ul id="nav">
<li class="nav"> This is the text that should be placed in the div </li>
</ul>
<div id="text"> this text should be changed </div>
Something like this:
$(document).ready(function () {
$(".nav").on('click', function() {
$("#text").html( $(this).html() ); // Getting too nested here
}
}

Hide jquery menu after click function

I have a simple function to show a dropdown menu.
This is a responsive menu, it only works with defined sizes on my media queries, that's why I want to hide submenu after click.
I want to click on one of the links and then this dropdown menu hides. I am aware of .hide() but I can't get it to work.
Any help?
I want to keep this code simple & clean.
HTML
<nav>
<ul class="navigation">
<img src="img/menu.png" alt="mobile" width="50" height="50"/>
<li class="n1">Principal</li>
<li class="n2">Serviços</li>
<li class="n3">Equipa</li>
<li class="n4">Contactos</li>
</ul>
<span>Euclides Style | 965648044</span>
</nav>
Javascript
$("nav").click(function () {
$(".navigation").toggleClass('open');
});
UPDATE
I used a simple solution:
$(".navigation a").click(function () {
$(".navigation").removeClass('open');
});
Thanks everyone for your help!
hide() is working.
Try:
$("nav li").click(function () {
$(".navigation").hide();
});
DEMO
$("a").on("click", function (e) {
e.preventDefault();
$(".navigation").fadeOut();
});
or you can try .hide() even .fadeOut();
Assuming that open class has display: none or display: block attribute (it depends on the starting state) will work fine.
Alternatively you can use toggle function.
Demo: http://jsfiddle.net/IrvinDominin/uQg2X/
$("nav").click(function () {
$(".navigation").toggleClass('open');
});
and you should add some css
.navigation{ display:'none'}
.navigation.open{display:'block'}
First: it is not allowed to have a img-tag directly inside ul-tag. This is an example of a working code with some modifications:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Hiding/showing navigation</title>
<style>
.closed{
display: none;
}
</style>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script>
$(function(){
$("nav").click(function(){
$(".navigation").toggleClass('closed');
});
});
</script>
</head>
<body>
<nav>
<h1>The nav</h1>
<ul class="navigation">
<li class="n1">Principal</li>
<li class="n2">Serviços</li>
<li class="n3">Equipa</li>
<li class="n4">Contactos</li>
</ul>
<span>Euclides Style | 965648044</span>
</nav>
</body>
</html>
The hide(); solution will totally hide the menu and will not show again the next time, the toggle solution will toggle all the other menus on the page, so here below is my solution, this effect will apply to all your dropdown menus on a page.
$(document).on("click",".dropdown-menu li a", function(){
jQuery(this).parent('li').parent('ul').parent('div').removeClass('open');
});

I want to re format my css and jquery code for dropdown menu

I have following code working fine for dropdown menu
but i need to modify css and jquery.
Need to increase the main box and decrease the 1st icon row
but when i try to decrease the width it is decrease whole box width
please guide.
FIDDLE LINK
http://fiddle.jshell.net/6pLPn/
code:
<html>
<head>
<meta charset="utf-8">
<title></title>
<meta name="viewport" content="width=device-width,initial-scale=1.0">
</head>
<body>
<div class="header">
<div class="logo">
JQUERY DEMO
</div>
<ul class="nav">
<li class="nav-link">
</li>
</ul>
</div>
<!-- Clickable Nav -->
<div class="click-nav">
<ul class="no-js">
<li>
<a class="clicker"><img src="img/i-1.png" alt="Icon">Profile</a>
<ul>
<li><img src="img/i-2.png" alt="Icon">Dashboard</li>
<li><img src="img/i-3.png" alt="Icon">Settings</li>
<li><img src="img/i-4.png" alt="Icon">Privacy</li>
<li><img src="img/i-5.png" alt="Icon">Help</li>
<li><img src="img/i-6.png" alt="Icon">Sign out</li>
</ul>
</li>
</ul>
</div>
<!-- /Clickable Nav -->
<!-- jQuery -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
$(function() {
// Clickable Dropdown
$('.click-nav > ul').toggleClass('no-js js');
$('.click-nav .js ul').hide();
$('.click-nav .js').click(function(e) {
$('.click-nav .js ul').slideToggle(200);
$('.clicker').toggleClass('active');
e.stopPropagation();
});
$(document).click(function() {
if ($('.click-nav .js ul').is(':visible')) {
$('.click-nav .js ul', this).slideUp();
$('.clicker').removeClass('active');
}
});
});
</script>
</body>
</html>
The width of everything here is set on the container div <div class="click-nav">.
That has a width of 200px defined in the CSS.
The <a> tags inside the <li> items are set to display: block causing them to fill the width of that parent container.
To make the 'first icon row' smaller, you can target it with the .clicker class.
.clicker {
width: 100px; /* or use a percentage like 50% */
}
And to make the 'main box' bigger you can add a width larger than 200px to .click-nav ul li ul which is already targeted in the CSS. E.g.
.click-nav ul li ul {
width: 250px; /* or use a percentage like 125% */
}
Updated your fiddle here.
Is that what you meant? It was hard to understand exactly what the problem was.

Regarding Combining Two Small Javascript Codes

Basically the question is self explanitory. I can't figure out how to combine small snippets of javascript rather than having two separate files.
The two codes are :
$(function(){
$('.navbar li').each(function(){
if(window.location.href.indexOf($(this).find('a:first').attr('href'))>-1)
{
$(this).addClass('active').siblings().removeClass('active');
}
});
});
$(function(){
$('.dropdown-menu li').each(function(){
if(window.location.href.indexOf($(this).find('a:first').attr('href'))>-1)
{
$(this).removeClass('active')
}
});
});
Another question, is the first $(function() necessary?
Thanks for you help.
You can do this way:
Create a function and call it under it. Or, you do this:
$(function(){
$('.navbar li').each(function(){
if(window.location.href.indexOf($(this).find('a:first').attr('href'))>-1)
{
$(this).addClass('active').siblings().removeClass('active');
}
});
$('.dropdown-menu li').each(function(){
if(window.location.href.indexOf($(this).find('a:first').attr('href'))>-1)
{
$(this).removeClass('active')
}
});
});
Some notes:
Those two functions are different.
You don't need the two function starters, $(function(){}) twice.
You can make it like this:
$(function(){
$('.navbar li, .dropdown-menu li').each(function(){
if(window.location.href.indexOf($(this).find('a:first').attr('href'))>-1)
{
if($(this).parents(".navbar").length > 0)//this is to figure out if we are working with LI inside .navbar or not.
$(this).addClass('active').siblings().removeClass('active');
else
$(this).removeClass('active')
}
});
});
Functions are almost equal, except that you need to detect if you are working with LI from .navbar or not to select corect removeClass code.
Also, in your code first $(function() is necessary. Otherwise code inside it could be started before document is loaded. But you may put both pieces of code inside one $(function()
Full working sample, this will make the first LI of first UL red, and remove the red from the second ul LI. Your two functions are now combined.
<!DOCTYPE HTML>
<html lang="en-US">
<head>
<meta charset="UTF-8">
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<title></title>
<style type="text/css">
.active {background-color:red}
ul.dropdown-menu>li.active {background-color:red}
</style>
</head>
<body>
</body>
<ul class="navbar">
<li>first</li>
<li>old</li>
<li>new</li>
<li>4</li>
</ul>
<ul class="dropdown-menu">
<li class="active">first</li>
<li>old</li>
<li>new</li>
<li>4</li>
</ul>
<script type="text/javascript">
$(function(){
$('.navbar li').each(function(){
if(window.location.href.indexOf($(this).find('a:first').attr('href'))>-1)
{
$(this).addClass('active').siblings().removeClass('active');
}
});
$('.dropdown-menu li').each(function(){
if(window.location.href.indexOf($(this).find('a:first').attr('href'))>-1)
{
$(this).removeClass('active')
}
});
});
</script>
</html>

Categories

Resources