0
# Browser Polyfill
1
2
The browser polyfill provides `:focus-within` behavior in browsers that don't support the native pseudo-class by dynamically applying attributes or classes to elements when their descendants receive focus.
3
4
## Capabilities
5
6
### Polyfill Initialization Function
7
8
Initializes the focus-within polyfill in the browser environment. The polyfill detects browser support and only activates if needed (unless forced).
9
10
```javascript { .api }
11
/**
12
* Initialize the focus-within polyfill in the browser
13
* @param options - Polyfill configuration options
14
*/
15
function focusWithin(options?: BrowserOptions): void;
16
17
interface BrowserOptions {
18
/** Force polyfill to run even if browser supports :focus-within. Default: false */
19
force?: boolean;
20
/** The replacement selector (class or attribute). Default: "[focus-within]" */
21
replaceWith?: string;
22
}
23
```
24
25
**Usage Examples:**
26
27
```javascript
28
import focusWithin from "postcss-focus-within/browser";
29
30
// Basic initialization with default options
31
focusWithin();
32
33
// Force polyfill to run regardless of browser support
34
focusWithin({ force: true });
35
36
// Use custom replacement selector
37
focusWithin({ replaceWith: ".focus-within" });
38
39
// Combined options
40
focusWithin({
41
force: false,
42
replaceWith: "[data-focus-within]"
43
});
44
```
45
46
**Global script usage:**
47
48
Note: When using the global script, the function is available as `focusWithinInit` (not `focusWithin`).
49
50
```html
51
<script src="https://unpkg.com/postcss-focus-within@9.0.1/dist/browser-global.js"></script>
52
<script>
53
focusWithinInit({ replaceWith: "[focus-within]" });
54
</script>
55
```
56
57
### Polyfill Options
58
59
#### force
60
61
Controls whether the polyfill runs even when the browser natively supports `:focus-within`.
62
63
```javascript { .api }
64
force?: boolean; // Default: false
65
```
66
67
**Usage:**
68
69
```javascript
70
// Only run if browser lacks support (default)
71
focusWithin({ force: false });
72
73
// Always run polyfill
74
focusWithin({ force: true });
75
```
76
77
#### replaceWith
78
79
Specifies the attribute or class to apply to elements when they contain focused descendants. Must match the `replaceWith` option used in the PostCSS plugin.
80
81
```javascript { .api }
82
replaceWith?: string; // Default: "[focus-within]"
83
```
84
85
**Supported formats:**
86
87
```javascript
88
// Attribute selector (default)
89
focusWithin({ replaceWith: "[focus-within]" });
90
91
// Class selector
92
focusWithin({ replaceWith: ".focus-within" });
93
94
// Custom attribute
95
focusWithin({ replaceWith: "[data-focus]" });
96
```
97
98
### Polyfill Behavior
99
100
#### Automatic Browser Detection
101
102
The polyfill automatically detects browser support by attempting to use `document.querySelector(':focus-within')`. If this throws an error, the polyfill activates.
103
104
```javascript
105
try {
106
document.querySelector(':focus-within');
107
// Browser supports :focus-within, polyfill won't run (unless forced)
108
} catch (error) {
109
// Browser doesn't support :focus-within, polyfill will run
110
}
111
```
112
113
#### DOM Ready Handling
114
115
The polyfill handles various document ready states:
116
117
```javascript
118
if (document.readyState === 'complete') {
119
// Initialize immediately
120
} else {
121
// Wait for DOMContentLoaded event
122
document.addEventListener('DOMContentLoaded', initialize);
123
}
124
```
125
126
#### Focus Event Management
127
128
The polyfill manages focus state by listening to focus and blur events:
129
130
```javascript
131
// Event listeners are attached with capture: true
132
document.addEventListener('focus', handleFocusChange, true);
133
document.addEventListener('blur', handleFocusChange, true);
134
```
135
136
#### Element State Management
137
138
When focus changes, the polyfill:
139
140
1. Removes the replacement selector from all previously focused ancestor elements
141
2. Identifies the currently focused element
142
3. Applies the replacement selector to the focused element and all its ancestors (excluding document, HTML, and BODY)
143
144
**Example behavior:**
145
146
```html
147
<!-- Before focus -->
148
<form class="my-form">
149
<fieldset class="fieldset">
150
<input type="text" id="name">
151
</fieldset>
152
</form>
153
154
<!-- After focusing input with replaceWith: "[focus-within]" -->
155
<form class="my-form" focus-within>
156
<fieldset class="fieldset" focus-within>
157
<input type="text" id="name">
158
</fieldset>
159
</form>
160
```
161
162
#### Polyfill Ready Class
163
164
The polyfill automatically adds the `js-focus-within` class to the document element to indicate it's active:
165
166
```javascript
167
document.documentElement.className += ' js-focus-within';
168
```
169
170
This class is used by the PostCSS plugin to scope fallback selectors and prevent flash of unstyled content.
171
172
### Error Handling
173
174
The polyfill validates the `replaceWith` option and throws an error for invalid selectors:
175
176
```javascript
177
// These will throw errors:
178
focusWithin({ replaceWith: ".class > .child" }); // Contains >
179
focusWithin({ replaceWith: "[attr]:hover" }); // Contains :
180
focusWithin({ replaceWith: "#id" }); // Contains #
181
```
182
183
**Error example:**
184
185
```javascript
186
try {
187
focusWithin({ replaceWith: "#invalid" });
188
} catch (error) {
189
console.error(error.message);
190
// "#invalid is not a valid replacement since it can't be applied to single elements."
191
}
192
```
193
194
### Framework Integration
195
196
#### Next.js Integration
197
198
For Next.js applications, use dynamic imports to ensure the polyfill only runs in the browser:
199
200
```javascript
201
import { useEffect } from 'react';
202
203
useEffect(async () => {
204
const focusWithin = (await import('postcss-focus-within/browser')).default;
205
focusWithin();
206
}, []);
207
```