Created
October 26, 2018 04:55
-
-
Save asvny/99988385aa5b1573be49309bbaa0f588 to your computer and use it in GitHub Desktop.
FocusTrap - React Hook
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Based on https://hiddedevries.nl/en/blog/2017-01-29-using-javascript-to-trap-focus-in-an-element | |
import React, { useRef, useEffect } from 'react'; | |
const KEYCODE_TAB = 9; | |
function useFocusTrap() { | |
const elRef = useRef(null); | |
function handleFocus(e) { | |
var focusableEls = elRef.current.querySelectorAll( | |
'a[href], button, textarea, input[type="text"], input[type="radio"], input[type="checkbox"], select' | |
), | |
firstFocusableEl = focusableEls[0], | |
lastFocusableEl = focusableEls[focusableEls.length - 1]; | |
var isTabPressed = e.key === 'Tab' || e.keyCode === KEYCODE_TAB; | |
if (!isTabPressed) { | |
return; | |
} | |
if (e.shiftKey) { | |
/* shift + tab */ if (document.activeElement === firstFocusableEl) { | |
lastFocusableEl.focus(); | |
e.preventDefault(); | |
} | |
} /* tab */ else { | |
if (document.activeElement === lastFocusableEl) { | |
firstFocusableEl.focus(); | |
e.preventDefault(); | |
} | |
} | |
} | |
useEffect(() => { | |
elRef.current.addEventListener('keydown', handleFocus); | |
return _ => { | |
elRef.current.removeEventListener('keydown', handleFocus); | |
}; | |
}, []); | |
return elRef; | |
} | |
export default function FocusTrap(props) { | |
const elRef = useFocusTrap(); | |
return ( | |
<div className={'trap'} ref={elRef}> | |
{props.children} | |
</div> | |
); | |
} | |
/// EXAMPLE | |
/* | |
<FocusTrap> | |
<div role="dialog"> | |
<p> | |
Here is a focus trap | |
<a href="#">with</a> | |
<a href="#">some</a> | |
<a href="#">focusable</a> | |
parts. | |
</p> | |
<p> | |
<label htmlFor="focused-input" style={{ marginRight: '10px' }}> | |
Initially focused input | |
</label> | |
<input id={'focused-input'} /> | |
</p> | |
<p> | |
<button>deactivate trap</button> | |
</p> | |
</div> | |
</FocusTrap> | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment