Skip to content

Commit 69c8c9a

Browse files
committed
Add focus capture experiment.
1 parent ab38a44 commit 69c8c9a

File tree

3 files changed

+151
-0
lines changed

3 files changed

+151
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ with.
1010

1111
## My JavaScript Demos - I Love JavaScript!
1212

13+
* [Trapping Focus Within An Element Using Tab-Key Navigation In JavaScript](https://bennadel.github.io/JavaScript-Demos/demos/focus-capture/)
1314
* [Applying Multiple Animation Keyframes To A Loading Indicator In CSS](https://bennadel.github.io/JavaScript-Demos/demos/multiple-animations-css/)
1415
* [Capturing Keyboard Event Modifiers Across Operating Systems In JavaScript](https://bennadel.github.io/JavaScript-Demos/demos/keydown-os-modifier/)
1516
* [Exploring The Interplay Between HTML Entities And TextContent In JavaScript](https://bennadel.github.io/JavaScript-Demos/demos/text-content-entities/)

demos/focus-capture/demo.css

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
2+
body {
3+
font-family: sans-serif ;
4+
font-size: 18px ;
5+
}
6+
7+
a {
8+
color: blue ;
9+
}
10+
11+
[ tabindex = "-1" ] {
12+
color: black ;
13+
}
14+
15+
*:focus {
16+
outline: 2px solid red ;
17+
}
18+
19+
[ tabindex = "-1" ]:focus {
20+
outline: 4px dotted blue ;
21+
}
22+
23+
.capture {
24+
border: 2px dotted red ;
25+
border-radius: 4px 4px 4px 4px ;
26+
padding: 10px 10px 10px 10px ;
27+
}
28+
29+
.capture a,
30+
.capture button,
31+
.capture input {
32+
display: block ;
33+
margin: 10px 0px 10px 0px ;
34+
}

demos/focus-capture/index.htm

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<title>
6+
Trapping Focus Within An Element Using Tab-Key Navigation In JavaScript
7+
</title>
8+
9+
<link rel="stylesheet" type="text/css" href="./demo.css" />
10+
</head>
11+
<body>
12+
13+
<h1>
14+
Trapping Focus Within An Element Using Tab-Key Navigation In JavaScript
15+
</h1>
16+
17+
<p>
18+
<a href="#">I can be focused</a> ,
19+
<a href="#">I can be focused</a> ,
20+
<a href="#">I can be focused</a> ,
21+
<a href="#">I can be focused</a>
22+
</p>
23+
24+
<!-- Once focus lands inside this container, we're going to trap it there. -->
25+
<p class="capture">
26+
<input type="text" />
27+
<a tabindex="-1">I am <strong>NOT</strong> tabbable</a>
28+
<a href="#">I can be focused</a>
29+
<a href="#">I can be focused</a>
30+
<button>Hey buddy</button>
31+
<a href="#">I can be focused</a>
32+
<input type="checkbox" />
33+
<a href="#">I can be focused</a>
34+
<a tabindex="-1">I am <strong>NOT</strong> tabbable</a>
35+
<button>Hey buddy</button>
36+
</p>
37+
38+
<p>
39+
<a href="#">I can be focused</a> ,
40+
<a href="#">I can be focused</a> ,
41+
<a href="#">I can be focused</a> ,
42+
<a href="#">I can be focused</a>
43+
</p>
44+
45+
<script type="text/javascript" src="../../vendor/jquery/3.6.0/jquery-3.6.0.min.js"></script>
46+
<script type="text/javascript">
47+
48+
var capture = $( ".capture" )
49+
// For the sake of the demo, when the page loads we're going to draw focus to
50+
// the capture container. By using a tabindex of -1, the capture container
51+
// won't be focusable via keyboard navigation; but, we can programmatically
52+
// focus the element.
53+
.attr( "tabindex", "-1" )
54+
.focus()
55+
// Inpsect any keydown events that come from within the capture container.
56+
.keydown(
57+
function handleKeydown( event ) {
58+
59+
if ( event.key.toLowerCase() !== "tab" ) {
60+
61+
return;
62+
63+
}
64+
65+
// At this point, we know that we're capturing a TAB-related keyboard
66+
// event ON or IN the Capture container. As such, we need to look at
67+
// the current state of the DOM (which may be changing dynamically
68+
// depending on the application logic) to see if we need to override
69+
// the keyboard navigation. What we're looking for here is any
70+
// element that is capable of NATURALLY receiving focus (via Tab).
71+
// --
72+
// NOTE: jQuery's .add() function results in a collection that is
73+
// ordered by the DOM ORDER. As such, we can use .first() and .last()
74+
// on the resultant collection with confidence.
75+
var tabbable = $()
76+
// All form elements can receive focus.
77+
.add( capture.find( "button, input, select, textarea" ) )
78+
// Any element that has an HREF can receive focus.
79+
.add( capture.find( "[href]" ) )
80+
// Any element that has a non-(-1) tabindex can receive focus.
81+
.add( capture.find( "[tabindex]:not([tabindex='-1'])" ) )
82+
;
83+
var target = $( event.target );
84+
85+
// Reverse tabbing (Key: Shift+Tab).
86+
if ( event.shiftKey ) {
87+
88+
if ( target.is( capture ) || target.is( tabbable.first() ) ) {
89+
90+
// Force focus to last element in container.
91+
event.preventDefault();
92+
tabbable.last().focus();
93+
94+
}
95+
96+
// Forward tabbing (Key: Tab).
97+
} else {
98+
99+
if ( target.is( tabbable.last() ) ) {
100+
101+
// Force focus to first element in container.
102+
event.preventDefault();
103+
tabbable.first().focus();
104+
105+
}
106+
107+
}
108+
109+
}
110+
)
111+
;
112+
113+
</script>
114+
115+
</body>
116+
</html>

0 commit comments

Comments
 (0)