Skip to content

Commit

Permalink
fix(sidenav) trap focus within the sidenav on small screens WD-2277
Browse files Browse the repository at this point in the history
  • Loading branch information
lorumic authored and bartaz committed Mar 8, 2023
1 parent 66462cc commit 152e84a
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,70 @@
// This is not a production ready code, just serves as an example
// of how the focus should be controlled within the sidenav drawer.
var expandedSidenavContainer = null;
var lastFocus = null;
var ignoreFocusChanges = false;
var focusAfterClose = null;

// Traps the focus within the currently expanded sidenav drawer
function trapFocus(event) {
if (ignoreFocusChanges || !expandedSidenavContainer) return;
// skip the focus trap if the sidenav is not in the expanded status (large screens)
if (!expandedSidenavContainer.classList.contains('is-drawer-expanded')) return;
var sidenavDrawer = expandedSidenavContainer.querySelector('.p-side-navigation__drawer');

if (sidenavDrawer.contains(event.target)) {
lastFocus = event.target;
} else {
focusFirstDescendant(sidenavDrawer);
if (lastFocus == document.activeElement) {
focusLastDescendant(sidenavDrawer);
}
lastFocus = document.activeElement;
}
}

// Attempts to focus given element
function attemptFocus(child) {
if (child.focus) {
ignoreFocusChanges = true;
child.focus();
ignoreFocusChanges = false;
return document.activeElement === child;
}

return false;
}

// Focuses first child element
function focusFirstDescendant(element) {
for (var i = 0; i < element.childNodes.length; i++) {
var child = element.childNodes[i];
if (attemptFocus(child) || focusFirstDescendant(child)) {
return true;
}
}
return false;
}

// Focuses last child element
function focusLastDescendant(element) {
for (var i = element.childNodes.length - 1; i >= 0; i--) {
var child = element.childNodes[i];
if (attemptFocus(child) || focusLastDescendant(child)) {
return true;
}
}
return false;
}

/**
Toggles the expanded/collapsed classed on side navigation element.
Toggles the expanded/collapsed classes on side navigation element.
@param {HTMLElement} sideNavigation The side navigation element.
@param {Boolean} show Whether to show or hide the drawer.
*/
function toggleDrawer(sideNavigation, show) {
expandedSidenavContainer = show ? sideNavigation : null;
const toggleButtonOutsideDrawer = sideNavigation.querySelector('.p-side-navigation__toggle, .js-drawer-toggle');
const toggleButtonInsideDrawer = sideNavigation.querySelector('.p-side-navigation__toggle--in-drawer');

Expand All @@ -16,13 +76,20 @@ function toggleDrawer(sideNavigation, show) {
toggleButtonInsideDrawer.focus();
toggleButtonOutsideDrawer.setAttribute('aria-expanded', true);
toggleButtonInsideDrawer.setAttribute('aria-expanded', true);
focusFirstDescendant(sideNavigation);
focusAfterClose = toggleButtonOutsideDrawer;
document.addEventListener('focus', trapFocus, true);
} else {
sideNavigation.classList.remove('is-drawer-expanded');
sideNavigation.classList.add('is-drawer-collapsed');

toggleButtonOutsideDrawer.focus();
toggleButtonOutsideDrawer.setAttribute('aria-expanded', false);
toggleButtonInsideDrawer.setAttribute('aria-expanded', false);
if (focusAfterClose && focusAfterClose.focus) {
focusAfterClose.focus();
}
document.removeEventListener('focus', trapFocus, true);
}
}
}
Expand Down
71 changes: 68 additions & 3 deletions templates/static/js/scripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,72 @@
};
};

/**
Toggles the expanded/collapsed classed on side navigation element.
var expandedSidenavContainer = null;
var lastFocus = null;
var ignoreFocusChanges = false;
var focusAfterClose = null;

// Traps the focus within the currently expanded sidenav drawer
function trapFocus(event) {
if (ignoreFocusChanges || !expandedSidenavContainer) return;
// skip the focus trap if the sidenav is not in the expanded status (large screens)
if (!expandedSidenavContainer.classList.contains('is-drawer-expanded')) return;
var sidenavDrawer = expandedSidenavContainer.querySelector('.p-side-navigation__drawer');

if (sidenavDrawer.contains(event.target)) {
lastFocus = event.target;
} else {
focusFirstDescendant(sidenavDrawer);
if (lastFocus == document.activeElement) {
focusLastDescendant(sidenavDrawer);
}
lastFocus = document.activeElement;
}
}

// Attempts to focus given element
function attemptFocus(child) {
if (child.focus) {
ignoreFocusChanges = true;
child.focus();
ignoreFocusChanges = false;
return document.activeElement === child;
}

return false;
}

// Focuses first child element
function focusFirstDescendant(element) {
for (var i = 0; i < element.childNodes.length; i++) {
var child = element.childNodes[i];
if (attemptFocus(child) || focusFirstDescendant(child)) {
return true;
}
}
return false;
}

// Focuses last child element
function focusLastDescendant(element) {
for (var i = element.childNodes.length - 1; i >= 0; i--) {
var child = element.childNodes[i];
if (attemptFocus(child) || focusLastDescendant(child)) {
return true;
}
}
return false;
}

/**
Toggles the expanded/collapsed classes on side navigation element.
@param {HTMLElement} sideNavigation The side navigation element.
@param {Boolean} show Whether to show or hide the drawer.
*/
function toggleDrawer(sideNavigation, show) {
const toggleButtonOutsideDrawer = sideNavigation.querySelector('.p-side-navigation__toggle');
expandedSidenavContainer = show ? sideNavigation : null;
const toggleButtonOutsideDrawer = sideNavigation.querySelector('.p-side-navigation__toggle, .js-drawer-toggle');
const toggleButtonInsideDrawer = sideNavigation.querySelector('.p-side-navigation__toggle--in-drawer');

if (sideNavigation) {
Expand All @@ -32,13 +90,20 @@
toggleButtonInsideDrawer.focus();
toggleButtonOutsideDrawer.setAttribute('aria-expanded', true);
toggleButtonInsideDrawer.setAttribute('aria-expanded', true);
focusFirstDescendant(sideNavigation);
focusAfterClose = toggleButtonOutsideDrawer;
document.addEventListener('focus', trapFocus, true);
} else {
sideNavigation.classList.remove('is-drawer-expanded');
sideNavigation.classList.add('is-drawer-collapsed');

toggleButtonOutsideDrawer.focus();
toggleButtonOutsideDrawer.setAttribute('aria-expanded', false);
toggleButtonInsideDrawer.setAttribute('aria-expanded', false);
if (focusAfterClose && focusAfterClose.focus) {
focusAfterClose.focus();
}
document.removeEventListener('focus', trapFocus, true);
}
}
}
Expand Down

0 comments on commit 152e84a

Please sign in to comment.