jQuery menu with sliding highlighter - javascript

I would like to do something like this: http://dreamapp.de/sites/portfolio/
At the moment this is done with an additional list item which has position: absolute. So I simply move the list item to the same place as the active list item. But this solution will only work if the content is not dynamically centered. So beside it's an ugly but working, solution, there must be a better one, right?
I thought that I've seen similar things on other website, but I couldn't find anything like this. So how could I do this better?
HTML:
<nav id="menu2" class="menu">
<ul>
<li class="marker"></li>
<li class="nav1">Home</li>
<li class="nav2">HTML/CSS</li>
<li class="nav3">JavaScript</li>
<li class="nav4">Resources</li>
<li class="nav5">Tutorials</li>
<li class="nav6">About</li>
</ul>
</nav>
jQuery:
$(".nav1 a").click(function() {
$(".marker").stop().animate({left:'8px'},200, function() {
$(".marker").stop().animate({display:'show'}, 200);
});
});
$(".nav2 a").click(function() {
$(".marker").stop().animate({'left':'118px'},200, function() {
$(".marker").stop().animate({display:'show'}, 200);
});
});
.
.
.
CSS:
.menu ul li.marker {
width: 110px;
height: 45px;
background-color: #42ff2e;
display:none;
position: absolute;
}
.menu {
width: 660px;
height: 45px;
display: block;
}
.menu ul {
list-style: none;
padding: 0;
margin: 0;
}
.menu ul li {
float: left;
overflow: hidden;
position: relative;
text-align: center;
line-height: 45px;
}
.menu ul li a {
position: relative;
display: block;
width: 110px;
height: 45px;
font-family: Arial;
font-size: 11px;
font-weight: bold;
letter-spacing: 1px;
text-transform: uppercase;
text-decoration: none;
cursor: pointer;
}

Think you could use this for fixed navigation item width http://jsfiddle.net/UqUBr/
(for variable navigation item width - http://jsfiddle.net/bdmjC/)
JS:
var navigation = $('nav'),
items = navigation.find('.item'),
itemWidth = 110
$(".item a").click(function() {
var item = $(this).parent()
$(".marker").stop().animate({left:items.index(item)*itemWidth},200, function() {
$(".marker").stop().animate({display:'show'}, 200);
});
});
HTML:
<nav id="menu2" class="menu">
<ul>
<li class="marker"></li>
<li class="item">Home</li>
<li class="item">HTML/CSS</li>
<li class="item">JavaScript</li>
<li class="item">Resources</li>
<li class="item">Tutorials</li>
<li class="item">About</li>
</ul>
</nav>
CSS:
nav#menu2 {
display: block;
margin: auto;
width: 660px;
position: relative;
}

Related

How to Target a Child Node Without Knowing Its Position Using Only Vanilla Javascript

