Search bar with dropdown list - javascript

I'm trying to make a search bar. No matter what I type in search box it shows the "Dropdown" as result and it doesn't show the items inside that list when I search. What am I doing wrong?
https://jsfiddle.net/5xh86fkn/
var dropdown = document.getElementsByClassName("dropdown-btn");
var i;
for (i = 0; i < dropdown.length; i++) {
dropdown[i].addEventListener("click", function() {
this.classList.toggle("active");
var dropdownContent = this.nextElementSibling;
if (dropdownContent.style.display === "block") {
dropdownContent.style.display = "none";
} else {
dropdownContent.style.display = "block";
}
});
}
function myFunction() {
var input, filter, ul, li, a, i;
input = document.getElementById("mySearch");
filter = input.value.toUpperCase();
ul = document.getElementById("myMenu");
li = ul.getElementsByTagName("li");
for (i = 0; i < li.length; i++) {
a = li[i].getElementsByTagName("a")[0];
if (a.innerHTML.toUpperCase().indexOf(filter) > -1) {
li[i].style.display = "";
} else {
li[i].style.display = "none";
}
}
}
<div class="sidenav">
<input type="text" id="mySearch" onkeyup="myFunction()" placeholder="Search.." title="Type in a category">
<ul id="myMenu">
<li>About</li>
<li>Services</li>
<li>Clients</li>
<li>Contact</li>
<button class="dropdown-btn">Dropdown
<i class="fa fa-caret-down"></i>
</button>
<div class="dropdown-container">
<li>Link 1</li>
<li>Link 2</li>
<li>Link 3</li>
</div>
<li>Search</li>
</ul>
</div>

This is a working demo of your attempt of filtering the menu items in real time by typing suggestions on the input text.
The main problem was using the correct strategy to fetch the menu items from dom. The element you wanted to partecipate in the filtering, wasn't a <LI> element.
Plus the list you embedded inside the Dropdown button wasn't included in a ol container and was breaking the correct behaviour.
I slightly refactored your html and focused on the single js function in charge of filtering the list according to the input typed:
function f(filter){
menuItems = document.querySelectorAll('#myMenu > li');
for(const menuItem of menuItems){
const textContent = menuItem.textContent.trim().toUpperCase();
//console.log(`"${textContent}" "${filter}" "${textContent.indexOf(filter)}"`);
if (textContent.indexOf(filter.toUpperCase()) > -1) {
menuItem.classList.remove('hidden');
}else{
menuItem.classList.add('hidden');
}
}
}
.hidden{
display: none;
}
body {
font-family: "Lato", sans-serif;
}
/* Fixed sidenav, full height */
.sidenav {
height: 100%;
width: 200px;
position: fixed;
z-index: 1;
top: 0;
left: 0;
background-color: #111;
overflow-x: hidden;
padding-top: 20px;
}
/* Style the sidenav links and the dropdown button */
.sidenav a, .dropdown-btn {
padding: 6px 8px 6px 16px;
text-decoration: none;
font-size: 20px;
color: #818181;
display: block;
border: none;
background: none;
width: 100%;
text-align: left;
cursor: pointer;
outline: none;
}
/* On mouse-over */
.sidenav a:hover, .dropdown-btn:hover {
color: #f1f1f1;
}
/* Main content */
.main {
margin-left: 200px; /* Same as the width of the sidenav */
font-size: 20px; /* Increased text to enable scrolling */
padding: 0px 10px;
}
/* Add an active class to the active dropdown button */
.active {
background-color: green;
color: white;
}
/* Dropdown container (hidden by default). Optional: add a lighter background color and some left padding to change the design of the dropdown content */
.dropdown-container {
display: none;
background-color: #262626;
padding-left: 8px;
}
/* Optional: Style the caret down icon */
.fa-caret-down {
float: right;
padding-right: 8px;
}
/* Some media queries for responsiveness */
#media screen and (max-height: 450px) {
.sidenav {padding-top: 15px;}
.sidenav a {font-size: 18px;}
}
<div class="sidenav">
<input type="text" id="mySearch" onkeyup="f(this.value)" placeholder="Search.." title="Type in a category">
<ul id="myMenu">
<li>About</li>
<li>Services</li>
<li>Clients</li>
<li>Contact</li>
<li>
<button class="dropdown-btn">
Dropdown
<i class="fa fa-caret-down"></i>
</button>
<div class="dropdown-container">
<ol>
<li>Link 1</li>
<li>Link 2</li>
<li>Link 3</li>
</ol>
</div>
</li>
<li>
Search
</li>
</ul>
</div>

For simplicity I removed code not related to the Search question.
I would suggest a slightly different approach here.
Use the textContent of the nodes so that you would not match the HTML other than that.
Leverage the data attribute and some CSS to make the code smaller by setting them to a true/false string and show/hide based upon that.
Use a descender selector to find only a that is directly inside an li by using li>a - I would strongly suggest classes instead of element selectors however for the "searchable" elements.
Note I hide the parent li by a toggle of the dataset value
I removed the JavaScript from the HTML as a best practice
Note this code can be further reduced but I left it verbose for clarity.
document.getElementById("mySearch").addEventListener('keyup', (event) => {
const findMe = event.target.value.toUpperCase();
const searchable = document.querySelectorAll('li>a');
searchable.forEach(function(searchItem) {
searchItem.parentElement.dataset.isfound = searchItem.textContent.toUpperCase().includes(findMe) ? "true" : "false";
});
});
li[data-isfound="true"] {
background-color: yellow;
}
li[data-isfound="false"] {
display: none;
}
<div class="sidenav">
<input type="text" id="mySearch" placeholder="Search.." title="Type in a category">
<ul id="myMenu">
<li>About</li>
<li>Services</li>
<li>Clients</li>
<li>Contact</li>
<li><button class="dropdown-btn">Dropdown
<i class="fa fa-caret-down"></i>
</button></li>
<ul class="dropdown-container">
<li>Link 1</li>
<li>Link 2</li>
<li>Link 3</li>
</ul>
<li>Search</li>
</ul>
</div>

Related

Read value from html menu?

How can I read the selected value of this menu, as if it were a dropdown list?
For a regular dropdown list like below, I use something like this to read the value:
<select id="ddl">
<option value="In" selected="selected">In</option>
<option value="Out">Out</option>
<option value="Ratio">Ratio</option>
</select>
With javascript I read the selected value:
var dropdown = document.getElementById("ddl");
var InOrOut = dropdown.options[dropdown.selectedIndex].value;
Is it possible to use something like this to read the selected value of the menu below:
function FillAll()
{
alert('I will read value');
// This is where menu value is read.
alert('Value read is');
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<style type="text/css">
#wrapper {
width: 100%;
border: 1px solid black;
overflow: hidden; /* will contain if #first is longer than #second */
}
#first {
width: 400px;
float:left; /* add this */
border: 1px solid red;
}
#second {
border: 1px solid green;
text-align: right;
overflow: hidden; /* if you don't want #second to wrap below #first */
}
.bs-example{
margin: 20px;
}
.text
{
font-size: 15pt;
font-family: Helvetica;
color: #3d718b;
}
hr{
margin: 60px 0;
}
</style>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<div style="float:left;">
<ul class="nav nav-pills">
<li class="text" class="dropdown">
</b>
<ul class="dropdown-menu">
<li>Value1</li>
<li>Value2</li>
<li>Value3</li>
</ul>
</li>
</li>
</ul>
</div>
<button id="ButtonSearch" onclick="FillAll()">GO</button>
Since one can't do much to change the design of the regular dropdown list, I was thinking of using another approach, and use an html menu instead of the ddl.
Something I didn't mention is that the code to read the value would go inside the click event of a button that already exists. I didn't mention it before because I didn't know the click event would be used to read the value.
You can use getAttribute to scrape a value off of each li:
$('.dropdown-menu li').click( e => {
console.log(e.target.getAttribute('value'));
});
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<style type="text/css">
#wrapper {
width: 100%;
border: 1px solid black;
overflow: hidden; /* will contain if #first is longer than #second */
}
#first {
width: 400px;
float:left; /* add this */
border: 1px solid red;
}
#second {
border: 1px solid green;
text-align: right;
overflow: hidden; /* if you don't want #second to wrap below #first */
}
.bs-example{
margin: 20px;
}
.text
{
font-size: 15pt;
font-family: Helvetica;
color: #3d718b;
}
hr{
margin: 60px 0;
}
</style>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<div style="float:left;">
<ul class="nav nav-pills">
<li class="text" class="dropdown">
</b>
<ul class="dropdown-menu">
<li value="1">Value1</li>
<li value="2">Value2</li>
<li value="3">Value3</li>
</ul>
</li>
</li>
</ul>
</div>
This looks to do what you need using either jQuery or native JS.
<script>
// with jquery
$('.dropdown-menu li').click(function(el) {
console.log(el.target.textContent)
})
// native
const list = document.querySelectorAll('.dropdown-menu li');
list.forEach(el => el.addEventListener('click', function listClick() {
console.log(el.textContent);
}));
</script>
The short answer is, yes. The following code, following your example, uses an event handler which is triggered when one of the menu items are clicked. I've also included a working jsfiddle example below.
HTML
<ul class="nav nav-pills">
<li class="text" class="dropdown">
</b>
<ul class="dropdown-menu">
<li>Value 1</li>
<li>Value 2</li>
<li>Value 3</li>
</ul>
</li>
</ul>
<div class="col-xs-12 text-danger" id="result"></div>
Javascript / jQuery
$(document).on('click', '.dropdown-menu li a', function() {
var r = $('#result');
r.empty().append('You have clicked <strong>'+$(this).text()+'</strong> in the drop-down menu.');
});
https://jsfiddle.net/Xonos/z9jfa86r/
UPDATE: I have added another answer which shows a secondary example where you can select/de-select a menu item and then click a button to determine which of the menu items are selected. The JSFiddle link is below.
HTML
<div class="col-xs-12">
<ul class="nav nav-pills">
<li class="text" class="dropdown">
</b>
<ul id="myDropdown" class="dropdown-menu">
<li>Value 1</li>
<li>Value 2</li>
<li>Value 3</li>
</ul>
</li>
</ul>
</div>
<div class="col-xs-12 text-red" id="result"> </div>
<br>
<div class="col-xs-12">
<button id="checkBtn" class="btn btn-sm btn-success pull-right">Check Selected Menu Item</button>
</div>
<div class="col-xs-12 text-orange" id="check"> </div>
CSS
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
button {
background: #0084ff;
border: none;
border-radius: 5px;
padding: 8px 14px;
font-size: 15px;
color: #fff;
}
.text-red {
color:rgb(206, 35, 35);
}
.text-orange {
color:rgb(252, 144, 0);
}
.nav {
background-color:rgba(255,255,255,0.15);
}
.selected-item {
background-color:rgb(255,0,0);
}
Javascript / jQuery
$(document).on('click', '#myDropdown li a', function() {
var r = $('#result');
$(this).parent().parent().children().removeClass('selected-item');
if($(this).hasClass('selected-item')) {
$(this).removeClass('selected-item'); //De-select menu item.
} else {
$(this).parent().addClass('selected-item'); //Select menu item.
}
r.empty().append('You have selected <strong>'+$(this).text()+'</strong> in the drop-down menu.');
});
$(document).on('click', '#checkBtn', function() {
var selected = $('#myDropdown .selected-item');
if(selected.length > 0) { //If any of the menu items <li> have the "selected-item" class added.
$('#check').empty().append('Found a selected menu item. The value of it is: <strong>'+selected.text()+'</strong>');
} else {
$('#check').empty().append('You have not selected an item from the menu.');
}
});
https://jsfiddle.net/Xonos/74pwyL1m/

Dropdown menu which will respond on click another content

