I made a dropdown menu in my header, and when I click the moon icon (at the top right), the dropdown list doesn't show up but the function does get called when I click the icon. I've tried to use buttons but then clicking on the icon doesn't work. Any one help me with this? I am new to this thanks.
Html:
<li>
<div class="dropdown">
<script src="./assets/js/Dropdown.js"></script>
<i class="fa-solid fa-bolt fa-lg" class="dropbtn" onclick="Dropdown()"></i>
<div id="myDropdown" class="dropdown-content">
<a><i class="fa-solid fa-moon"></i> Dark Mode</a>
<a><i class="fa-solid fa-sun"></i> Light Mode</a>
<a><i class="fa-solid fa-display"></i> System</a>
</div>
</div>
</li>
CSS:
.dropbtn {
background-color: #24252A;
color: white;
padding: 16px;
font-size: 16px;
border: none;
cursor: pointer;
}
.dropdown {
position: relative;
display: inline-block;
}
.dropdown-content {
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;
cursor: pointer;
}
.dropdown-content a {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
}
.dropdown-content a:hover {
background-color: #ddd;
}
.show {
display: block;
}
Javascript:
function Dropdown() {
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');
}
}
}
}
I used to have that problem. But, you don't have to reinvent the wheel, I recommend using a CSS framework, like Materialize or Bootstrap.
I recommend Materialize, it's easy to use and you just have to initialize each element you want to use.
I show you an example of navbar:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Example</title>
<!-- Google icons -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<!-- Compiled and minified CSS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
</head>
<body>
<nav>
<div class="nav-wrapper">
<a href="#!" class="brand-logo">Logo</a>
<a href="#" data-target="mobile-demo" class="sidenav-trigger"><i class="material-icons">menu</i></a>
<ul class="right hide-on-med-and-down">
<li><a href="sass.html">Sass</a></li>
<li><a href="badges.html">Components</a></li>
<li><a href="collapsible.html">Javascript</a></li>
<li><a href="mobile.html">Mobile</a></li>
</ul>
</div>
</nav>
<ul class="sidenav" id="mobile-demo">
<li><a href="sass.html">Sass</a></li>
<li><a href="badges.html">Components</a></li>
<li><a href="collapsible.html">Javascript</a></li>
<li><a href="mobile.html">Mobile</a></li>
</ul>
<!-- Compiled and minified JavaScript -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<!-- Materialize initializers -->
<script>
document.addEventListener('DOMContentLoaded', function() {
var elems = document.querySelectorAll('.sidenav');
var instances = M.Sidenav.init(elems);
});</script>
</body>
</html>
You have two class attributes in your icon element.
When the user clicks the icon there are two click events - the first happens on the icon itself and the show class is correctly added.
The second is the general click on the window. Having two classes confuses the JS matches function which reckons the element does not match (ie would not get selected with .dropbtn) [probably not exactly 'confused' it just looks at the first class attribute so misses the dropbtn value] so it clears the show class.
If you put all the icon's classes into one attribute value then things work OK.
function Dropdown() {
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');
}
}
}
}
.dropbtn {
background-color: #24252A;
color: white;
padding: 16px;
font-size: 16px;
border: none;
cursor: pointer;
}
.dropdown {
position: relative;
display: inline-block;
}
.dropdown-content {
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;
cursor: pointer;
}
.dropdown-content a {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
}
.dropdown-content a:hover {
background-color: #ddd;
}
.show {
display: block;
}
<li>
<div class="dropdown">
<script src="./assets/js/Dropdown.js"></script>
<i class="fa-solid fa-bolt fa-lg dropbtn" onclick="Dropdown()">click me</i>
<div id="myDropdown" class="dropdown-content">
<a><i class="fa-solid fa-moon"></i> Dark Mode</a>
<a><i class="fa-solid fa-sun"></i> Light Mode</a>
<a><i class="fa-solid fa-display"></i> System</a>
</div>
</div>
</li>
You could instead consider using event.stopPropagation in the first event handler which would stop the click event going through to the whole window.