I have created a mockup navbar, and using the mouseenter event, have been able to display the submenus inside their parent li element.
This has been achieved using the children property and locating the position of the submenu, but was wondering how this is achieved without knowing so, or if there is a better idiomatic approach.
The submenus and list items have the same class names.
Below, you will find my code:
'use strict';
var dropdown = document.getElementsByClassName('dropdown');
document.addEventListener('DOMContentLoaded', function(e) {
e.preventDefault();
Array.from(dropdown).forEach(function(node) {
node.addEventListener('mouseenter', function(event) {
event.target.children[1].style.display = 'block';
});
});
Array.from(dropdown).forEach(function(node) {
node.addEventListener('mouseleave', function(event) {
event.target.children[1].style.display = 'none';
});
});
});
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
header {
height: 20vh;
width: 100vw;
background-color: #000;
}
.navbar {
position: relative;
width: 100%;
margin: 0;
padding: 0;
float: left;
}
.navbar > li {
display: inline-block;
list-style-type: none;
position: relative;
float: left;
margin: 0;
padding: 0;
}
.navbar li a {
text-align: center;
padding-left: 30px;
white-space: nowrap;
}
.menu {
display: none;
position: absolute;
top: 100%;
left: 0;
padding: 0;
}
.menu > li {
list-style-type: none;
padding: 10px 0;
float: none;
}
li {
width: 100px;
}
a {
font-family: Helvetica Neue;
text-decoration: none;
color: #fff;
display: block;
}
<header>
<nav>
<ul class="navbar">
<li class="dropdown">I drop down
<ul class="menu">
<li>1</li>
<li>2</li>
</ul>
</li>
<li class="dropdown">So do I
<ul class="menu">
<li>3</li>
<li>4</li>
</ul>
</li>
<li>No effect</li>
<li>Same here</li>
</ul>
</nav>
</header>
One option is to add ids (or classes) to the target elements, then add some attribute to the controlling element to express which target element it controls. Without knowledge of the DOM relationship between the two nodes, you're going to have to add some information to connect them.
I was going to use a data- attribute, but then I remembered that by using aria-controls you could add some a11y too.
'use strict';
var dropdown = document.getElementsByClassName('dropdown');
document.addEventListener('DOMContentLoaded', function(e) {
e.preventDefault();
Array.from(dropdown).forEach(function(node) {
node.addEventListener('mouseenter', function(event) {
var menuId = event.srcElement.getAttribute('aria-controls');
document.getElementById(menuId).style.display = 'block';
});
node.addEventListener('mouseleave', function(event) {
var menuId = event.srcElement.getAttribute('aria-controls');
document.getElementById(menuId).style.display = 'none';
});
});
});
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
header {
height: 20vh;
width: 100vw;
background-color: #000;
}
.navbar {
position: relative;
width: 100%;
margin: 0;
padding: 0;
float: left;
}
.navbar > li {
display: inline-block;
list-style-type: none;
position: relative;
float: left;
margin: 0;
padding: 0;
}
.navbar li a {
text-align: center;
padding-left: 30px;
white-space: nowrap;
}
.menu {
display: none;
position: absolute;
top: 100%;
left: 0;
padding: 0;
}
.menu > li {
list-style-type: none;
padding: 10px 0;
float: none;
}
li {
width: 100px;
}
a {
font-family: Helvetica Neue;
text-decoration: none;
color: #fff;
display: block;
}
<header>
<nav>
<ul class="navbar">
<li class="dropdown" aria-controls="menu1">I drop down
<ul class="menu" id="menu1">
<li>1</li>
<li>2</li>
</ul>
</li>
<li class="dropdown" aria-controls="menu2">So do I
<ul class="menu" id="menu2">
<li>3</li>
<li>4</li>
</ul>
</li>
<li>No effect</li>
<li>Same here</li>
</ul>
</nav>
</header>
Another way (one I prefer) would be to use the structure of the HTML to find the nodes. To do this the ul.menu must be a descendant of the currentTarget. Then you can use querySelector to locate it.
'use strict';
var dropdown = document.getElementsByClassName('dropdown');
document.addEventListener('DOMContentLoaded', function(e) {
e.preventDefault();
Array.from(dropdown).forEach(function(node) {
node.addEventListener('mouseenter', function(event) {
this.querySelector(':scope > ul.menu').style.display = 'block';
});
node.addEventListener('mouseleave', function(event) {
this.querySelector(':scope > ul.menu').style.display = 'none';
});
});
});
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
header {
height: 20vh;
width: 100vw;
background-color: #000;
}
.navbar {
position: relative;
width: 100%;
margin: 0;
padding: 0;
float: left;
}
.navbar > li {
display: inline-block;
list-style-type: none;
position: relative;
float: left;
margin: 0;
padding: 0;
}
.navbar li a {
text-align: center;
padding-left: 30px;
white-space: nowrap;
}
.menu {
display: none;
position: absolute;
top: 100%;
left: 0;
padding: 0;
}
.menu > li {
list-style-type: none;
padding: 10px 0;
float: none;
}
li {
width: 100px;
}
a {
font-family: Helvetica Neue;
text-decoration: none;
color: #fff;
display: block;
}
<header>
<nav>
<ul class="navbar">
<li class="dropdown">I drop down
<ul class="menu">
<li>1</li>
<li>2</li>
</ul>
</li>
<li class="dropdown">So do I
<ul class="menu">
<li>3</li>
<li>4</li>
</ul>
</li>
<li>No effect</li>
<li>Same here</li>
</ul>
</nav>
</header>