I have this dropdown menu solution. http://jsfiddle.net/ftymhs8s/
I need to hide the one dropdown menu that is displayed when I click on a different line and show dropdown menu of it. I also need the first line dropdown menu to be displayed always when people go to the website.
I hope I correctly described my problem, can you please help me?
// Dropdown Menu
var dropdown = document.querySelectorAll('.dropdown');
var dropdownArray = Array.prototype.slice.call(dropdown, 0);
dropdownArray.forEach(function(el) {
var button = el.querySelector('a[data-toggle="dropdown"]'),
menu = el.querySelector('.dropdown-menu'),
arrow = button.querySelector('i.icon-arrow');
button.onclick = function(event) {
if (!menu.hasClass('show')) {
menu.classList.add('show');
menu.classList.remove('hide');
arrow.classList.add('open');
arrow.classList.remove('close');
event.preventDefault();
} else {
menu.classList.remove('show');
menu.classList.add('hide');
arrow.classList.remove('open');
arrow.classList.add('close');
event.preventDefault();
}
};
})
Element.prototype.hasClass = function(className) {
return this.className && new RegExp("(^|\\s)" + className + "(\\s|$)").test(this.className);
};
ul {
list-style: none
}
.dropdown a {
text-decoration: none;
}
.dropdown [data-toggle="dropdown"] {
position: relative;
display: block;
color: black;
padding: 10px;
}
.dropdown .dropdown-menu {
max-height: 0;
overflow: hidden;
}
.dropdown .dropdown-menu li {
padding: 0;
}
.dropdown .dropdown-menu li a {
display: block;
padding: 10px 10px;
}
.dropdown .show {
display: block;
max-height: 9999px;
margin-left: 50px;
}
.dropdown .hide {
max-height: 0;
}
<div class="container">
<ul>
<li class="dropdown">
First Menu
<ul class="dropdown-menu">
<li>Home</li>
<li>About Us</li>
<li>Services</li>
<li>Contact</li>
</ul>
</li>
<li class="dropdown">
Second Menu
<ul class="dropdown-menu">
<li>Home</li>
<li>About Us</li>
<li>Services</li>
<li>Contact</li>
</ul>
</li>
<li class="dropdown">
Third Menu
<ul class="dropdown-menu">
<li>Home</li>
<li>About Us</li>
<li>Services</li>
<li>Contact</li>
</ul>
</li>
</ul>
</div>
The behavior of multiple collapsible elements where only one of them can be open at one time is that of an accordion. The general idea is to first close all collapsibles then open the one that was selected by user. The following demo exhibits that behavior by Event Delegation.
BTW I noticed that you made a hasClass... er Class. That's not necessary, just use: node.classList.contains('class')
Details are commented in the demo
Demo
/* Added .main class to parent <ul>
|| By adding the eventListener to the
|| parent of multiple clickable nodes
|| and using e.target property to find
|| the exact node actually clicked, we
|| have just needed the <ul> to listen
|| rather than 3 separate <li>
|| This is part of Event Delagation
*/
var main = document.querySelector('.main');
main.addEventListener('click', accordion, false);
function accordion(e) {
/* Gather all .dropdown-menu to a NodeList
|| then covert it to an array
*/
var dropArray = Array.from(document.querySelectorAll('.dropdown-menu'));
/* Gather all links in the .dropdown-menus to
|| a NodeList then convert it to an array
*/
var linxArray = Array.from(document.querySelectorAll('a + .dropdown-menu a'));
/* if the clicked node (e.target) is NOT the
|| node listening for event (e.currentTarget
|| ul.main) then...
*/
if (e.target !== e.currentTarget) {
// Assign e.target to var tgr
var tgr = e.target;
/* if tgr has data-toggle attribute...
|| Find tgr next sibling (.dropdown-menu)
|| Iterate through dropArray wth a
|| for...of loop
|| Remove .show and add .hide on
|| each .dropdown-menu in dropArray
|| Then add .show and remove .hide
|| on tgt
|| Finally stop the click event from
|| bubbling, thereby preventing anything
|| else from being triggered.
*/
if (tgr.hasAttribute('data-toggle')) {
// Stop <a> from jumping
e.preventDefault();
var tgt = tgr.nextElementSibling;
for (let drop of dropArray) {
drop.classList.remove('show');
drop.classList.add('hide');
}
tgt.classList.add('show');
tgt.classList.remove('hide');
} else {
return;
}
e.stopPropagation();
}
}
html,
body,
.contain {
height: 100%;
width: 100%;
}
.main,
section,
article {
margin-bottom: 100vh;
}
ul {
list-style: none
}
.dropdown a {
text-decoration: none;
}
.dropdown [data-toggle="dropdown"] {
position: relative;
display: block;
color: black;
padding: 10px;
}
.dropdown .dropdown-menu {
max-height: 0;
overflow: hidden;
}
.dropdown .dropdown-menu li {
padding: 0;
}
.dropdown .dropdown-menu li a {
display: block;
padding: 10px 10px;
}
.dropdown .show {
display: block;
max-height: 9999px;
margin-left: 50px;
}
.dropdown .hide {
max-height: 0;
}
<div id='home' class="container">
<ul class='main'>
<li class="dropdown">
First Menu
<ul class="dropdown-menu">
<li>Home</li>
<li>About Us</li>
<li>Services</li>
<li>Contact</li>
</ul>
</li>
<li class="dropdown">
Second Menu
<ul class="dropdown-menu">
<li>Section I</li>
<li>Section II</li>
<li>Section III</li>
<li>Section IV</li>
</ul>
</li>
<li class="dropdown">
Third Menu
<ul class="dropdown-menu">
<li>Example</li>
<li>Example</li>
<li>Example</li>
<li>Example</li>
</ul>
</li>
</ul>
<article id='about'>
<h2>About</h2>
</article>
<article id='services'>
<h2>Services</h2>
</article>
<article id='contact'>
<h2>Contact</h2>
</article>
<hr>
<section id='1'>
<h2>Section I</h2>
</section>
<section id='2'>
<h2>Section II</h2>
</section>
<section id='3'>
<h2>Section III</h2>
</section>
<section id='4'>
<h2>Section IV</h2>
</section>
</div>

