├── .gitignore ├── README.md ├── index.ts ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | .rpt2_cache/ 3 | .rts2_cache_cjs/ 4 | .rts2_cache_es/ 5 | .rts2_cache_umd/ 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lbx ![npm](https://img.shields.io/npm/v/lbx) [![](https://badgen.net/bundlephobia/minzip/lbx)](https://bundlephobia.com/result?p=lbx) 2 | 3 | Minimalist util to trap focus inside a container. 4 | 5 | ### Install 6 | 7 | ``` 8 | npm i lbx --save 9 | ``` 10 | 11 | # Usage 12 | 13 | ```javascript 14 | import { lock } from "lbx"; 15 | 16 | const lockbox = lock(document.getElementById("modal")); 17 | 18 | lockbox(); // destroy 19 | ``` 20 | 21 | ### License 22 | 23 | MIT License © [Eric Bailey](https://estrattonbailey.com) 24 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import tabbable from "tabbable"; 2 | 3 | function createGuard({ onfocus }) { 4 | const d = document.createElement("div"); 5 | d.style.cssText = ` 6 | width: 1px; 7 | height: 0px; 8 | padding: 0px; 9 | overflow: hidden; 10 | position: fixed; 11 | top: 1px; 12 | left: 1px; 13 | `; 14 | d.onfocus = onfocus; 15 | d.setAttribute("tabindex", "0"); 16 | d.setAttribute("aria-hidden", "true"); 17 | d.setAttribute("data-lockbox", ""); 18 | return d; 19 | } 20 | 21 | export function lock(node: Element) { 22 | if (!node) return; 23 | 24 | const { activeElement } = document; 25 | 26 | let before: HTMLElement; 27 | let after: HTMLElement; 28 | const targets: HTMLElement[] = tabbable(node); 29 | 30 | if (!node.querySelector('[data-lockbox]')) { 31 | before = createGuard({ 32 | onfocus() { 33 | const n = targets[targets.length - 1]; 34 | if (n) n.focus(); 35 | } 36 | }); 37 | after = createGuard({ 38 | onfocus() { 39 | const n = targets[0]; 40 | if (n) n.focus(); 41 | } 42 | }); 43 | 44 | node.insertBefore(before, node.children[0]); 45 | node.appendChild(after); 46 | 47 | const n = targets[0]; 48 | if (n) n.focus(); 49 | } 50 | 51 | return function unlock() { 52 | node.removeChild(before); 53 | node.removeChild(after); 54 | 55 | (activeElement as HTMLElement).focus(); 56 | }; 57 | } 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lbx", 3 | "version": "0.0.1", 4 | "description": "Minimalist util to trap focus inside a container.", 5 | "source": "index.ts", 6 | "main": "dist/lbx.js", 7 | "module": "dist/lbx.es.js", 8 | "unpkg": "dist/lbx.umd.js", 9 | "types": "dist/index.d.ts", 10 | "files": [ 11 | "dist" 12 | ], 13 | "scripts": { 14 | "build": "microbundle build", 15 | "watch": "microbundle watch", 16 | "test": "ava test.ts" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+ssh://git@github.com/estrattonbailey/lbx.git" 21 | }, 22 | "author": "estrattonbailey", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/estrattonbailey/lbx/issues" 26 | }, 27 | "homepage": "https://github.com/estrattonbailey/lbx#readme", 28 | "devDependencies": { 29 | "ava": "^2.4.0", 30 | "microbundle": "^0.11.0", 31 | "ts-node": "^8.4.1" 32 | }, 33 | "ava": { 34 | "compileEnhancements": false, 35 | "extensions": [ 36 | "ts" 37 | ], 38 | "require": [ 39 | "ts-node/register" 40 | ] 41 | }, 42 | "dependencies": { 43 | "tabbable": "^4.0.0" 44 | } 45 | } 46 | --------------------------------------------------------------------------------