On my (live site), I have a select element created using the 'nice-select' library. This select element is nested inside a dropdown menu, which has its own JavaScript file
function singleMenu(targetId, menuId, show = false) {
const targetElement = document.getElementById(targetId);
const menuElement = document.getElementById(menuId);
// Initial state
if (show) {
// show dropdown
menuElement.style.display = "block";
targetElement.classList.add("active");
} else {
// hide dropdown
menuElement.style.display = "none";
targetElement.classList.remove("active");
}
// Toggle menu visibility when target element is clicked
targetElement.addEventListener("click", () => {
show = !show;
if (show) {
// show dropdown
menuElement.style.display = "block";
targetElement.classList.add("active");
} else {
// hide dropdown
menuElement.style.display = "none";
targetElement.classList.remove("active");
}
});
// Close menu if clicked outside of container
document.addEventListener("click", (event) => {
if (!targetElement.contains(event.target)) {
show = false;
menuElement.style.display = "none";
targetElement.classList.remove("active");
}
});
// Prevent menu from closing when clicked inside the menu element
menuElement.addEventListener("click", function (event) {
event.stopPropagation();
});
// Calculate half of the targetElement width
const targetHalfWidth = targetElement.offsetWidth / 2;
// Set a CSS variable with the half width value
targetElement.style.setProperty(
"--target-half-width",
targetHalfWidth + "px"
);
}
Currently, I'm facing an issue where the select element does not open when clicked, but it does work properly when using the enter and up/down buttons.
I suspect that the problem might be related to the event.stopPropagation() function, as when I remove that line, the select element's 'open' class gets added (as seen in the inspect tool). However, removing the event.stopPropagation() line also results in the dropdown menu closing immediately.
My goal is to find a solution that allows me to open the select elements without closing the dropdown menu. I would greatly appreciate any insights or suggestions on how to achieve this.
Here is the relevant HTML code (html file code) for reference. Thank you for your help!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link
rel="stylesheet"
id="hello-elementor-child-style-css"
href="https://fs.codelinden.com/wp-content/themes/hello-child/style.css?ver=3"
media="all"
/>
<link rel="stylesheet" href="header.css" />
<script src="header.js" defer></script>
<!-- single-menu dropdown style -->
<link rel="stylesheet" href="single-menu-dropdown.css" />
<!-- custom form element style (nice-select) -->
<link rel="stylesheet" href="form.css" />
<!-- nice-select style-->
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/jquery-nice-select/1.1.0/css/nice-select.min.css"
integrity="sha512-CruCP+TD3yXzlvvijET8wV5WxxEh5H8P4cmz0RFbKK6FlZ2sYl3AEsKlLPHbniXKSrDdFewhbmBK5skbdsASbQ=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
/>
<title>Header</title>
</head>
<body>
<header class="header HruDj">
<div class="nav-dropdown target-id DeYlt" id="target_id1">
<p>toggle dropdown</p>
<!-- single-menu dropdown container -->
<div id="menu_id1" class="menu-id">
<div class="form-field full-width">
<input
type="hidden"
name="form_my_contact_form"
value="1"
/>
<label for="product_type">Product Type</label>
<select
name="product_type"
id="product_type"
required=""
>
<option value="book">Book</option>
<option value="movie">Movie</option>
<option value="music">Music</option>
<option value="" disabled="">
Select a product type
</option>
</select>
</div>
</div>
</div>
<div class="nav-dropdown target-id DeYlt" id="target_id2">
<p>toggle dropdown</p>
<!-- single-menu dropdown container -->
<div id="menu_id2" class="menu-id">
<div class="form-field full-width">
<input
type="hidden"
name="form_my_contact_form"
value="1"
/>
<label for="product_type">Product Type</label>
<select
name="product_type"
id="product_type"
required=""
>
<option value="book">Book</option>
<option value="movie">Movie</option>
<option value="music">Music</option>
<option value="" disabled="">
Select a product type
</option>
</select>
</div>
</div>
</div>
</div>
</header>
<!-- jquery cdn -->
<script src="popup/jquery/jquery.min.js"></script>
<!-- single-menu dropdown script -->
<script src="single-menu-dropdown.js"></script>
<!-- nice-select script -->
<script
src="https://cdnjs.cloudflare.com/ajax/libs/jquery-nice-select/1.1.0/js/jquery.nice-select.min.js"
integrity="sha512-NqYds8su6jivy1/WLoW8x1tZMRD7/1ZfhWG/jcRQLOzV1k1rIODCpMgoBnar5QXshKJGV7vi0LXLNXPoFsM5Zg=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
></script>
<!-- initialize dropdown and select -->
<script>
// Call singleMenu function for each menu
singleMenu("target_id1", "menu_id1", false);
singleMenu("target_id2", "menu_id2", false);
$(document).ready(function () {
// Apply the niceSelect plugin to all select elements
$("select").niceSelect();
});
</script>
</body>
</html>
So I was able to arrive at the desired result but with some small changes.
There didn't seem to be any approach with the event.stopPropagation()
that would work consistently.
What you can do is that instead of setting the whole menu parent as the click handler set it to work on the more direct target instead.
This means that instead of using
<div class="nav-dropdown target-id DeYlt" id="target_id1">
<p>toggle dropdown</p>
You would want to add the target id to the p tag inside.
<div class="nav-dropdown target-id DeYlt">
<p id="target_id1">toggle dropdown</p>
Same for the menu_id2 and target_id2.
This will require a small change in the javascript.
Change the click handler that closes the menu when clicking outside to handle targetElement.parentElement
instead to account for the target being within the menu now.
// Close menu if clicked outside of container
document.addEventListener("click", (event) => {
if (!targetElement.parentElement.contains(event.target)) {
show = false;
menuElement.style.display = "none";
targetElement.classList.remove("active");
}
});
Get rid of the menu stop propagation item as it's no longer needed.
menuElement.addEventListener("click", function (event) {
event.stopPropagation();
All in all, here's what an updated code from your question looks like which should work as expected.
function singleMenu(targetId, menuId, show = false) {
const targetElement = document.getElementById(targetId);
const menuElement = document.getElementById(menuId);
// Initial state
if (show) {
// show dropdown
menuElement.style.display = "block";
targetElement.classList.add("active");
} else {
// hide dropdown
menuElement.style.display = "none";
targetElement.classList.remove("active");
}
// Toggle menu visibility when target element is clicked
targetElement.addEventListener("click", () => {
show = !show;
if (show) {
// show dropdown
menuElement.style.display = "block";
targetElement.classList.add("active");
} else {
// hide dropdown
menuElement.style.display = "none";
targetElement.classList.remove("active");
}
});
// Close menu if clicked outside of container
document.addEventListener("click", (event) => {
if (!targetElement.parentElement.contains(event.target)) {
show = false;
menuElement.style.display = "none";
targetElement.classList.remove("active");
}
});
// Prevent menu from closing when clicked inside the menu element
/* menuElement.addEventListener("click", function (event) {
event.stopPropagation();
});*/
// Calculate half of the targetElement width
const targetHalfWidth = targetElement.offsetWidth / 2;
// Set a CSS variable with the half width value
targetElement.style.setProperty(
"--target-half-width",
targetHalfWidth + "px"
);
}
singleMenu("target_id1", "menu_id1", false);
singleMenu("target_id2", "menu_id2", false);
$(document).ready(function () {
// Apply the niceSelect plugin to all select elements
$("select").niceSelect();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link
rel="stylesheet"
id="hello-elementor-child-style-css"
href="https://fs.codelinden.com/wp-content/themes/hello-child/style.css?ver=3"
media="all"
/>
<link rel="stylesheet" href="header.css" />
<script src="header.js" defer></script>
<!-- single-menu dropdown style -->
<link rel="stylesheet" href="single-menu-dropdown.css" />
<!-- custom form element style (nice-select) -->
<link rel="stylesheet" href="form.css" />
<!-- nice-select style-->
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/jquery-nice-select/1.1.0/css/nice-select.min.css"
integrity="sha512-CruCP+TD3yXzlvvijET8wV5WxxEh5H8P4cmz0RFbKK6FlZ2sYl3AEsKlLPHbniXKSrDdFewhbmBK5skbdsASbQ=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
/>
<title>Header</title>
</head>
<body>
<header class="header HruDj">
<div class="nav-dropdown target-id DeYlt">
<p id="target_id1">toggle dropdown</p>
<!-- single-menu dropdown container -->
<div id="menu_id1" class="menu-id">
<div class="form-field full-width">
<input
type="hidden"
name="form_my_contact_form"
value="1"
/>
<label for="product_type">Product Type</label>
<select
name="product_type"
id="product_type"
required=""
>
<option value="book">Book</option>
<option value="movie">Movie</option>
<option value="music">Music</option>
<option value="" disabled="">
Select a product type
</option>
</select>
</div>
</div>
</div>
<div class="nav-dropdown target-id DeYlt">
<p id="target_id2">toggle dropdown</p>
<!-- single-menu dropdown container -->
<div id="menu_id2" class="menu-id">
<div class="form-field full-width">
<input
type="hidden"
name="form_my_contact_form"
value="1"
/>
<label for="product_type">Product Type</label>
<select
name="product_type"
id="product_type"
required=""
>
<option value="book">Book</option>
<option value="movie">Movie</option>
<option value="music">Music</option>
<option value="" disabled="">
Select a product type
</option>
</select>
</div>
</div>
</div>
</div>
</header>
<!-- jquery cdn -->
<script src="popup/jquery/jquery.min.js"></script>
<!-- single-menu dropdown script -->
<script src="single-menu-dropdown.js"></script>
<!-- nice-select script -->
<script
src="https://cdnjs.cloudflare.com/ajax/libs/jquery-nice-select/1.1.0/js/jquery.nice-select.min.js"
integrity="sha512-NqYds8su6jivy1/WLoW8x1tZMRD7/1ZfhWG/jcRQLOzV1k1rIODCpMgoBnar5QXshKJGV7vi0LXLNXPoFsM5Zg=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
></script>
<!-- initialize dropdown and select -->
<script>
// Call singleMenu function for each menu
</script>
</body>
</html>
Note: moved the following code to the javascript portion only to make the code snippet runnable, that is not a required modification.
singleMenu("target_id1", "menu_id1", false);
singleMenu("target_id2", "menu_id2", false);
$(document).ready(function () {
// Apply the niceSelect plugin to all select elements
$("select").niceSelect();
});