How to make CSS drop down menu appear/change on click

I have a web page, 1000px by 1000px for the main div. Inside the main div, at the top, there is a horizontal bar with four sections, each taking up 1/4 of the space. Each section has some text [wrapped in h2 tag], horizontally/vertically centered in the middle of the 1/4 section and each section must generate a drop-down menu.
For the drop down menu [which must work both on mobile and desktop], I borrowed the idea of using a checkbox [check for make dropdown visible, uncheck for invisible], but it isn't working correctly. The checkbox is small and if it is invisible it is almost impossible to know where to click to check/uncheck. I want the drop down menu to appear if the user clicks/taps ANYWHERE in the 1/4 section area.
The horizontal row of 1/4 section drop down menus looks like this:
^ Note that they don't actually work.
HTML Code:
<div id="Media_Choices">
<div id="Video" class="media_choice"> <h2>Video▼</h2> </div>
<div id="Pictures" class="media_choice"> <h2>Pictures▼</h2> </div>
<div id="Audio" class="media_choice"> <h2>Audio▼</h2> </div>
<div id="Stories" class="media_choice"> <h2>Stories▼</h2> </div>
</div>
CSS:
#Media_Choices {
width: 100%;
max-height:40px;
min-height:40px;
}
.media_choice {
display: inline;
float: left;
width: 24.5%;
max-height: 38px;
min-height: 38px;
text-align: center;
vertical-align: middle;
line-height: 38px; /* the same as your div height */
}
#Video {
}
#Pictures {
}
#Audio {
}
#Stories {
}
Extra credit if you can get the ▼ downward facing arrow to turn into a ▲ whenever the drop down menu is down and then revert back into a ▼ downward facing arrow whenever the menu is up. You don't need to use the check-box based technique [I know there is a hover option], but anything that works cross platform is good.
For reference, check boxes were origionally implemented using the following code [taken from another question], but copy-pasting in this solution and changing the text inside the box isn't good enough:
<input class="dropdowninput" type="checkbox" id="dropdownbox1"/>
<div class="dropdownbox">
<label for="dropdownbox1">Open dropdown</label>
<ul class="dropdown">
<li>...</li><li>etc</li>
</ul>
</div>
with CSS:
.dropdowninput, .dropdown {display:none;}
.dropdowninput:checked + .dropdownbox .dropdown {display:block;}
If i understand you correctly you want to create a responsive dropdown menu and you want the arrows to change when the menu appear/disappear, if this is the case a one way to do it would be to attach event listeners to the menu items that would show/hide the submenus on click, using css and javascript you can do the following:
.media_choice > h2:after {
display: inline-block;
content: '▼';
}
.media_choice.dropped > h2:after {
content: '▲';
}
.media_choice > ul {
display: none;
}
.media_choice.dropped > ul {
display: block;
}
And with javascript add the event listeners:
$(document).ready (function()
{
$('.media_choice').on ('click', function()
{
$(this).toggleClass ('dropped');
});
});
JSFiddle
Here it is using checkboxes and no JS.
nav {
width: 80%;
margin: 20px auto;
}
nav ul {
margin: 0;
padding: 0;
list-style-type: none;
overflow: none;
/* to contain the floats */
}
nav li {
padding: 0;
margin: 0;
width: 25%;
float: left;
position: relative;
white-space: nowrap;
}
nav input {
display: none;
}
nav label {
display: block;
border: 1px solid black;
padding: 10px;
cursor: pointer;
}
nav label:hover {
background: #ccc;
}
nav a {
display: block;
padding: 10px;
}
nav a:hover {
background: #ccc;
}
nav label:after {
content: '▼';
font-size: 10px;
}
nav ul ul {
display: none;
position: absolute;
top: 100%;
border: 1px solid red;
box-sizing: border-box;
background: #fff;
width: 100%;
}
nav ul ul li {
width: 100%;
float: none;
}
nav input:checked ~ ul {
display: block;
}
nav input:checked ~ label:after {
content: '▲';
}
<!-- http://codepen.io/allicarn/pen/gPPmZZ -->
<nav>
<ul>
<li>
<input type="checkbox" id="navitem1" name="navinputs" />
<label for="navitem1">Menu Item #1</label>
<ul>
<li>Sub Menu Item #1a</li>
<li>Sub Menu Item #1b</li>
<li>Sub Menu Item #1c</li>
<li>Sub Menu Item #1d</li>
</ul>
</li>
<li>
<input type="checkbox" id="navitem2" name="navinputs" />
<label for="navitem2">Menu Item #2</label>
<ul>
<li>Sub Menu Item #2a</li>
<li>Sub Menu Item #2b</li>
<li>Sub Menu Item #2c</li>
<li>Sub Menu Item #2d</li>
</ul>
</li>
<li>
<input type="checkbox" id="navitem3" name="navinputs" />
<label for="navitem3">Menu Item #3</label>
<ul>
<li>Sub Menu Item #3a</li>
<li>Sub Menu Item #3b</li>
<li>Sub Menu Item #3c</li>
<li>Sub Menu Item #3d</li>
</ul>
</li>
<li>
<input type="checkbox" id="navitem4" name="navinputs" />
<label for="navitem4">Menu Item #4</label>
<ul>
<li>Sub Menu Item #4a</li>
<li>Sub Menu Item #4b</li>
<li>Sub Menu Item #4c</li>
<li>Sub Menu Item #4d</li>
</ul>
</li>
</ul>
</nav>

