I am trying to make a nav bar with two drop down menus "appliances" and "furniture"
i can get one to work but i am having trouble with the second one, i am assuming my function is only applying to one of them at a time but cannot figure out why the other is not working nor can i get it to work. `
<table>
<tr>
<td>Home
<td class="dropdown">Appliances
<div class="dropdown-content" id="drop">
Link 1
Link 2
<td><td class="dropdown">Furniture
<div class="dropdown-content" id="drop">
Link 1
Link 2
<td>Bedding
<td>Contact Us
<td>Shopping Cart
</tr>
</table>
<script>
function myFunction() {
document.getElementById("drop").classList.toggle("show");
}
window.onclick = function(e) {
if (!e.target.matches('.dropbtn')) {
var dropdowns = document.getElementsByClassName("dropdown-content");
for (var d = 0; d < dropdowns.length; d++) {
var openDropdown = dropdowns[d];
if (openDropdown.classList.contains('show')) {
openDropdown.classList.remove('show');
}
}
}
}
</script>
here is the css
td a, .dropbtn {
display: inline-block;
color: #b35900;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
td a:hover, .dropdown:hover .dropbtn {
background-color:#ff0000;
}
td.dropdown {
display: inline-block;
}
.dropdown-content {
display: none;
position: absolute;
background-color: #ff000;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
}
.dropdown-content a {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
text-align: left;
}
.dropdown-content a:hover {background-color: #ff0000}
<table>
<tr>
<td>Home
<td class="dropdown">Appliances
<div class="dropdown-content" id="drop1">
Link 1
Link 2
<td><td class="dropdown">Furniture
<div class="dropdown-content" id="drop2">
Link 1
Link 2
<td>Bedding
<td>Contact Us
<td>Shopping Cart
</tr>
</table>
<script>
function myFunction(id) {
document.getElementById(id).classList.toggle("show");
}
</script>
At least its a beginning. You need unique ids on everything in your dom, so the getElemenetById call will only find one of them. Adding the id as a param will seperate them.
For something like a menu, using id is probably more hassle than its worth (down the line anyway)
When an event is fired an event variable is passed to the handler, it's got everything you need to be able to identify the pressed element
function myFunction(e) {
e=e||event;
if (e.target.classList.contains("dropdown")) {
e.target.classList.toggle("show");
}
}
I haven't tested this, and I dont think it will work with the code you've presented as there is no css definition for "show" unless you missed it when pasting it.
Related
I have attempted to implement the method of opening and closing a drop-down using Javascript via this tutorial on w3schools.com. While the function to "show" the drop-down works, the one to close it does not. Furthermore, there is no explanation alongside this code to explain why it should work, making it difficult to debug.
/* When the user clicks on the button,
toggle between hiding and showing the dropdown content */
function myFunction() {
document.getElementById("myDropdown").classList.toggle("show");
}
// Close the dropdown menu if the user clicks outside of it
window.onclick = function(event) {
if (!event.target.matches('.dropbtn')) {
var dropdowns = document.getElementsByClassName("dropdown-content");
var i;
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('show')) {
openDropdown.classList.remove('show');
}
}
}
}
My questions are, therefore,
1) whether the code in the tutorial should work for the purpose of closing the drop-down. (ANSWERED)
2) Could someone please clarify how/why this should work, for the sake of clarity for myself and future newbies who make come across the same tutorial and issue? (UNANSWERED)
Edit (MY ATTEMPT):
HTML:
<div class="sharedown">
<p onclick="shareVis()" class="sharebtn">  Share</p>
<div id="mySharedown" class="sharedown-content">
Self
<p>User</p><input type="text" name="user-name" placeholder="Share to">
Community
</div>
</div>
JS:
function shareVis() {
document.getElementById("mySharedown").className = "show";
}
window.onclick = function(event) {
if (!event.target.matches('sharebtn')) {
var sharedowns = document.getElementsByClassName("sharedown-content");
var i;
for (i = 0; i < sharedowns.length; i++) {
var openSharedown = sharedowns[i];
if (openSharedown.classList.contains('show')) {
openSharedown.classList.remove('show');
}
}
}
}
CSS:
/* Share dropdown menu */
p.sharebtn {
color: darkgrey;
font-family:calibri;
padding: 0px;
margin: 0px;
font-size: 12;
border: none;
cursor: pointer;
display: inline;
}
/* Dropdown button on hover & focus */
p.sharebtn:hover, p.sharebtn:focus {
color: grey;
}
/* The container <div> - needed to position the dropdown content */
.sharedown {
position: relative;
display: inline-block;
}
/* Dropdown Content (Hidden by Default) */
.sharedown-content {
display: none;
position: absolute;
background-color: #f1f1f1;
min-width: 100px;
box-shadow: 0 2px 4px 1px #C4E3F5;
z-index:1; /* place dropdown infront of everything else*/
}
.sharedown-content a {
color: black;
padding: 5px 5px;
text-decoration: none;
display: block;
}
/* Show the dropdown menu (use JS to add this class to the .dropdown-
content container when the user clicks on the dropdown button) */
.show {display: block;
position: absolute;
background-color: #f1f1f1;
min-width: 100px;
box-shadow: 0 2px 4px 1px #C4E3F5;
opacity: 1;
z-index:1;}
The issue lies in shareVis function. Here
document.getElementById("mySharedown").className = "show";
you are replacing #mySharedown class name to show. Then in window.onclick
var sharedowns = document.getElementsByClassName("sharedown-content");
you are not getting any sharedowns as you already replaced the class name to show.
You can either add show class into classList
document.getElementById("mySharedown").classList.add("show");
or replace the class name with sharedown-content show
document.getElementById("mySharedown").className = "sharedown-content show";
Working solution below:
function shareVis() {
//document.getElementById("mySharedown").className = "sharedown-content show";
document.getElementById("mySharedown").classList.add("show");
}
window.onclick = function(event) {
if (!event.target.matches('.sharebtn')) {
var sharedowns = document.getElementsByClassName("sharedown-content");
var i;
for (i = 0; i < sharedowns.length; i++) {
var openSharedown = sharedowns[i];
if (openSharedown.classList.contains('show')) {
openSharedown.classList.remove('show');
}
}
}
}
document.getElementById("mySharedown").addEventListener('click',function(event){
event.stopPropagation();
});
#mySharedown{
display: none;
border: 1px solid black;
}
#mySharedown.show {
display: block;
}
<div class="sharedown">
<p onclick="shareVis()" class="sharebtn">  Share</p>
<div id="mySharedown" class="sharedown-content">
Self
<p>User</p><input type="text" name="user-name" placeholder="Share to">
Community
</div>
</div>
Update
To prevent the second click within #mySharedown from hiding #mySharedown, you should add another click event for #mySharedown and prevent it from bubbling up, like this
document.getElementById("mySharedown").addEventListener('click',function(event){
event.stopPropagation();
});
Updates are included in the working solution
Update 2022
Vanilla Javascript now contains a mehtod called Node.closest(Node) to check if the event matches the node in the upper hierarchy. below is an example to open the dropdown menu on click and hide it again on click and if clicking outside the document will also hide the dropdown menu.
const list = document.querySelector('.list')
const btn = document.querySelector('.btn')
btn.addEventListener('click', (e)=> {
list.classList.toggle('hidden')
e.stopPropagation()
})
document.addEventListener('click', (e)=> {
if(e.target.closest('.list')) return
list.classList.add('hidden')
})
.hidden {
display:none
}
ul {
background-color: blue;
}
<button class="btn">open</button>
<ul class="list hidden">
<li class="item1">Item 1</li>
<li class="item2">Item 2</li>
<li class="item3">Item 3</li>
</ul>
Here I leave another "short" example that I implemented to my own code, but is easy to understand.
.tw-hidden is a class "display: none"
window.onclick = function(event) {
let customDropdownsEl = document.querySelectorAll(".custom-dropdown");
let liContainerEl = event.target.querySelector(".custom-dropdown");
customDropdownsEl.forEach(el => el.parentNode !== event.target && !el.classList.contains("tw-hidden") && el.classList.add("tw-hidden"));
event.target.matches('.custom-dropdown-container') && liContainerEl.classList.toggle("tw-hidden");
}
The example is fully functional and should work. Copy the following code below:
/* When the user clicks on the button,
toggle between hiding and showing the dropdown content */
function myFunction() {
document.getElementById("myDropdown").classList.toggle("show");
}
// Close the dropdown if the user clicks outside of it
window.onclick = function(event) {
if (!event.target.matches('.dropbtn')) {
var dropdowns = document.getElementsByClassName("dropdown-content");
var i;
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('show')) {
openDropdown.classList.remove('show');
}
}
}
}
.dropbtn {
background-color: #3498DB;
color: white;
padding: 16px;
font-size: 16px;
border: none;
cursor: pointer;
}
.dropbtn:hover, .dropbtn:focus {
background-color: #2980B9;
}
.dropdown {
position: relative;
display: inline-block;
}
.dropdown-content {
display: none;
position: absolute;
background-color: #f1f1f1;
min-width: 160px;
overflow: auto;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 1;
}
.dropdown-content a {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
}
.dropdown a:hover {background-color: #ddd}
.show {display:block;}
<h2>Clickable Dropdown</h2>
<p>Click on the button to open the dropdown menu.</p>
<div class="dropdown">
<button onclick="myFunction()" class="dropbtn">Dropdown</button>
<div id="myDropdown" class="dropdown-content">
Home
About
Contact
</div>
</div>
I've got myself all in a mess. When each of the "..." within the span is clicked, I would like the myDropdown div containing "Report" to be shown next to the span.
Then I would like for when the corresponding Report is clicked to return the id of the "elementid" class in this case either 1 or 2 but I wasn't sure what to put in the reportFunction
<html>
<style>
.dropbtn {
background-color: white;
color: black;
padding: 2px;
font-size: 30px;
border: none;
cursor: pointer;
font-weight: bold;
}
.dropdown {
position: relative;
display: inline-block;
}
.dropdown-content {
display: none;
background-color: #f9f9f9;
min-width: 160px;
font-size:10px;
overflow: auto;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
border: 1px solid lightgrey;
z-index: 1;
float: left;
}
.dropdown-content a {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
}
.dropdown a:hover {background-color: #f1f1f1;cursor:pointer;}
.show {display:block;}
</style>
<span class="dropdown">
<button onclick="myFunction()" class="dropbtn">...</button>
<div id="myDropdown" class="dropdown-content">
<a class="elementid" id="1">Report</a>
</div>
</span>
<p>
<span class="dropdown">
<button onclick="myFunction()" class="dropbtn">...</button>
<div id="myDropdown" class="dropdown-content">
<a class="elementid" id="2">Report</a>
</div>
</span>
<script>
function myFunction() {
document.getElementById("myDropdown").classList.toggle("show");
}
window.onclick = function(event) {
if (!event.target.matches('.dropbtn')) {
var dropdowns = document.getElementsByClassName("dropdown-content");
var i;
var id = document.getElementsByID("id");
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('show')) {
openDropdown.classList.remove('show');
}
}
}
}
function reportFunction() {
}
</script>
</html>
Something like this should get you the id you want. It goes up to the containing span with parent() and then gets the element with class elementId within.
function reportFunction() {
var id = $(this).parent().find('.elementid');
... //whatever you need to do
}
Your code has other problems though. You have two div elements with the same id myDropdown. Your current code with getElementById will always get the first one. You need to either specify a class and no id on them, and get the right one in the same way as above, or to use one div that you remove and attach to the correct span as needed.
I have a simple drop-down menu using JavaScript.
<div id="show-nav" class="dropdown">
<div id="dropdown" onClick="myFunction()">Menu Name</div>
<div id="myDropdown" class="dropdown-content">
Option 1
Option 2
Option 3
</div>
</div>
And the JS:
<script>
/* When the user clicks on the button,
toggle between hiding and showing the dropdown content */
function myFunction() {
document.getElementById("myDropdown").classList.toggle("show");
}
// Close the dropdown menu if the user clicks outside of it
window.onclick = function(event) {
if (!event.target.matches('#dropdown')) {
var dropdowns = document.getElementsByClassName("dropdown-content");
var i;
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('show')) {
openDropdown.classList.remove('show');
}
}
}
}
</script>
I have multiple copies of the same menu in several adjacent divs. With this code, the menu works in the first element, but if I try to toggle it in the second, it only drops down in the first. How can I make this work for several menus on a single page?
To the myFunction pass this. That is:
<div id="dropdown" onClick="myFunction(this)">Menu Name</div>
Write a JavaScript function like below:
function myFunction(a) {
a.parentNode.getElementsByClassName("dropdown-content")[0].classList.toggle("show");
}
No need of that much of JavaScript: keep as simple as possible.
You need two changes on your code :
Make all buttons' onClick attributes same. (I guess it is "myFunction();")
Change your handler to process the button which is actually clicked.
This is possible with event.target :
function myFunction(event) {
event.target.classList.toggle("show");
}
You can simply do this without written any long javascript code.
use this
<div id="show-nav" class="dropbtn">
<div id="dropdown" onClick="myFunction(this)">Menu Name</div>
<div id="myDropdown" class="dropdown-content">
Option 1
Option 2
Option 3
</div>
</div>
<div id="show-nav" class="dropbtn" style="margin-left:300px; margin-top:-16px;">
<div id="dropdown" onClick="myFunction(this)">Menu Name</div>
<div id="myDropdown" class="dropdown-content">
Option 1
Option 2
Option 3
</div>
</div>
function myFunction(a) {
a.parentNode.getElementsByClassName('dropdown-content')[0].classList.toggle("show");
}
.dropdown {
position: absolute;
right:5%;
overflow: hidden;
}
.dropdown .dropbtn {
font-size: 16px;
border: none;
outline: none;
background-color: inherit;
font-family: inherit;
margin: 0;
}
.dropdown-content {
display: none;
background-color: #f9f9f9;
min-width: 50px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 1;
}
.dropdown-content a {
float: none;
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
text-align: left;
}
.show {
display: block;
}
First of all, I am new with javascript, html, and CSS so bear with me. I have looked everywhere for the answer to my question but I can't find anything that works for my specific code.
I am trying to create a webpage that has multiple dropdown menus and that each one opens when the user clicks on it. I am able to do this but an issue occurs. If I open a dropdown menu and then click on another dropdown menu, the first menu remains open. I want the first menu to close when I open a new one.
Here is a section of my html code with 2 of the dropdown menus:
<table class="prodMenu">
<tr><td>
<div class="dropdown2">
<button onclick="myFunction('m1')" class="dropbtn2">SPCGuidance</button>
<div id="m1" class="dropdown2-content">
[PR]:4-hr Calibrated Tornado Probability
[PR]:4-hr Calibrated Hail Probability
</div>
</div>
</td>
<td>
<div class="dropdown2">
<button onclick="myFunction('m2')" class="dropbtn2">Reflectivity</button>
<div id="m2" class="dropdown2-content">
[SP]:3-hr 1-km Reflectivity >=40 dBZ
[NPRS]:3-hr 1-km Reflectivity >=40 dBZ
</div>
</div>
</td>
Next is the part of the .js script that interacts with these dropdown menus. I do have a function that closes the open menus if you click somewhere in the window. However, I'm not sure how to make a function that closes the first dropdown menu when another dropdown menu is opened.
// When the user clicks on the button, toggle between hiding and showing the dropdown content.
function myFunction(id) {
document.getElementById(id).classList.toggle("show");
}
// Close the dropdown if the user clicks in window.
window.onclick = function(event) {
if (!event.target.matches('.dropbtn2')) {
var dropdowns = document.getElementsByClassName("dropdown2-content");
var i;
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('show')) {
openDropdown.classList.remove('show');
}
}
}
}
And finally here is the part of the CSS script that controls the dropdown menus:
/* dropdown2 is for the rest of the dropdown menus. */
.dropbtn2 {
background-color: #444444;
color: #FFFFFF;
margin: 0 1px 0 0;
padding: 4px 3px;
width: auto;
font: bold 10px arial;
cursor: pointer;
text-align: center;
white-space: nowrap;
text-decoration: none;
border: none;
}
.dropbtn2:hover, .dropbtn2:focus {
background-color: #000066;
border: none;
}
.dropdown2 {
position: relative;
display: inline-block;
z-index: 30;
.dropdown2-content {
display: none;
position: absolute;
padding: 0px;
width: auto;
min-width: 160px;
white-space: nowrap;
background: #DDDDDD;
overflow: auto;
z-index: 1;
border-style: solid;
border-width: 1px;
border-color: #000000;
}
.dropdown2-content a {
color: #000000;
padding: 2px 3px;
font: 10px arial;
display: block;
}
.dropdown2 a:hover {
background: #000066;
color: #FFF;
border: none;
text-decoration: none;
}
.show {
display:block;
}
Any help would be greatly appreciated. Thanks.
EDIT:
I got it.
For the Javascript part, this successfully closes the current dropdown menu when you click on another, click outside in the window, or click again on the same menu's header.
function myFunction(id) {
var dropdowns = document.getElementsByCLassName("dropdown2-content");
var i;
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if ( dropdowns[i] != document.getElementById(id) ) {
openDropdown.classList.remove('show');
}
}
document.getElementById(id).classList.toggle("show");
}
// Close the dropdown if the user clicks in window.
window.onclick = function(event) {
if (!event.target.matches('.dropbtn2')) {
var dropdowns = document.getElementsByClassName("dropdown2- content");
var i;
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('show')) {
openDropdown.classList.remove('show');
}
}
}
}
You can just close all dropdowns before opening the one that was clicked
function myFunction(id) {
var dropdowns = document.getElementsByClassName("dropdown2-content");
var i;
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
openDropdown.classList.remove('show');
}
document.getElementById(id).classList.toggle("show");
}
If you can include jQuery and use it, this will work:
$(document).ready(function(){
$(document).on('click','.dropbtn2',function(){
$('.dropbtn2').not(this).next().removeClass('show');
$(this).next().toggleClass('show');
});
$(document).on('click',function(e){
if(!$(e.target).closest('.dropbtn2').length)
$('.dropbtn2').next().removeClass('show');
});
});
/* dropdown2 is for the rest of the dropdown menus. */
.dropbtn2 {
background-color: #444444;
color: #FFFFFF;
margin: 0 1px 0 0;
padding: 4px 3px;
width: auto;
font: bold 10px arial;
cursor: pointer;
text-align: center;
white-space: nowrap;
text-decoration: none;
border: none;
}
.dropbtn2:hover, .dropbtn2:focus {
background-color: #000066;
border: none;
}
.dropdown2 {
position: relative;
display: inline-block;
z-index: 30;
}
.dropdown2-content {
display: none;
position: absolute;
padding: 0px;
width: auto;
min-width: 160px;
white-space: nowrap;
background: #DDDDDD;
overflow: auto;
z-index: 1;
border-style: solid;
border-width: 1px;
border-color: #000000;
}
.dropdown2-content a {
color: #000000;
padding: 2px 3px;
font: 10px arial;
display: block;
}
.dropdown2 a:hover {
background: #000066;
color: #FFF;
border: none;
text-decoration: none;
}
.show {
display:block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<table class="prodMenu">
<tr><td>
<div class="dropdown2">
<button class="dropbtn2">SPCGuidance</button>
<div id="m1" class="dropdown2-content">
[PR]:4-hr Calibrated Tornado Probability
[PR]:4-hr Calibrated Hail Probability
</div>
</div>
</td>
<td>
<div class="dropdown2">
<button class="dropbtn2">Reflectivity</button>
<div id="m2" class="dropdown2-content">
[SP]:3-hr 1-km Reflectivity >=40 dBZ
[NPRS]:3-hr 1-km Reflectivity >=40 dBZ
</div>
</div>
</td>
I'd say the best solution is to use Bootstrap, which can handle these situations right out of the box: see how it works http://getbootstrap.com/components/#btn-dropdowns
If you for some reason cannot use Bootstrap and can use jQuery, that would be quite easy too. When clicking the button, you would hide all the dropdowns and then show just the adjacent one. It would go something like this:
$('.dropbtn2').click(function(){
$('.dropdown2-content).hide(); // hide all the dropdowns
$(this).next().show(); // show the next sibling
});
If you can use neither Bootstrap nor jQuery, just copy the code you have in the windows.onclick part before showing the element in myFunction, as Funk Doc said.
i am trying to make a clickable menu, and trying to make it toggle using javascript and css, but I want to make the each also to have sub-menus also toggle, and I trying to do it mainly with javascript, how can I make it?
Here is my code:
HTML:
<div class="dropdown">
<button onclick="myFunction()" class="dropbtn">|||</button>
<div id="myDropdown" class="dropdown-content">
Link 1
Link 2
<div class="child-dropdown">
Sublink 1
Sublink 1
Sublink 1
</div>
Link 3
<div class="child-dropdown">
Sublink 1
Sublink 1
Sublink 1
</div>
</div>
</div>
Javascript:
<script>
function myFunction() {
document.getElementById("myDropdown").classList.toggle("show");
}
window.onclick = function(event) {
if (!event.target.matches('.dropbtn')) {
var dropdowns = document.getElementsByClassName("dropdown-content");
var i;
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('show')) {
openDropdown.classList.remove('show');
}
}
}
}
</script>
css
.dropbtn {
background-color: #cc0000;
color: white;
padding: 16px;
font-size: 16px;
border: none;
cursor: pointer;
position:relative;
}
.dropbtn:hover, .dropbtn:focus {
background-color: #e6e6e6;
}
.dropdown {
position: relative;
display: inline-block;
}
.dropdown-content {
display: none;
position: absolute;
background-color: #e6e6e6;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
}
.dropdown-content a {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
}
.dropdown-content a:hover {background-color: #cc0000}
.show {display:block;}
With the given markup, javascript would not be even necessary for links inside #myDropdown: you could just use :focus pseudoclass like so
#myDropdown a:focus + div {
display: block;
}
Of course this works as long as your link is focused: if you want to be able to have something else focused (or open many submenus) you could use a bit of javascript like
[].forEach.call(document.querySelectorAll('#myDropdown > a'), function(l) {
l.addEventListener('click', function() {
l.classList.toggle('open');
}, false);
});
or with event delegation on #myDropdown
document.getElementById('myDropdown').addEventListener('click', function(evt) {
var target = evt.target;
if (target.nodeName.toLowerCase() === 'a') {
target.classList.toggle('open');
}
}, false);
and this CSS
#myDropdown div { display: none; }
#myDropdown a.open + div { display: block; }
Codepen Demo