Reputation: 405
I am attempting to show a drop down menu on click of an icon. At the moment, upon clicking on the icon that is wrapped inside of an anchor
tag with an onclick function nothing happens which can be seen in the first example.
I attempted to work around this by inputting text inside of the onclick anchor tag that says DOWNLOAD REPORT
upon clicking on the text, the drop down menu appears as expected but if I were to icon on the right, the drop down menu doesn't to appear.
How can I get the drop down menu to display on click of the icon and not the text?
Here is my code samples:
function myFunction() {
document.getElementById("myDropdown").classList.toggle("show");
}
function myFunction() {
document.getElementById("myDropdown2").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-download");
var i;
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('show')) {
openDropdown.classList.remove('show');
}
}
}
}
a {
cursor: pointer;
}
.download-btn-container {
display: inline-block;
float: right;
}
.download-btn-container a {
font-weight: 700;
letter-spacing: .5px;
color: #c94927;
}
/* Dropdown Content (Hidden by Default) */
.dropdown-download {
display: none;
position: absolute;
background-color: #f1f1f1;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 1;
}
/* Links inside the dropdown */
.dropdown-download a {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
}
/* Change color of dropdown links on hover */
.dropdown-download a:hover {
background-color: #ddd
}
/* 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;
}
<link href="https://fonts.googleapis.com/icon?family=Material+Icons"
rel="stylesheet">
<div class="download-btn-container">
<span>DOWNLOAD LIST </span>
<a onclick="myFunction()" title="Download List" class="dropbtn"><span class='material-icons'>save_alt</span></a>
<div id="myDropdown" class="dropdown-download">
<a href="#">Link 1</a>
<a href="#">Link 2</a>
<a href="#">Link 3</a>
</div>
</div>
<br/>
<br/>
<br/>
<div class="download-btn-container">
<a onclick="myFunction()" title="Download List" class="dropbtn"> DOWNLOAD LIST<span class='material-icons'>save_alt</span></a>
<div id="myDropdown2" class="dropdown-download">
<a href="#">Link 1</a>
<a href="#">Link 2</a>
<a href="#">Link 3</a>
</div>
</div>
Upvotes: 0
Views: 4055
Reputation: 8610
I restructured the javascript a bit, removing the onclick attribute from the HTML and added a function that can be run in a forEach loop. Rather than adding the same named function myFunction()
on both your buttons, query the button elements using querySelectorAll
and run the nodeList through a loop. Run a function that uses the event.targets parent elements to find the drop down element and then toggle the show
class for that element using the event listener click.
You can use: e.target.parentNode.nextElementSibling.classList.toggle('show')
event.target
(this is your ICON) => .parentNode
=> (this is your .dropbtn class) => .nextElementSibling
=> (.dropdown-download - this is your hidden element) => .classList.toggle('show')
(toggles the class .show
)
NOTE: Also, not sure if you intended to have the second DOWNLOAD LIST
included in your a tag unlike the first. If so the code would need an additional conditional to run as I have it laid out.
let btns = document.querySelectorAll('.dropbtn')
// This returns a nodeList which can use the forEach method to loop over the list of nodes
// simple one line function to get the event targets parent and nextElementsSibling
function showDrpDown(e) {
e.target.parentNode.nextElementSibling.classList.toggle('show')
}
// loop and eventListener to handle each nodeLists click event
btns.forEach(btn => {
btn.addEventListener('click', showDrpDown)
})
a {
cursor: pointer;
}
.download-btn-container {
display: inline-block;
float: right;
}
.download-btn-container a {
font-weight: 700;
letter-spacing: .5px;
color: #c94927;
}
/* Dropdown Content (Hidden by Default) */
.dropdown-download {
display: none;
position: absolute;
background-color: #f1f1f1;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
z-index: 1;
}
/* Links inside the dropdown */
.dropdown-download a {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
}
/* Change color of dropdown links on hover */
.dropdown-download a:hover {
background-color: #ddd
}
/* 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;
}
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<div class="download-btn-container">
<span>DOWNLOAD LIST </span>
<a title="Download List" class="dropbtn"><span class='material-icons'>save_alt</span></a>
<div id="myDropdown" class="dropdown-download">
<a href="#">Link 1 - drpdown1</a>
<a href="#">Link 2 - drpdown1</a>
<a href="#">Link 3 - drpdown1</a>
</div>
</div>
<br/>
<br/>
<br/>
<div class="download-btn-container">
<span>DOWNLOAD LIST </span>
<a title="Download List" class="dropbtn"><span class='material-icons'>save_alt</span></a>
<div id="myDropdown2" class="dropdown-download">
<a href="#">Link 1 - drpdown2</a>
<a href="#">Link 2 - drpdown2</a>
<a href="#">Link 3 - drpdown2</a>
</div>
</div>
Upvotes: 0
Reputation: 651
I went through your code and found a couple of errors.
Error 1 in your example you have the same function named twice, so I'm assuming that's just for testing, but make sure you update your function names when testing eg. your JS has myFunction() and myFunction(), try myFunction() and myFunction2().
Error 2 your windows.onClick event needs to be rewritten more like this:
// 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-download"),
showEvent = 'show';
for (var i = 0; i < dropdowns.length; i++) {
var currentElement = dropdowns[i],
currentElementAsString = dropdowns[i].toString();
if (currentElementAsString.localeCompare(showEvent)) {
currentElement.classList.remove('show');
}
}
}
}
Explanation your original code was iterating through your classList already so you were essentially comparing characters, this line specifically:
var dropdowns = document.getElementsByClassName("dropdown-download");
var i;
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('show')) {
openDropdown.classList.remove('show');
}
}
notice how you're using getElementsByClassName()
, this is going to return an array of class names within the element, by the time you get to your for loop, you're already in class names, so essentially you're iterating through the strings within your indices.
Error 3
Your event trigger is looking for the .dropbtn
class but your anchor tag has that class, not your actual icon. Here is the line I'm referring to:
<a onclick="myFunction()" title="Download List" class="dropbtn"><span class='material-icons'>save_alt</span></a>
This should be rewritten like this:
<a onclick="myFunction()" title="Download List" class="dropbtn material-icons">save_alt</a>
If you prefer the other way, you'll need to add dropbtn class to your material-icons span. I hope this makes sense, let me know if you need help. :)
Upvotes: 1