I'm currently using this following script to operate my dropdown menu so that when a user clicks the menu item the drop appears and they can currently click anywhere outside the screen to make the menu disappear but i was wondering how id go about adding functionality to make the dropdown disappear by clicking the menu item again (while keeping the option to click anywhere outside the item). Is this hard to implement?
$(document).ready(function() {
/* for keeping track of what's "open" */
var activeClass = 'dropdown-active', showingDropdown, showingMenu, showingParent;
/* hides the current menu */
var hideMenu = function() {
if(showingDropdown) {
showingDropdown.removeClass(activeClass);
showingMenu.hide();
}
};
/* recurse through dropdown menus */
$('.dropdown').each(function() {
/* track elements: menu, parent */
var dropdown = $(this);
var menu = dropdown.next('div.dropdown-menu'), parent = dropdown.parent();
/* function that shows THIS menu */
var showMenu = function() {
hideMenu();
showingDropdown = dropdown.addClass('dropdown-active');
showingMenu = menu.show();
showingParent = parent;
};
/* function to show menu when clicked */
dropdown.bind('click',function(e) {
if(e) e.stopPropagation();
if(e) e.preventDefault();
showMenu();
});
/* function to show menu when someone tabs to the box */
dropdown.bind('focus',function() {
showMenu();
});
});
/* hide when clicked outside */
$(document.body).bind('click',function(e) {
if(showingParent) {
var parentElement = showingParent[0];
if(!$.contains(parentElement,e.target) || !parentElement == e.target) {
hideMenu();
}
}
});
});
Here is my attempt based on the answer below:
/* function to show menu when clicked */
dropdown.bind('click',function(e) {
if (!dropdown.data('open')) {
dropdown.data'(open', true);
// open menu
} else {
dropdown.data('open', false);
// close menu
}
});
Although im getting errors so i assume ive played it in incorrectly or perhaps overwritten something? This is just a section from the code above as it's the only section ive edited with this answer.
Thanks
What you can do is use jQuery.data() and have a variable called open for example which is true or false depending on the state. So you could be something like this.
if (!dropdown.data('open')) {
dropdown.data'(open', true);
// open menu
} else {
dropdown.data('open', false);
// close menu
}
Related
I'm stuck while implementing drop down for each individual list item on focus/hover on any of them, now on hovering over any single list item all dropdowns are getting displayed.
This is my pen: https://codepen.io/apeandme/pen/GRZOxJQ
and the JS:
// main navigation interaction
'use strict';
var navbar;
var triggerContainer;
var triggerLink;
var toggleButton;
var lastMenuItem;
var mouseOutTimer; // timer used to delay hiding of menu after mouse leaves
var mouseOutHideDelay = 0; // time (in ms) before menu is closed after mouse leaves
var menuVisible = false;
var i = 0;
window.addEventListener('DOMContentLoaded', function(e) {
navbar = document.querySelector('nav');
triggerContainer = document.querySelectorAll('nav > ul > li.with-dd');
triggerLink = document.querySelectorAll('nav > ul > li.with-dd > a');
toggleButton = document.querySelectorAll('nav > ul > li.with-dd > .toggle-button');
lastMenuItem = document.querySelectorAll('nav > ul > li.with-dd > ul > li:last-of-type > a');
// Show the menu on mouse hover of the trigger
triggerContainer.forEach(item => {
item.addEventListener('mouseover', function(e) {
showMenu();
clearTimeout(mouseOutTimer);
});
});
// Hide the menu when mouse hover leaves both the trigger and menu fly-out, but only after a short delay to help people jerky mouse movements (like those using head/eye trackers)
triggerContainer.forEach(item => {
item.addEventListener('mouseout', function(e) {
mouseOutTimer = setTimeout(function() {
hideMenu();
}, mouseOutHideDelay);
});
});
// Hide the menu when the user tabs out of it
triggerContainer.forEach(item => {
item.addEventListener('keydown', triggerKeydownHandler);
});
// Toggle the menu when the trigger is activated
toggleButton.forEach(item => {
item.addEventListener('click', toggleMenu);
});
// Close the menu when the user activates something outside the navbar.
document.body.addEventListener('click', handleBodyClick);
});
/**
Menu visibility
**/
function showMenu() {
triggerLink.forEach(item => {
item.setAttribute('aria-expanded', true);
});
toggleButton.forEach(item => {
item.setAttribute('aria-expanded', true);
});
menuVisible = true;
}
function hideMenu() {
triggerLink.forEach(item => {
item.setAttribute('aria-expanded', false);
});
toggleButton.forEach(item => {
item.setAttribute('aria-expanded', false);
});
menuVisible = false;
}
function toggleMenu() {
if (menuVisible) {
hideMenu();
} else {
showMenu();
}
}
/**
Event handlers
*/
function handleBodyClick(e) {
if (!navbar.contains(e.target)) {
hideMenu();
}
}
function triggerKeydownHandler(e) {
// Hide the menu a keyboard user tabs out of it or presses Escape
if ((e.key === 'Tab' && !e.shiftKey && e.target === lastMenuItem) || e.key == 'Escape') {
hideMenu();
// Move focus back to the menu toggle button if Escape was pressed
if (e.key == 'Escape') {
toggleButton.focus();
}
}
}
Instead of opening all the dropdowns at once when one is hovered, you need to pass the 'hover' event to the function, in order to know which one of the triggers were hovered :
triggerContainer.forEach((item) => {
item.addEventListener("mouseover", function (event) { // catch the 'hover' event
showMenu(event); // pass the event to your function
clearTimeout(mouseOutTimer);
});
});
function showMenu(event) {
event.target.setAttribute("aria-expanded", true); // know which element whas hovered
event.target.nextElementSibling.setAttribute("aria-expanded", true); // open the corresponding dropdown
menuVisible = true;
}
Thanks for the help. Have changed the code , for both showMenu and hideMenu and for mouseover and mouseout events, now individual drop downs are showing on hovering over single menu item, but the dropdowns are hiding soon enough I'm reaching to the last menu item inside the dropdown. Am I missing anything and currently this error is in the console whenever hovering over the last menu item inside the drop-down-
Uncaught TypeError: Cannot read property 'setAttribute' of null
at hideMenu (main.js:68)
at main.js:38
//main navigation interaction
"use strict";
var navbar;
var triggerContainer;
var triggerLink;
var toggleButton;
var lastMenuItem;
var mouseOutTimer; // timer used to delay hiding of menu after mouse leaves
var mouseOutHideDelay = 1000; // time (in ms) before menu is closed after mouse leaves
var menuVisible = false;
window.addEventListener("DOMContentLoaded", function (e) {
navbar = document.querySelector("nav");
triggerContainer = document.querySelectorAll("nav > ul > li.with-dd");
triggerLink = document.querySelectorAll("nav > ul > li.with-dd > a");
toggleButton = document.querySelectorAll(
"nav > ul > li.with-dd > .toggle-button"
);
lastMenuItem = document.querySelectorAll(
"nav > ul > li.with-dd > ul > li:last-of-type > a"
);
// Show the menu on mouse hover of the trigger
triggerContainer.forEach((item) => {
item.addEventListener("mouseover", function (e) {
showMenu(e);
clearTimeout(mouseOutTimer);
});
});
// Hide the menu when mouse hover leaves both the trigger and menu fly-out, but only after a short delay to help people jerky mouse movements (like those using head/eye trackers)
triggerContainer.forEach((item) => {
item.addEventListener("mouseout", function (e) {
mouseOutTimer = setTimeout(function () {
hideMenu(e);
}, mouseOutHideDelay);
});
});
// Hide the menu when the user tabs out of it
triggerContainer.forEach((item) => {
item.addEventListener("keydown", triggerKeydownHandler);
});
// Toggle the menu when the trigger is activated
toggleButton.forEach((item) => {
item.addEventListener("click", toggleMenu);
});
// Close the menu when the user activates something outside the navbar.
document.body.addEventListener("click", handleBodyClick);
});
/**
Menu visibility
**/
function showMenu(e) {
e.target.setAttribute("aria-expanded", true);
e.target.nextElementSibling.setAttribute("aria-expanded", true);
menuVisible = true;
}
function hideMenu(e) {
e.target.setAttribute("aria-expanded", false);
e.target.nextElementSibling.setAttribute("aria-expanded", false);
menuVisible = false;
}
function toggleMenu() {
if (menuVisible) {
hideMenu(e);
} else {
showMenu(e);
}
}
/**
Event handlers
*/
function handleBodyClick(e) {
if (!navbar.contains(e.target)) {
hideMenu();
}
}
function triggerKeydownHandler(e) {
// Hide the menu a keyboard user tabs out of it or presses Escape
if (
(e.key === "Tab" && !e.shiftKey && e.target === lastMenuItem) ||
e.key == "Escape"
) {
hideMenu();
// Move focus back to the menu toggle button if Escape was pressed
if (e.key == "Escape") {
toggleButton.focus();
}
}
}
I'm currently studying a full stack course and my modal isn't behaving as expected
I'm a bit lost on what to do as I can't find any documentation anywhere and while clicking on the close button or pressing ESC works, clicking outside of the box doesn't.
The following code is how it has been suggested I approach the issue but, it doesn't work. I've honestly stared at this for about an hour and just can't connect the dots on what is (not) happening? Please excuse all the commenting and additional code as I'm still learning so, it's how I'm able to follow what's going on:
function showModal() {
var $modalContainer = document.querySelector('#modal-container');
$modalContainer.classList.add('is-visible');
}
function hideModal() {
var $modalContainer = document.querySelector('#modal-container');
$modalContainer.classList.remove('is-visible');
}
//modal IFFE
document.querySelector('#modal-button').addEventListener('click', () => {
showModal();
});
//-- show modal --
function showModal(title, text) {
var $modalContainer = document.querySelector('#modal-container');
//Selects the element with the associated id
// Clear all content for the selected element
$modalContainer.innerHTML = '';
var modal = document.createElement('div'); //creates a div element withing selected element
modal.classList.add('modal'); //assigns new class to the div element
// Add the new modal content
var closeButtonElement = document.createElement('button'); //creates the close button
closeButtonElement.classList.add('modal-close'); //assigns a class to the new (close) button
closeButtonElement.innerHTML = "×"; //inserts text within the new(close) button
closeButtonElement.addEventListener('click', hideModal);
var titleElement = document.createElement('h1');
titleElement.innerText = title;
var contentElement = document.createElement('p');
contentElement.innerText = text;
modal.appendChild(closeButtonElement);
modal.appendChild(titleElement);
modal.appendChild(contentElement);
$modalContainer.appendChild(modal);
$modalContainer.classList.add('is-visible');
}
document.querySelector('#modal-button').addEventListener('click', () => {
showModal('PokéMon', 'Here is all of the info about your PokéMon');
});
window.addEventListener('keydown', (e) => {
var $modalContainer = document.querySelector('#modal-container');
if (e.key === 'Escape' && $modalContainer.classList.contains('is-
visible')) {
hideModal();
}
});
$modalContainer.addEventListener('click', (e) => {
var target = e.target;
if (target === $modalContainer) {
hideModal();
}
});
Expected result: User clicks outside of the modal (on the container) and the modal closed.
Current result: No change in state, modal remains active and visible. Only by clicking on the close button (x) or by pressing ESC is the desired result achievable.
By Looking at this code I am not sure what is actually supposed to make the modal visible or hide it. Without access to your css (if you have any). I am assuming that all you are doing is adding and removing the class .is-visible from the #modal-container element.
I would suggest that you apply this class to the modal itself, and then you could toggle this class on and off,
Modify your code to do this by doing something like this (added on top of your code):
function showModal() {
var $modalContainer = document.querySelector('#modal-container');
$modalContainer.classList.add('is-visible');
document.querySelector('.modal').classList.remove('hide-el')
}
function hideModal() {
var $modalContainer = document.querySelector('#modal-container');
$modalContainer.classList.remove('is-visible');
document.querySelector('.modal').classList.add('hide-el')
}
Where hide-el in your css is:
.hide-el {
display: none;
}
You could also modify your code to appply the is-visible class to your modal element. You should always try to attach the class/id to the element you want to manipulate if you have that option.
Or if you do not have access to a css file:
document.querySelector('.modal').style.display = "none"
and
document.querySelector('.modal').style.display = "block"
Also, your code seems very verbose, was this boilerplate part of the assignment?
heres a working example: https://codepen.io/mujakovic/pen/zVJRKG
The code was in the incorrect place in the end and should have looked something like this:
modal.appendChild(closeButtonElement);
modal.appendChild(titleElement);
modal.appendChild(contentImage);
modal.appendChild(contentHeight);
modal.appendChild(contentElement);
$modalContainer.appendChild(modal);
$modalContainer.classList.add('is-visible');
$modalContainer.addEventListener('click', (e) => { //listening for an event (click) anywhere on the modalContainer
var target = e.target;
console.log(e.target)
if (target === $modalContainer) {
hideModal();
}
});
};
window.addEventListener('keydown', (e) => { //listening for an event (ESC) of the browser window
var $modalContainer = document.querySelector('#modal-container');
if (e.key === 'Escape' && $modalContainer.classList.contains('is-visible')) {
hideModal();
}
});
This is because the action was originally being called on page load and targeted within the window instead of being called within the container and being loaded when the modal loads.
Thank for your help
I am working on closing toggle menu for mobiles and having a small problem. So what i want is when the toggle menu is active, user to be able to close it by touching somewhere on the screen on his device. I almost got it working, but when closed the basket in the header disappears and the menu doesn't retrieve to a hamburger icon. I am working on Wordpress website, just to notice.
I guess the problem comes from this: aria-expanded="true" , because the default value should be false after the user has closed it.
So my website is:
https://www.ngraveme.com/bg
my JQuery code is:
jQuery(document).ready(function($) {
var $menu = $('.menu');
$('.menu-toggle').click(function() {
$menu.toggle();
});
$(document).mouseup(function(e) {
if (!$menu.is(e.target) // if the target of the click isn't the container...
&&
$menu.has(e.target).length === 0) // ... nor a descendant of the container
{
$menu.hide();
}
});
});
and the original js code written from the theme i am using in wordpress is:
/**
* navigation.js
*
* Handles toggling the navigation menu for small screens.
* Also adds a focus class to parent li's for accessibility.
* Finally adds a class required to reveal the search in the handheld footer bar.
*/
(function() {
// Wait for DOM to be ready.
document.addEventListener('DOMContentLoaded', function() {
var container = document.getElementById('site-navigation');
if (!container) {
return;
}
var button = container.querySelector('button');
if (!button) {
return;
}
var menu = container.querySelector('ul');
// Hide menu toggle button if menu is empty and return early.
if (!menu) {
button.style.display = 'none';
return;
}
button.setAttribute('aria-expanded', 'false');
menu.setAttribute('aria-expanded', 'false');
menu.classList.add('nav-menu');
button.addEventListener('click', function() {
container.classList.toggle('toggled');
var expanded = container.classList.contains('toggled') ? 'true' : 'false';
button.setAttribute('aria-expanded', expanded);
menu.setAttribute('aria-expanded', expanded);
});
// Add class to footer search when clicked.
document.querySelectorAll('.storefront-handheld-footer-bar .search > a').forEach(function(anchor) {
anchor.addEventListener('click', function(event) {
anchor.parentElement.classList.toggle('active');
event.preventDefault();
});
});
// Add focus class to parents of sub-menu anchors.
document.querySelectorAll('.site-header .menu-item > a, .site-header .page_item > a, .site-header-cart a').forEach(function(anchor) {
var li = anchor.parentNode;
anchor.addEventListener('focus', function() {
li.classList.add('focus');
});
anchor.addEventListener('blur', function() {
li.classList.remove('focus');
});
});
// Add an identifying class to dropdowns when on a touch device
// This is required to switch the dropdown hiding method from a negative `left` value to `display: none`.
if (('ontouchstart' in window || navigator.maxTouchPoints) && window.innerWidth > 767) {
document.querySelectorAll('.site-header ul ul, .site-header-cart .widget_shopping_cart').forEach(function(element) {
element.classList.add('sub-menu--is-touch-device');
});
}
});
})();
Try replacing your jQuery code with this:
jQuery(document).ready(function($) {
$(document).mouseup(function(e) {
var $menuContainer = $('.menu');
var $menu = $menu.find('ul');
var $container = $('.site-navigation');
var $button = $container.find('button')
if (!$menuContainer.is(e.target) && $menuContainer.has(e.target).length === 0) {
if ($container.hasClass('toggled')) {
$button.attr('aria-expanded', false);
$menu.attr('aria-expanded', false);
}
}
});
});
It uses the vanilla-js code from the template for hiding/showing the menu, but with jQuery synthax.
I have menu which is open on click and close automatically. I'm not very much into .js and don't know how exactly work.
What I want is this menu to not close automatically. When is open to stay open.
This is the .js
var current_item = 0;
// few settings
var section_hide_time = 400;
var section_show_time = 400;
// jQuery stuff
jQuery(document).ready(function($) {
// Switch section
$("a", '.mainmenu').click(function()
{
if( ! $(this).hasClass('active') ) {
current_item = this;
// close all visible divs with the class of .section
$('.section:visible').fadeOut( section_hide_time, function() {
$('a', '.mainmenu').removeClass( 'active' );
$(current_item).addClass( 'active' );
var new_section = $( $(current_item).attr('href') );
new_section.fadeIn( section_show_time );
} );
}
return false;
});
});
I suppose the part for open/close is in switch section but I'm not sure.
Try using Toggle();, then it won't close until you click the button again.
The "section_show_time" is what is making the menu close automatically. If you take that out of the javascript it should stay open when opened. You might want to take out the "section_hide_time" as well if you don't want it to do anything with out a users click.
I would recommend using completely different javascript to make a open and close toggle button:
In the HTML file:
<button onclick="toggleMenu()"> </button>
<div id="toggleMenu" style="display:none;">
<"menu content here">
</div>
JS:
<script>
function toggleMenu() {
if (document.getElementById("toggleMenu").style.display == "none"){
document.getElementById("toggleMenu").style.display = "block";
} else {
document.getElementById("toggleMenu").style.display = "none";
}
}
</script>
In js you could change out "block" to what ever you want the display to be when the menu is to be visible.
In a web page one "context-menu/subMneu" made up of divs is displayed on hovering over a separate div. This sub-menu is getting closed when user clicked on somewhere else in the document means any mouse click on a document will result in closing of the sub-menu.
In IE8, Chrome12 & Firefox sub-menu is getting closed on left mouse click.
What is happening.In Internet Explorer8 & Chrome12 when we press right mouse button then it is not getting closed. Browser's default menu is getting opened which is a right functionality. But in Mozilla Firefox on clicking right mouse button sub-menu is getting closed.
My problem is why this is happening in Mozilla only and how to resolve it?
Below is the code snippet. I have used detachEvent/deatchEventListener and attachEvent/attachEventListener both because in IE8 does not support deatchEventListener/attachEventListener
Code:
function hideSubMenu(){
doument.getElementById("elem").style.display = none;
if(document.detachEvent){
document.detachEvent('onclick',hideSubMenu);
}
else(document.detachEventListener){
document.detachEventListener('click',hideSubMenu,true);
}
}
#in which Event is attached
function displayMenu(){
if(document.attachEvent){
document.attachEvent('onclick',hideSubMenu);
}
else(document.attachEventListener){
document.attachEventListener('click',hideSubMenu,true);
}
}
Note: only javascript I can use.So request you all to keep suggestions keeping this in mind.
Thanks.
It seems FF triggers click with right mouse button too. You can try something like this:
function hideSubMenu (e) {
var menu = document.getElementById('elem');
if (document.detachEvent) {
menu.style.display = 'none';
document.detachEvent('onclick', hideSubMenu);
} else {
if (e.which === 1) { // Checks left-click
document.removeEventListener('click', hideSubMenu);
menu.style.display = 'none';
}
}
return;
}
function displayMenu () {
if (document.attachEvent) {
document.attachEvent('onclick', hideSubMenu);
} else {
document.addEventListener('click', hideSubMenu, false);
}
return;
}
Working demo in jsFiddle.