Dropdown menu JS - javascript

Based on this post - How to open dropdown button by clicking on text?
I'm trying to add this dropdown effect when you hover the link "tools" in addition to this script but with any success (I would like to keep onclick at the same time). Someone could help me on this ? I'm sure it's quite easy but I'm very not an expert in JS sorry. Spent some hours without finding any solution :(
Many thanks for the help.
JS:
/* 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');
}
}
}
}
I tried to add this -
document.getElementByClassName(".dropbtn").onmouseenter = function(){
document.getElementByClassName("dropdown-content").classList.contains("show");
}
CSS
.dropdown {
position: relative;
display: inline-block;
}
.dropdown-content {
display: none;
position: absolute;
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;
}
button.dropbtn{
font-weight: 700;
background:none;
border:none;
padding:0;
}
.dropdown a:hover {background-color: #ddd;}
.show {display: block;}
HTML
<div>
<button onclick="myFunction()" class="dropbtn">Tools</button>
<ul id="myDropdown" class="dropdown-content">
<li>Blok 1</li>
<li>Blok 1</li>
<li>Blok 1</li>
</ul>
</div>

Hover event can be handled with mouseenter event
I have also included mouseleave event which can be used to do something on mouse leave event.
/* 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").on({
mouseenter: function () {
document.getElementById("myDropdown").classList.toggle("show");
},
//mouseleave: function () {
//do something on mouse pointer leave event
//}
});
.dropdown {
position: relative;
display: inline-block;
}
.dropdown-content {
display: none;
position: absolute;
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;
}
button.dropbtn{
font-weight: 700;
background:none;
border:none;
padding:0;
}
.dropdown a:hover {background-color: #ddd;}
.show {display: block;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
<div class="column-outlet">
<button onclick="myFunction()" class="dropbtn">Tools</button>
<ul id="myDropdown" class="dropdown-content">
<li class="force-css">Blok 1</li>
<li class="force-css">Blok 1</li>
<li class="force-css">Blok 1</li>
</ul>
</div>
PS: This is a modified answer. Credits to Uncoke for original answer at How to open dropdown button by clicking on text?

Related

Dropdown with Javascript solution

Hello all I have a problem when I use two dropdowns, when you click on one, both are opened, below is my code:
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');
}
}
}
}
function newFunction() {
document.getElementById("myDropdown1").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: #ffffff;
color: #252930;
font-size: 18px;
border: none;
cursor: pointer;
}
.dropdown {
position: relative;
display: inline-block;
}
.dropbtn-selected {
background-color: #ec292d;
color: #fff!important
}
.dropdown-content {
display: none;
position: absolute;
background-color: #f1f1f1;
min-width: max-content;
overflow: auto;
margin-top: 15px;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
z-index: 969999;
}
.dropdown-content a {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
}
.dropdown a:hover {
background-color: #ddd;
}
.show {
display: block;
}
.dropbtn>i {
pointer-events: none;
}
<li class="dropdown desktopNav">
<button onclick="myFunction()" class="dropbtn">Test <i
class="fas fa-chevron-down"></i></button>
<div id="myDropdown" class="dropdown-content">
Lorem1
Lorem 2
Lorem 3
</div>
</li>
<li class="dropdown desktopNav">
<button onclick="newFunction()" class="dropbtn">Second <i
class="fas fa-chevron-down"></i></button>
<div id="myDropdown1" class="dropdown-content">
Lorem 4
Lorem 5
Lorem 6
</div>
</li>
Is there a way to make this code more flexible? I want to achieve, when the user clicks on the dropdown that dropdown should be only opened,.
Can somebody try to help me with this? What is the best way to achieve this?
To have only one dropdown visible at a time you need to hide all others. The simple way to achieve this is to group the element structures by classname based on their behaviour, as opposed to giving each block its own incremental id. The latter approach leads to bloated duplicate code which becomes harder to maintain.
To fix this remove the id and give all the elements in the repeated blocks the same class. From there you can use DOM traversal to relate the elements to each other, hiding and displaying as necessary.
let dropdownContents = document.querySelectorAll('.dropdown-content');
// display the dropdown relevant to the clicked button
document.querySelectorAll('.dropbtn').forEach(btn => {
btn.addEventListener('click', e => {
let targetContent = e.target.parentNode.querySelector(".dropdown-content");
targetContent.classList.toggle("show");
dropdownContents.forEach(el => el !== targetContent && el.classList.remove('show'));
});
});
// hide all dropdowns when the document is clicked
document.addEventListener('click', e => {
if (!e.target.matches('.dropbtn'))
dropdownContents.forEach(el => el.classList.remove('show'));
})
.dropbtn {
background-color: #ffffff;
color: #252930;
font-size: 18px;
border: none;
cursor: pointer;
}
.dropdown {
position: relative;
display: inline-block;
}
.dropbtn-selected {
background-color: #ec292d;
color: #fff!important
}
.dropdown-content {
display: none;
position: absolute;
background-color: #f1f1f1;
min-width: max-content;
overflow: auto;
margin-top: 15px;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
z-index: 969999;
}
.dropdown-content a {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
}
.dropdown a:hover {
background-color: #ddd;
}
.show {
display: block;
}
.dropbtn>i {
pointer-events: none;
}
<ul>
<li class="dropdown desktopNav">
<button class="dropbtn">Test <i class="fas fa-chevron-down"></i></button>
<div class="dropdown-content">
Lorem1
Lorem 2
Lorem 3
</div>
</li>
<li class="dropdown desktopNav">
<button class="dropbtn">Second <i class="fas fa-chevron-down"></i></button>
<div class="dropdown-content">
Lorem 4
Lorem 5
Lorem 6
</div>
</li>
</ul>
Note the use of addEventListener over the outdated onX properties, and also the click handler which hides the dropdowns being attached to the document instead of the window.
There is no seperate 'close' event, so when clicking the second, there's no reason why the first should close.
You could have a variable that is set when a dropdown is to be shown. When clicking the second dropdown, the function would check if the variable is set, and if so, toggle that one off before toggling the correct one on. Eg:
let dropdownShown = null;
function onClick(e) {
if (dropdownShown !== e.target) {
dropdownShown.classList.toggle("show");
}
e.target.classList.toggle("show");
dropdownShown = e.target;
}

Closing dropdown by clicking outside in Javascript (tutorial clarification)

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">&nbsp 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">&nbsp 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>

Function to obtain the id on click

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.

why my code don't working in IE11

In Internet Explorer 11, when I click outside the button, the submenu does not hide, but it works fine in Google Chrome and Firefox.
http://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_onclick_dropdown
<!DOCTYPE html>
<html>
<head>
<style>
.dropbtn {
background-color: #4CAF50;
color: white;
padding: 16px;
font-size: 16px;
border: none;
cursor: pointer;
}
.dropbtn:hover, .dropbtn:focus {
background-color: #3e8e41;
}
.dropdown {
position: relative;
display: inline-block;
}
.dropdown-content {
display: none;
position: absolute;
background-color: #f9f9f9;
min-width: 160px;
overflow: auto;
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: #f1f1f1}
.show {display:block;}
</style>
</head>
<body>
<h2>Clickable Dropdown</h2>
<p>Click on the button to open the dropdown menu.</p>
<div class="dropdown">
<button id="myBtn" class="dropbtn">Dropdown</button>
<div id="myDropdown" class="dropdown-content">
Home
About
Contact
</div>
</div>
<script>
// Get the button, and when the user clicks on it, execute myFunction
document.getElementById("myBtn").onclick = function() {myFunction()};
// myFunction toggles between adding and removing the show class, which is used to hide and show 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');
}
}
}
}
</script>
</body>
</html>
event.target.matches doesn't exist in Internet Explorer. The Browser Compatibility table in Element.matches() says that starting with IE 9 this method is available, but with the non-standard name msMatchesSelector.
So try:
window.onclick = function(event) {
matches = event.target.matches ? event.target.matches('.dropbtn') : event.target.msMatchesSelector('.dropbtn');
if (!matches) {
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');
}
}
}
}
You can use the jQuery library to get cross-browser compatible code which works in IE as well.
In your case, replace your <script> with the following code:
<script src="//code.jquery.com/jquery-1.12.3.min.js"></script>
<script>
$(function(){
$(document).click(function(event){
if(!$(event.target).is('#myBtn')
&& !$(event.target).is('#myDropdown a')) {
$('#myDropdown').hide();
}
if($(event.target).is('#myBtn')) {
$('#myDropdown').toggle();
}
});
});
</script>

How can I make clickable list with sublinks ?

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

Categories

Resources