I am creating a web app that searches for events using an event api. I have a filter navbar where the user can enter certain filters (date-range, event category, distance, etc). I put a click handler on one of the navbar elements(category) so that it displays the hidden dropdown menu upon click. If the same navbar element is clicked again or any of its children (hidden dropdown elements), the dropdown menu should disappear. I used jQuery to change the text of the navbar element to the text of the category that was clicked from the dropdown menu. However, when I try to click the navbar element again, it is locked and I can't open it. I can't seem to find what is wrong with my code. Hopefully some additional eyes can help me spot it.
Here is the code.
<nav class="filter__container">
<ul class="filter">
<li class="filter__category">Category
<div class="filter__category__choices">
<p class="filter__category__choice">Art</p>
<p class="filter__category__choice">Music</p>
<p class="filter__category__choice">Sports</p>
<p class="filter__category__choice">Festival</p>
<p class="filter__category__choice">Miscellaneous</p>
</div>
</li>
<li class="filter__submit">Apply Filters</li>
</ul>
</nav>
const categoryFilter = $(".filter__category");
let categoryOpen = false;
categoryFilter.click((e) => {
if(e.target !== categoryFilter[0]) {
$(".filter__category").text($(e.target).text());
}
if(categoryOpen) {
categoryPopup.slideUp();
categoryOpen = false;
}
else {
categoryPopup.slideDown();
categoryOpen = true;
}
});
Related
I am learning JavaScript and am trying to wrap my head around creating a nav megamenu. Basically, each button has a div after it that contains the megamenu content. Hovering OR clicking on the button adds a class to the dropdown (I could also use a show/hide within the JS, not sure which is better). The class adds opacity and visibility to the div.
I've created a "container" variable so it's only listening within the navbar, and then looping through the buttons to add the listener with nextElementSibling, but I can't seem to get it working.
Markup:
<nav id="main-nav">
<div class="nav-content">
<div class="nav-item">
<button class="nav-dropdown">Services</button>
<div class="nav-submenu">
(Links)
</div>
</div>
<div class="nav-item">
<button class="nav-dropdown">Locations</button>
<div class="nav-submenu">
(Links)
</div>
</div>
<div class="nav-item">
<button class="nav-dropdown">About Us</button>
<div class="nav-submenu">
(Links)
</div>
</div>
</div>
</nav>
JS:
( function() {
// initiating vars
var desktop, mobile, container;
// Breakpoints
desktop = window.matchMedia("(min-width: 769px)");
mobile = window.matchMedia("(max-width: 768px)");
// Target only navbar
container = document.getElementById( 'main-nav' );
if ( ! container ) {
return;
}
// Desktop dropdown click controls
if (desktop.matches) {
// only look for .nav-dropdown class
const desktopParents = container.querySelectorAll(".nav-dropdown");
// get each instance of .nav-dropdown
for (const desktopParent of desktopParents) {
// if a button is clicked, add class to that button's dropdown only
document.addEventListener('click', function(event) {
// getting correct dropdownby targeting next sibling of button (div.nav-submenu)
var dropdown = desktopParent.nextElementSibling;
var desktopParent = desktopParent.contains(event.target);
// adding and removing show dropdown class
if (dropdown.classList.contains('nav-active')) {
dropdown.classList.remove('nav-active');
} else {
dropdown.classList.add('nav-active');
}
});
}
}
}() );
Your event listener is attached to the whole document so the event is triggered when someone clicks anywhere on the page. Change document to your button variable desktopParent for the events to be triggered when the button is clicked. You can refer to the button element using this inside the event listener's callback function.
desktopParent.addEventListener('click', function(event) {
// getting correct dropdownby targeting next sibling of button (div.nav-submenu)
var dropdown = this.nextElementSibling;
var desktopParent = this.contains(event.target);
...
}
Here is an example
I am using this script to open a drop down menu and then close it when anything else but the trigger is clicked. Now I am trying to add a second drop down to another area on the page and repeat the script but it is breaking.
For instance, I click button A (Gravatar), and drop down A opens.
However when I add the second script and click button B (category) to open drop down B, down down A stays open.
Also adding the second script breaks the drop down close function of the first script.
Here is the script:
<script>
function openAccount(event) {
event.stopPropagation();
document.getElementById("gravatar").classList.toggle("open");
}
window.onclick = function(event) {
document.getElementById("gravatar").classList.remove("open");
}
</script>
<script>
function openCategory(event) {
event.stopPropagation();
document.getElementById("category").classList.toggle("open");
}
window.onclick = function(event) {
document.getElementById("gravatar").classList.remove("open");
}
</script>
<li class="gravatar">
<a href="#" class="dropbtn" onclick="openAccount(event)">
<img src="<?php echo $gravatar; ?>" alt="" />
<span class="fa fa-icon fa-caret-down"></span>
</a>
<ul class="dropdown" id="gravatar">
<li class="header">
<?php echo $user['email']; ?>
</li>
</ul>
</li>
<div class="category">
Properties<span class="fa fa-icon fa-caret-down"></span>
<div id="category">Test</div>
</div>
Goals:
Multiple drop down menus on different parts of the page.
On click opens drop down.
Click on anywhere else on the page closes the drop down.
On click also closes any previously opened menu.
I'd do something really simple like putting a data* attribute on the element that's clicked on that contains the ID of the element to show or hide, e.g.
// Toggle hidden class on/off
function toggleVis(event) {
// Stop click on element bubbling (to body)
event.stopPropagation();
// Get target element
var el = document.getElementById(this.dataset.id);
// If non-target elements are visible, hide them
hideAll(el);
// Toggle target
el.classList.toggle('hidden');
}
// Hide all, excluding passed element
function hideAll(el) {
Array.from(document.querySelectorAll('ul:not(.hidden)')).forEach(function(node){
if (el != node) node.classList.add('hidden');
});
}
// Attach listeners
window.onload = function() {
// Add to linkLike spans
Array.from(document.querySelectorAll('.linkLike')).forEach(function(node) {
node.addEventListener('click', toggleVis, false);
});
// Add hideAll listener to wndow
window.addEventListener('click', hideAll, false);
// Run hideAll
hideAll();
}
/* style span like link */
.linkLike {
text-decoration: underline;
cursor: pointer;
}
/* class to hide element */
.hidden {
visibility: hidden;
}
<ul id="a"><li>A</ul>
<ul id="b"><li>B</ul>
<ul id="c"><li>C</ul>
<ul id="d"><li>D</ul>
<div><span class="linkLike" data-id="a">Toggle A</span></div>
<div><span class="linkLike" data-id="b">Toggle B</span></div>
<div><span class="linkLike" data-id="c">Toggle C</span></div>
<div><span class="linkLike" data-id="d">Toggle D</span></div>
Of course there are other ways to do the association, but ID is simple, explicit and doesn't depend on document layout or formatting.
I want to have my menu closed when the user clicks outside the menu, not only outside the navbar element. Because I have more collapses in my menu, this solution did not work for me: How to close an open collapsed navbar when clicking outside of the navbar element in Bootstrap 3?
The menu disapeares when I click outside the menu, but when I click on the link with a dropdown, the whole menu collapses.
<div class="collapse navbar-collapse nav-mobile" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="list-group panel">
Webshop
<ul class="collapse" id="submenu-1">
Industriƫle verpakkingen
Promotionele verpakkingen
Gelamineerde verpakkingen
Enveloppen &verzend verpakkingen
Medische verpakkingen
Co-packing
</ul>
</li>
</ul>
You can use this to collapse if not clicking a link: fiddle
$(document).click(function(e) {
if (!$(e.target).is('a')) {
$('.collapse').collapse('hide');
}
});
another alternative, you can add code below :
<script>
$(document).ready(function(){
$(".list-group ").hover(
function() {
$('.collapse', this).stop( true, true ).slideDown("fast");
$(this).toggleClass('open');
},
function() {
$('.collapse', this).stop( true, true ).slideUp("fast");
$(this).toggleClass('open');
}
);
});
</script>
another example : dtc-eng
Here is my take on this:
$(document).on('click', function(event){
var $clickedOn = $(event.target),
$collapsableItems = $('.collapse'),
isToggleButton = ($clickedOn.closest('.navbar-toggle').length == 1),
isLink = ($clickedOn.closest('a').length == 1),
isOutsideNavbar = ($clickedOn.parents('.navbar').length == 0);
if( (!isToggleButton && isLink) || isOutsideNavbar ) {
$collapsableItems.each(function(){
$(this).collapse('hide');
});
}
});
This solution handles:
single page website/applications (and will work on multi-pages too).
clicks on:
.navbar-toggle elements (could be <buttons> or <a>, and it handles clicks on potential inner elements like <span> or <strong> or whatever).
on simple <a> elements (again, it handles clicks on inner elements).
just outside some particular parent (ie. .navbar).
multiple collapsable (.collapse) elements that might be open (indistinct to where they are placed in the DOM).
Not enough for you? No problem. You can customize most of the selectors passed to jQuery (document, .collapse, .navbar, etc) to suit your needs or even add more conditions.
I have a homepage with 4 buttons. When hovered over a button, a menu appears behind the buttons. When you hover over another button, a different colored menu appears in it's place.
Currently, I can get the buttons to show the menus, but when I hover onto the menus (and hover off the button) I lose the menu.
Here's my simple code:
Jquery at top:
$(".mybutton").hover(
function () {
$(".mybox").fadeIn();
},
function () {
$(".mybox").fadeOut();
}
);
$(".mybutton2").hover(
function () {
$(".mybox2").fadeIn();
},
function () {
$(".mybox2").fadeOut();
}
);
And my HTML:
<div class="mybox">
<div style="position: absolute;">
Item 1
Item 2
</div>
</div>
<div class="buttons">
<div class="mybutton">
/* Button image here */
</div>
<div class="mybutton2">
/* Button 2 image here */
</div>
</div>
So I need some way to keep the box that fades in active when it is hovered over. I was thinking of not doing the callback for the fadeout, and somehow only doing the fadeout if they fade off the .mybox DIV or if they hover over another button. But it's a little unclear to me how to accomplish that.
Thanks in advance.
you need to include your menu and the button inside a container and have a hover event on the container. this way your menu will be visible as long as you're hovering over the container.
here's what you need to do.
declare the container like this with your menu and button both inside it.
<div id='container'>
<div class="mybox box">
<div style="position: absolute;">
Item 1
Item 2
</div>
</div>
<div class="buttons">
<div class="mybutton">
/* Button image here */
</div>
</div>
</div>
here's what you need to do in jquery.
$(document).ready(function() {
$("#container").hover(
function() {
console.log($(".mybox").fadeIn());
$(".mybox").fadeIn();
},
function() {
$(".mybox").fadeOut();
}
);
});
here's a working JSFIDDLE with 2 buttons
It's because you're no longer hovering over the button and instead going to a different element "mybox" so you could rearrange the html structure for it to work by keeping the menu in the button class like so:
<div class="buttons">
<div class="mybutton">
/* Button image here */
<div class="mybox">
<div style="position: absolute;">
Item 1
Item 2
</div>
</div>
</div>
</div>
this should keep the menu active as long as the curser is in there.
I don't recommend this as a UI design pattern for various reasons (one of them being the complexity of implementing it); you could instead consider changing it so that the menu appears when the user clicks.
Having said that, here's a way to do it. Get rid of your existing fadeOut() calls and add this:
$("body").on("mousemove", function(e) {
var $hovered = $(e.target);
var $myButton = $(".myButton");
var $box = $(".myBox");
if ( $hovered.is( $myButton ) ) return;
if ( $hovered.is( $box ) ) return;
if ( $.contains( $box.get(0), $hovered ) ) return;
$box.fadeOut();
});
...and similar for button2. The basic principle is this - whenever the mouse moves, we check whether the mouse is hovering over the button, or the box, or over an element contained in the box (using $.contains()). If not, we hide the box.
I'm trying to make a dropdown very similar to dropbox dashboard, where if you click the username a flyout menu appears. Clicking the username again will close the flyout (toggling it every time you click).
The one caveat is that clicking anywhere except on the flyout itself will also close it.
So far, I have it working almost, but not 100%. If you click on the actual 'body' element directly, it will close the flyout as it should. By this I mean my website has a .wrapper element which doesn't take up the full height of the page. Theres a thin strip down at the bottom with no actual element covering it, only the <body> tag. Any place where .wrapper or some other element takes up space (even a 100% width invisible wrapper), it will not close the window if you click on anything where there is an element (besides body).
javascript:
// FLYOUT menu
$flyout = $('.flyout ul'),
flyoutDuration = 120;
$('.flyout h3 a').click(function(e) {
e.preventDefault();
$flyout.fadeToggle(flyoutDuration);
});
$(document).on('click',function(e) {
if ( $(e.target).parents($flyout).length === 0 ) {
$flyout.fadeOut(flyoutDuration);
}
});
HTML
<body>
<div class="blackbar">
<div class="container clearfix">
<div class="icon logo"></div>
<div class="flyout">
<h3>
Welcome back, username
</h3>
<div class="menu">
<ul>
<li><div class="users"></div>Users</li>
<li><div class="groups"></div>Groups</li>
<li><div class="configuration"></div>Configuration</li>
<li><div class="logout"></div>Logout</li>
</ul>
</div>
</div>
</div>
</div>
<div class="wrapper">
<! -- content here -->
</div>
</body>
The expected behavior should be any element you click on that isnt a descendent of .flyout should close the window (including .blackbar, the logo, etc)
To be honest - when I am doing something like this and I do not want clicks inside of the "box" to close the element - I prevent clicks from bubbling.
// FLYOUT menu
$flyout = $('.flyout ul'),
flyoutDuration = 120;
$('.flyout h3 a').click(function(e) {
e.preventDefault();
$flyout.fadeToggle(flyoutDuration);
});
$('.flyout').click(function(e) {
e.stopPropagation();
e.preventDefault();
});
$(document).on('click',function(e) {
if(flyout-open) {
$flyout.fadeOut(flyoutDuration);
}
});
Jquery Menu Click - on click anywhere on body will close the menu
In my case i wanted to close a drop down menu if its clicked primary link or outside the link i.e. anywhere on the html/body
http://jsfiddle.net/austinnoronha/k2Lwj/
var toggleClick = function(){
var divObj = $(this).next();
var nstyle = divObj.css("display");
if(nstyle == "none"){
divObj.slideDown(false,function(){
$("html").bind("click",function(){
divObj.slideUp();
$("html").unbind("click");
});
});
}
};
$(".clickme").click(toggleClick);