AngularJS top menu submenu dropdowns on hover (persist showing whilst moving to submenu)

I have a top menu with various links. On hover, each should show a dropdown with additional menu items. I have tried attached onmouseover and onmouseleave events to the menu item to hide/show the sub menu; however, when transitioning off of the menu item and into the sub menu, the onmouseleave fires and hides the sub menu and the user doesn't have a chance to actually interact with the sub menu.
<nav>
<div class="container-fluid">
<ul class="">
<li>
<a ui-sref="home.person" ng-init="showPersonSubMenu=false" ng-mouseenter="showPersonSubMenu=true" ng-mouseleave="showPersonSubMenu=false">People</a>
<ul class="person-sub-menu" ng-show="showPersonSubMenu">
<li>Add Person</li>
</ul>
</li>
<li><a ui-sref="home.company">Companies</a></li>
<li><a ui-sref="home.job">Jobs</a></li>
<li><a ui-sref="home.report">Reports</a></li>
</ul>
</div>
</nav>
How can I show the sub menu on hover, and hide it on leaving... whilst still allowing the user to actually access the sub menu so it doesn't hide before they can interact with it.
You were on the right track.
Make sure there is no space between your menu item and your absolute sub-menu. To ensure that there is no space, make the menu item bigger (using height or line-height), or add a padding to it...
Here's a working example:
http://codepen.io/jlowcs/pen/QwJwJZ
HTML:
<ul class="menu">
<li>
<a>People</a>
<ul class="sub-menu">
<li>Add Person</li>
<li>Action 2</li>
</ul>
</li>
<li><a ui-sref="home.company">Companies</a></li>
</ul>
CSS:
ul, li {
list-style-type: none;
margin: 0;
padding: 0;
}
.menu {
background: lightblue;
height: 30px;
padding: 0 10px;
}
.menu > li {
display: inline-block;
width: 100px;
line-height: 30px;
}
.sub-menu {
position: absolute;
display: none;
background-color: lightgreen;
padding: 5px;
}
.sub-menu > li {
display: block;
cursor: pointer;
}
li:hover .sub-menu {
display: block;
}
EDIT: if you want your submenu to float lightly lower, here's a way of doing that:
http://codepen.io/jlowcs/pen/dPQPxW
Just add the following CSS:
.sub-menu {
margin-top: 10px;
}
.menu > li:hover {
padding-bottom: 10px;
}

how to implement css horizontal menu with variable width submenus