CSS horizontal submenu

I'm working on the navigation bar for a website and currently the main menu is complete. However, the "Services" and "Products" buttons need to each have their own sub-menu. The sub-menu should normally be hidden from view and appears when the user mouse-overs on the respective button.
Here is a fiddle with the desired result. Obviously, I'd rather not use any javascript if possible.
The idea I had initially was to have sub-menu have position: absolute with a z-index value lower than that of the main-menu, so that it can slide underneath the main-menu. However, doing so messes up with the width if I give it width: 100% and since my site is responsive, I avoid static widths.
I also tried doing with relative positioning, but that doesn't work either.
Another thing I don't like with that approach is that the markup for the main menu and sub-menu get split. Is it possible to get the above result, but with this markup?
<nav>
<ul class="nav">
<li role="presentation" class="active">Home</li>
<li role="presentation">Services
<ul>
<li role="presentation">Link 1
<li role="presentation">Link 2
</ul>
</li>
<li role="presentation">Products
<ul>
<li role="presentation">Link 3
<li role="presentation">Link 4
</ul>
</li>
<li role="presentation">About</li>
<li role="presentation">Contact</li>
</ul>
</nav>
Here is my code:
CSS
body {
font-size: 0;
}
.bodyframe {
display: inline-block;
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.6);
}
.div_container {
max-width: 1460px;
width: 100%;
display: inline-block;
margin: 0 auto;
}
header {
width: 100%;
height: 49px;
}
.nav {
display: block;
position: relative;
list-style: none;
background: #304770;
z-index: 10;
}
.nav li {
display: inline-block;
background-color: #304770;
margin: 0 5px;
}
.nav li a {
padding: 12px 15px;
font-size: 18px;
color: #EFEFEF;
display: block;
}
.nav li.active a {
color: orange;
}
.nav li.active a:before {
width: 100%;
}
.nav li a:hover {
background-color: #304770;
color: orange;
transition: color 0.25s;
}
.nav li a:before {
content: "";
display: block;
position: absolute;
left: 0;
bottom: 0;
height: 3px;
width: 0;
background-color: orange;
-webkit-transition: width 0.2s;
transition: width 0.2s;
}
.nav li:nth-last-of-type(1) a:after {
display: none;
}
.nav li a:hover:before {
width: 100%;
}
.nav li a:after {
content: "";
display: block;
position: absolute;
right: -8px;
top: 21px;
height: 6px;
width: 6px;
background: #ffffff;
opacity: .5;
}
.subnav {
list-style-type: none;
display: block;
position: relative;
top: -49px;
margin: 0;
z-index: 1;
background-color: #ccc;
-webkit-transition: top 0.2s;
}
.subnav li {
display: inline-block;
background-color: #ccc;
margin: 0 5px;
}
.subnav li a {
padding: 8px 10px;
font-size: 14px;
color: #EFEFEF;
display: block;
}
HTML
<div class="bodyframe div_container">
<header>
<nav>
<ul class="nav">
<li role="presentation" class="active">Home</li>
<li role="presentation">Services</li>
<li role="presentation">Products</li>
<li role="presentation">About</li>
<li role="presentation">Contact</li>
</ul>
</nav>
<ul class="subnav">
<li>Test</li>
<li>1243</li>
</ul>
</header>
</div>
If you only need the submenu to mimic the one in the example, without using jQuery, using the second chunk of HTML with the CSS you supplied you could do:
nav:hover~ul {
top: 0px;
}
This shows the next ul element, in this case the subnav, whenever the nav is hovered over ("~" selector means select the ul element preceded by nav:hover).
However, if you want to do something more dynamic... id suggest just using JS/jQuery as well

Why can't I center my nav bar?

