I have a fly-out menu on my website, that closes when the user clicks away (anywhere else on the page) using an event listener. I would like to do the same but for accessible user for when they tab away from the fly-out menu. Any help doing this would be appreciated.
configure() {
window.addEventListener('resize', debounce(this.resize.bind(this), 300));
window.addEventListener('blur', () => once(window, 'focus', this.resize.bind(this)));
this.more_btn.addEventListener('click', () => {
if (this.more_list.classList.contains('hidden')) {
setTimeout(() => {
once(window, 'click', () => {
this.container.classList.remove('opened');
this.more_list.classList.add('hidden');
toggleAria(this.more_list, 'aria-expanded');
});
}, 100);
}
this.container.classList.toggle('opened');
this.more_list.classList.toggle('hidden');
toggleAria(this.more_list, 'aria-expanded');
});
this.dropdown.addEventListener('click', () => {
this.toggle();
});
this.resize();
}
you can bind the eventlistener to keyup.
document.addEventListener('keyup', function(event) {
if (event.keyCode == 9) {
// close your menu
}
});
as stated in your question, close menu only when user "tab-away", bind the listener to the menu.
document.getElementById("your_menu_selector").document.addEventListener('keyup', function(event) {...}
your question has already an answer here: How to call function when I press tab key in javascript?
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();
}
}
}
This question already has answers here:
Detect left mouse button press
(5 answers)
Closed 2 years ago.
I have some popups and now they close if i press any mouse button. I need only left mouse to press and close popup
And the 2nd one question. Why esc popup code doesnt work??
const overlayClose = (evt) => {
if (evt.target.classList.contains('popup_active')) {
togglePopup(evt.target);
}
}
editPopup.addEventListener('mousedown', (evt) => {
overlayClose(evt);
});
addPopup.addEventListener('mousedown', (evt) => {
overlayClose(evt);
});
imagePopup.addEventListener('mousedown', (evt) => {
overlayClose(evt);
});
//popup esc code
function togglePopup(popup) {
popup.classList.toggle('popup_active');
if (popup.classList.contains('popup_active')) {
document.addEventListener('keydown', closeEscape);
} else {
document.removeEventListener('keydown', closeEscape);
}
}
const closeEscape = (evt) => {
if (evt.key === "Escape") {
popup.classList.remove('popup_active');
}
}
Here's useful docs about mouse click events
https://www.w3schools.com/jsref/event_button.asp
You can add event listener on 'mousedown' event and check if event.button equals 0.
UPD
You simply need to add if condition in your event handlers
editPopup.addEventListener('mousedown', (evt) => {
if (evt.button === 0) {
overlayClose(evt);
}
});
I have a div that is created on a button pressed and I'm trying to have it deleted by clicking outside of it but it takes the original button press as clicking outside and immediately closes the div.
closediv();
}
function closediv() {
document.addEventListener("click", (evt) => {
const maindiv = document.getElementById('div3');
let targetElement = evt.target;
do {
if (targetElement == maindiv.childNodes[1]) {
return;
}
targetElement = targetElement.parentNode;
} while (targetElement);
var viewpost = maindiv.childNodes[1];
viewpost.parentNode.removeChild(viewpost);
});
}
In your button click handler, you could use
theButton.addEventListener("click", function(ev) {
ev.stopPropagation();
// rest of the code
});
and the click event would not propagate to the parent containers.
just add a click event on body to close your div and then add prevent default into your div to block closing action when user click on your div
document.body.addEventListener('click', (e) =>{
//close your div here
})
document.addEventListener("click", (evt) => {
evt.preventDefault()
// your code
}
I'm using dropdown functionality of bootstrap and I have this dropdown in container, but in outside I have wrapper, which is reacting on the same event (onclick), so I do
e.stopPropagation();
because I don't want to react on event in wrapper, when I'm clicking dropdown button. Unfortunately, this code also stops my dropdown event. Is it possible to avoid this behaviour and display only dropdown list, without alert?
https://jsfiddle.net/hms5265s/
Here is my solution.
document.querySelector('.container').addEventListener('click', (e) => {
if($(e.target).is("#dropdown")){
alert('work');
}
else{
e.stopPropagation();}
});
If you want your alert to be called on the click event of the wrapper but not the click event of the dropdown iteself you can try something like this
document.querySelector('#wrapper').addEventListener('click', (e) => {
var source = event.target || event.srcElement;
console.log(source.id);
if (source.id !== 'someId') {
// do some stuff
alert("I don't want this alert");
}
// you can stop the even propagation here if you want to.
});
Here is a JSFiddle
If you also don't want to assign an Id for your dropdown you can also check for a class.
here is my solution
Element.prototype.hasClass = function(className){
tClassName = this.className;
return tClassName.match(".*[ ]?"+className+"[ ]?.*") ? true : false;
};
document.querySelector('#wrapper').addEventListener('click', (e) => {
console.log('clicked' + e.target.hasClass('dropdown-toggle'));
if(e.target.hasClass('dropdown-toggle')) return
alert("I don't want this alert");
});
document.querySelector('.dropdown-menu').addEventListener('click', (e) => {
e.stopPropagation();
});
https://jsfiddle.net/hms5265s/6/
updated solution:
Element.prototype.hasClass = function(className){
tClassName = this.className;
return tClassName.match(".*[ ]?"+className+"[ ]?.*") ? true : false;
};
document.querySelector('#wrapper').addEventListener('click', (e) => {
if(e.target.hasClass('dropdown-toggle')) return
alert("I don't want this alert");
});
document.querySelector('.container').addEventListener('click', (e) => {
if(! e.target.hasClass('dropdown-toggle')){
e.stopPropagation();
}
});
https://jsfiddle.net/hms5265s/12/
I am trying to disable right and middle button of mouse so that it cant open new window or tab when click on any menu or hyperlink. Below javascript code works fine for right button but not working for middle button. Middle button of mouse gets captured but still new window or tab opens when click on hyperlink or menu.
<script type="text/javascript">
if (document.layers) {
document.captureEvents(Event.MOUSEDOWN);
document.onmousedown = function () {
return false;
};
}
else {
document.onmouseup = function (e) {
if (e != null && e.type == "mouseup") {
if (e.which == 3) {
alert("Sorry..... Right click Is Disabled!!!!");
return false;
}
if(e.which===2)
{
e.preventDefault();
e.stopImmediatePropagation();
alert("Sorry..... Mouse Scroll click Is Disabled!!!!");
return false;
}
else if(e.button===4)
{
e.preventDefault();
e.stopImmediatePropagation();
alert("Sorry..... Mouse Scroll click Is Disabled!!!!");
return false;
}
}
};
}
Its not woking for firefox, chrome and IE.
try
document.onmousedown= function (e) {
if( e.which == 2 ) {
e.preventDefault();
alert("middle button");
}
}
According to MDN the auxclick event handles the "open link in new tab with middle mouse button" behaviour.
The following code will prevent the middle click behaviour on the entire page.
window.addEventListener("auxclick", (event) => {
if (event.button === 1) event.preventDefault();
});
If you want to disable it for a certain link only, just replace the event listener target (window) with a reference to the specific node.