I have a menu I would like to implement. It can sometimes drill down 3 or 4 layers so I'm trying to optimize space taken up by each menu. I'd like to have each ul set to width: auto, the only problem is I can't set the right/left property reliably for where the next submenu should appear. Here's some code:
HTML
<ul class="top-level-menu horizontal-multimenu">
<li>L360 Home</li>
<li>
Liquidity Risk Monitor
<ul class="second-level-menu">
<li>Indicators</li>
<li>Stress Levels</li>
</ul>
</li>
<li>
Sources and Assumptions
<ul class="second-level-menu">
<li>Source of Funds</li>
<li>Funding Assumptions</li>
</ul>
</li>
<li>
Data
<ul class="second-level-menu">
<li>Transfer</li>
<li>Table</li>
</ul>
</li>
<li>
Reports
<ul class="second-level-menu">
<li>
Risk Monitor
<ul class="subsecond-level-menu">
<li>Scorecard</li>
<li>DCG ALCO Data</li>
<li>Historical</li>
<li>
Prior Values
<ul class="subsecond-level-menu">
<li>Conditional Item</li>
<li>Conditional Item</li>
<li>Conditional Item</li>
<li>Conditional Item</li>
<li>Conditional Item</li>
<li>Conditional Item</li>
</ul>
</li>
</ul>
</li>
<li>
Stress Reports
<ul class="subsecond-level-menu">
<li>Footnotes</li>
<li>
Liquidity Availability Analysis
<ul class="subsecond-level-menu">
<li>Base Case</li>
<li>Level 1</li>
<li>Level 2</li>
<li>Level 3</li>
</ul>
</li>
<li>Exexcutive Summary</li>
<li>Liquidity Forecast</li>
</ul>
</li>
<li>
Worksheets
<ul class="subsecond-level-menu">
</ul>
</li>
<li>Package Creator</li>
<li>
Memoranda
<ul class="subsecond-level-menu">
</ul>
</li>
</ul>
</li>
<li>Support</li>
</ul>
CSS
/* Menu Styles */
.subsecond-level-menu
{
position: absolute;
top: 0;
right: 999px;
width: 300px;
list-style: none;
padding: 0;
margin: 0;
opacity: 0;
transition: opacity .3s;
}
.subsecond-level-menu>li
{
height: 30px;
background: #999999;
padding: 0 1em;
}
.subsecond-level-menu>li:hover { background: #CCCCCC; }
.second-level-menu
{
position: absolute;
top: 30px;
left: 0;
width: 150px;
list-style: none;
padding: 0;
margin: 0;
display: none;
}
.second-level-menu>li
{
position: relative;
height: 30px;
background: #999999;
padding: 0 1em;
}
.second-level-menu>li:hover { background: #CCCCCC; }
.top-level-menu
{
list-style: none;
padding: 0;
margin: 0;
}
.top-level-menu>li
{
position: relative;
float: left;
height: 30px;
width: /*150px*/auto;
background: #999999;
padding: 0 1em;
}
.top-level-menu>li:hover { background: #CCCCCC; }
.top-level-menu li:hover>ul
{
/*On hover, display the next level's menu */
display: block;
right: -300px;
opacity: 1;
}
/* Menu Link Styles */
.top-level-menu a /* Apply to all links inside the multi-level menu */
{
font: bold 14px Arial, Helvetica, sans-serif;
color: #FFFFFF;
text-decoration: none;
/*padding: 0 0 0 10px;*/
margin: 0 auto;
/* Make the link cover the entire list item-container */
display: inline;
line-height: 30px;
}
.top-level-menu a:hover { color: #000000; }
.top-level-menu li{whitespace: no-wrap;}
This should work, but all the .subsecond-level-menu's are required to be x px wide so I know where to put them.
Do you think it would be better to assign id's to each submenu and then just assign static widths/right's to each or try to make it happen with some javascript?
I've been wrestling with the js attempt for a while, but it doesn't quite seem to the submenu widths. Here's that code that I've been working on if you're curious:
JS
$(document).ready(function(){
$('.top-level-menu').find('li').hover(
function(e){
$(e.target).css('background-color', 'red');
var item = $(this);
console.log('The child list is considered hidden: ' + $(e.target).children('ul').is(':hidden') + ', Child width: '+$(e.target).children('ul').outerWidth() + 'px');
$(e.target).children('ul').css({
'right': (-1 * $(e.target).children('ul').outerWidth()) + 'px',
'opacity': '1',
'display': 'block'
});
},
function(e){
$(e.target).css('background-color', '');
var item = $(this),
emptyCss = {
'right' : '',
'opacity' : '',
'display': ''};
item.children('ul').css(emptyCss);
}
);
});

Categories

Resources