I am using jQuery to create a drop down nav menu and it works fine, but I can not get it to center on my page.
I have tried using align="center" in the div "menu", but that did not work. I then tried aligning it using the css for the div and the lis and uls, but that also did not work.
Here is my code
jQuery:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js" type="text/javascript">
</script>
<link rel="stylesheet" type="text/css" href="style.css">
<script type="text/javascript">
$(document).ready(function () {
$("#menu li").hover(function () {
$(this).children(":hidden").slideDown();
}, function(){
$(this).parent().find("ul").slideUp();
});
});
</script>
HTML:
<div id="menu">
<ul>
<li>Home
</li>
<li>test
<ul>
<li>drop1</li>
<li>drop2</li>
<li>drop3</li>
</ul>
</li>
<li>test
<ul>
<li>drop1</li>
<li>drop2</li>
</ul>
</li>
</ul>
</div>
CSS:
#menu {
height: 30px;
background-color: #26C7FF;
margin-left: 0;
margin-right: 0;
}
#menu li li:hover {
background-color: yellow;
cursor: pointer;
}
#menu ul, #menu li {
list-style-type: none;
padding: 0;
margin: 0;
}
#menu li {
float: left;
width: 120px;
list-style-type: none;
line-height: 30px;
text-align: center;
}
#menu li ul {
position: absolute;
background-color: #26C7FF;
display: none;
}
#menu li li {
float: none;
padding: 2px;
}
#menu a {
color: #000;
text-decoration: none;
}
Use display:inline-block; instead of floating the li elements and then use text-align:center; for the container ul.
#menu > ul > li {
display: inline-block;
}
Here is a working example:
http://codepen.io/taneleero/pen/MwojLV
Wrap it in a container that has a max-width of whatever width you'd like your menu to be:
.container {
max-width: 600px; // Or whatever
width: 100%;
margin: 0 auto;
}
#menu {
height: 30px;
background: #26C7FF;
}
<div class="container">
<div id="menu">
</div>
Please check this code. I think this is what you wanted.
< script type = "text/javascript" >
$(document).ready(function() {
$("#menu li").hover(function() {
$(this).children(":hidden").slideDown();
}, function() {
$(this).parent().find("ul").slideUp();
});
}); < /script>
#menu {
height: 30px;
background-color: #26C7FF;
margin-left: 0;
margin-right: 0;
}
#menu li li:hover {
background-color: yellow;
cursor: pointer;
}
#menu ul,
#menu li {
list-style-type: none;
padding: 0;
margin: 0;
text-align: center;
position: relative;
}
#menu li {
float: none;
width: 120px;
list-style-type: none;
line-height: 30px;
text-align: center;
display: inline-block;
}
#menu li ul {
position: absolute;
background-color: #26C7FF;
display: none;
left: 0;
right: 0;
}
#menu li li {
float: none;
padding: 2px;
}
#menu a {
color: #000;
text-decoration: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js" type="text/javascript">
</script>
<link rel="stylesheet" type="text/css" href="style.css">
<div id="menu">
<ul>
<li>Home
</li>
<li>test
<ul>
<li>drop1
</li>
<li>drop2
</li>
<li>drop3
</li>
</ul>
</li>
<li>test
<ul>
<li>drop1
</li>
<li>drop2
</li>
</ul>
</li>
</ul>
</div>
this is the JSFIDDLE link
Since you're setting your ordered list tags at a fixed width, the following css should fix your problem.
#menu ul {
width: 369px;
margin: 0 auto;
}
Here's a working jsfiddle.

Closing Menu on Outside Click on page with iFrames

I'm trying to use the trick outlined on this answer to close a navigation menu on a page I've designed. However, the majority of the space on the page is taken up by an iframe that loads articles stored on the same server as the page containing the iframe.
If I click on any of the elements on the parent page, the menu closes as it should. However, clicking on any of the space within the iframe does not close the menu.
I'm assuming this is because the parent page does not capture events happening inside of the iframe. Like I've said, both pages are stored on the same server, so how can I capture the click to close my menu when the user clicks within the iframe?
HTML:
<div id="menucontainer">
<nav id="mobilemenu">
<ul>
<li><span class="menutrigger">☰ Menu</span></li>
</ul>
</nav>
<nav id="fullmenu">
<ul>
<li>Menu Item 1</li>
<li>Menu Item 2</li>
<li>Menu Item 3</li>
<li>Menu Item 4</li>
</ul>
</nav>
</div>
<div id="frame">
<iframe name="content" id="content" src="intro.html"></iframe>
</div>
JQuery:
$(document).ready(function() {
$("a[target='content']").click(function() {
if ($("#mobilemenu").css("display") == "block" ){
$('#fullmenu').hide();
}
});
$('html').click(function() {
$('#fullmenu').hide();
});
$('#menucontainer').click(function(event){
event.stopPropagation();
});
$('.menutrigger').click(function() {
$('#fullmenu').toggle();
});
});
CSS: (Added because it occurred to me it might be affecting things)
html, body { height: 100%; width: 100%; }
nav, #frame { position: absolute; right: 0; left: 0; padding: 0; margin: 0; width: 100%; }
#content { height: 100%; width: 100%; }
#frame { top: 38px; bottom: 14px; }
nav { width: 100%; z-index: 100; }
#fullmenu { display: none; position: absolute; top: 38px; width: 100%; }
#mobilemenu { display: block; height: 38px; top: 0; background: #333; }
.menutrigger { font-size: 20px; font-weight: bold; padding: 1px 8px; color: #FFF; cursor: pointer; }
#frame { top: 38px; bottom: 14px; }
nav ul { position: relative; width: 100%; background: #333; list-style: none; display: inline-block; padding: 0; }
nav ul:after { clear: both; }
nav ul li { height: 29px; float: none; min-width: 110px; font-size: 14px; padding: 4px 4px; font-family: 'Roboto Condensed', sans-serif; }
nav ul li a { display: block; padding: 5px; color: #FFF; text-decoration: none; }
nav ul li:hover { background: #666; display: inline-block; height: 29px; }
nav ul li:hover a { color: #FFF; }
The contents().click() does not work when the iframe is on a different domain. You'll get a cross-domain access error attempting to make the contents() call.
Working Demo
Instead of
$('html').click(function() {
$('#fullmenu').hide();
});
use
$('html').click(function() {
$('#fullmenu').hide();
});
//get iframe contents and bind click on them.
$('#content').contents().click(function(){
$('#fullmenu').hide();
});
For iframes with different domains, my solution is to send an event from the iframe with Post Message API and listen to it on the parent. This way the parent can perform any action when the iframe is clicked.
In parent page:
window.addEventListener("message", (event) => {
// DO SOMETHING
}, false);
In iframe:
window.addEventListener("click", (event) => {
parent.postMessage("click", "*");
}, false);

how to align list dropdown

I have a dropdown in menu. But it drops not the way I need.
but I need it to be aligned as a menu item. like in here:
could it be done simply with css?
html
<li class="dropdown">
<a data-toggle="dropdown" class="dropdown-toggle" href="#">Account<b class="caret" style="float: right;"></b></a>
<ul class="dropdown-menu">
<li>Account Info</li>
<li>Billing Settings</li>
<li><a href="{% url 'dev_logout' %}" >Sign out</a></li>
</ul>
</li>
css:
.dropdown-menu {
background-color: black;
min-width: 100px;
width: 160px;
}
.dropdown-menu > li {
align: center;
}
.dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus {
color: #cfeffd;
background-color: #333333;
background-image: none;
filter: none;
}
.dropdown-toggle {
min-width: 100px;
width: 125px;
}
.caret {
marging-left: 30px;
}
You can make it by setting position:relative to ".dropdown" and position: absolute to ".dropdown-menu" and his width to 100%.
li {
padding: 10px 20px;
}
.dropdown {
position: relative;
display: block;
background-color: #ff0;
width: 120px;
}
.dropdown-menu {
position: absolute;
top: 100%;
left: 0;
z-index: 1000;
display: block;
width: 100%;
padding: 0;
margin: 0;
list-style: none;
background-color: gray;
}
Here is a working example :
http://jsfiddle.net/5CB4Q/3/
Assign the DropDownList a CssClass like DDLStyle.
select.DDLStyle{text-align:middle;}
This works in most modern browsers, but not in IE.

Categories

Resources