The inert Attribute
The inert Attribute
Section titled “The inert Attribute”The inert attribute makes an element non-interactive by preventing all user interactions including clicks, focus, and selection, while keeping the element visible. It’s particularly useful for modal dialogs, disabled sections, and when you need to temporarily disable parts of your interface.
Syntax
Section titled “Syntax”<element inert>Content</element>Values
Section titled “Values”| Value | Description |
|---|---|
| Boolean (no value) | Element and its descendants become non-interactive |
Examples
Section titled “Examples”Basic Inert Element
Section titled “Basic Inert Element” Result
Modal Dialog Pattern
Section titled “Modal Dialog Pattern” Result
Loading State
Section titled “Loading State” Result
Disabled Section
Section titled “Disabled Section” Result
How It Works
Section titled “How It Works”Effect on Elements
Section titled “Effect on Elements”When an element has the inert attribute:
- Cannot be clicked: All mouse/touch events are ignored
- Cannot be focused: Tab key skips inert elements
- Cannot be selected: Text selection is disabled
- Cascade to children: All descendants become inert
- Visible: Element remains visible (unlike
hidden) - Accessibility: Removed from accessibility tree
Browser Implementation
Section titled “Browser Implementation”/* Browser default styles (conceptual) */[inert] { /* Inert elements are not focusable */ /* Inert elements cannot receive pointer events */ /* Inert elements are not exposed to assistive technology */}Comparison with Other Attributes
Section titled “Comparison with Other Attributes”inert vs disabled
Section titled “inert vs disabled”<!-- disabled: Only works on form elements --><button disabled>Can't click</button><input disabled>
<!-- inert: Works on any element, including containers --><div inert> <button>Can't click</button> <a href="#">Can't click</a> <input type="text"></div>inert vs pointer-events: none
Section titled “inert vs pointer-events: none”<!-- pointer-events: none - Only prevents mouse/touch --><div style="pointer-events: none"> <button tabindex="0">Can still be focused with keyboard!</button></div>
<!-- inert - Prevents all interactions --><div inert> <button tabindex="0">Cannot be focused at all</button></div>inert vs hidden
Section titled “inert vs hidden”<!-- hidden: Element not visible --><div hidden> <p>You can't see this</p></div>
<!-- inert: Element visible but not interactive --><div inert> <p>You can see this but can't interact with it</p></div>Best Practices
Section titled “Best Practices”1. Use for Modal Dialogs
Section titled “1. Use for Modal Dialogs”The primary use case for inert is modal dialogs:
function openModal() { // Make background inert document.querySelector('main').inert = true; document.querySelector('nav').inert = true;
// Show modal modal.classList.remove('hidden'); modal.focus();}
function closeModal() { // Remove inert document.querySelector('main').inert = false; document.querySelector('nav').inert = false;
// Hide modal modal.classList.add('hidden');}2. Provide Visual Feedback
Section titled “2. Provide Visual Feedback”Make it obvious that content is inert:
[inert] { opacity: 0.6; filter: grayscale(50%); cursor: not-allowed; user-select: none;}
/* Or use blur for background content */.background[inert] { filter: blur(3px); opacity: 0.8;}3. Use for Loading States
Section titled “3. Use for Loading States”Prevent interaction during async operations:
async function submitForm() { const form = document.getElementById('form');
// Disable form form.inert = true; showLoadingSpinner();
try { await saveData(); } finally { // Re-enable form form.inert = false; hideLoadingSpinner(); }}4. Premium Features Paywall
Section titled “4. Premium Features Paywall”Visually show locked features:
<div class="feature-card" inert> <div class="upgrade-badge"> 🔒 Premium Feature </div> <h3>Advanced Analytics</h3> <p>Unlock detailed insights...</p> <button>View Reports</button></div>5. Temporary Sections
Section titled “5. Temporary Sections”Disable sections during specific states:
// Disable payment section until shipping address is completeif (!shippingAddress.isComplete()) { paymentSection.inert = true;} else { paymentSection.inert = false;}Accessibility Considerations
Section titled “Accessibility Considerations”Screen Readers
Section titled “Screen Readers”Elements with inert:
- Are removed from accessibility tree
- Cannot be navigated to with screen reader shortcuts
- Are not announced to screen readers
<!-- Screen reader will skip this entire section --><div inert> <h2>Section Title</h2> <p>Content that screen readers won't announce</p> <button>Inaccessible button</button></div>Focus Management
Section titled “Focus Management”When making content inert, ensure focus is in the right place:
function openDialog() { // Make background inert mainContent.inert = true;
// Show dialog dialog.removeAttribute('hidden');
// Move focus to dialog dialog.focus();}Keyboard Navigation
Section titled “Keyboard Navigation”Inert elements are skipped during Tab navigation:
<button>Button 1</button><div inert> <button>Button 2 - Skipped by Tab</button> <a href="#">Link - Also skipped</a></div><button>Button 3</button><!-- Tab goes: Button 1 → Button 3 -->JavaScript API
Section titled “JavaScript API”Setting and Removing
Section titled “Setting and Removing”const element = document.getElementById('myElement');
// Make inertelement.inert = true;element.setAttribute('inert', '');
// Remove inertelement.inert = false;element.removeAttribute('inert');
// Check if inertif (element.inert) { console.log('Element is inert');}
// Toggleelement.inert = !element.inert;Checking Support
Section titled “Checking Support”if ('inert' in HTMLElement.prototype) { console.log('Inert attribute is supported');} else { // Load polyfill console.log('Need polyfill');}Polyfill for Older Browsers
Section titled “Polyfill for Older Browsers”<!-- For browsers without native support -->Common Use Cases
Section titled “Common Use Cases”1. Modal Dialog with Focus Trap
Section titled “1. Modal Dialog with Focus Trap”class Modal { constructor(element) { this.modal = element; this.background = document.querySelector('main'); }
open() { this.background.inert = true; this.modal.removeAttribute('hidden'); this.modal.querySelector('button').focus(); }
close() { this.background.inert = false; this.modal.setAttribute('hidden', ''); }}2. Multi-Step Form
Section titled “2. Multi-Step Form”const steps = document.querySelectorAll('.step');
function goToStep(stepNumber) { steps.forEach((step, index) => { if (index === stepNumber) { step.inert = false; step.hidden = false; } else { step.inert = true; step.hidden = true; } });}3. Drawer/Sidebar
Section titled “3. Drawer/Sidebar”function openDrawer() { drawer.classList.add('open'); mainContent.inert = true;}
function closeDrawer() { drawer.classList.remove('open'); mainContent.inert = false;}4. Loading Overlay
Section titled “4. Loading Overlay”function showLoading() { document.body.inert = true; loadingOverlay.hidden = false;}
function hideLoading() { document.body.inert = false; loadingOverlay.hidden = true;}Common Pitfalls
Section titled “Common Pitfalls”Browser Support
Section titled “Browser Support”| Browser | Support | Notes |
|---|---|---|
| Chrome | 102+ | Native support |
| Firefox | 112+ | Native support |
| Safari | 15.5+ | Native support |
| Edge | 102+ | Native support |
| IE | ❌ | No support (use polyfill) |
Polyfill Usage
Section titled “Polyfill Usage”<!DOCTYPE html><html><head> <!-- Load polyfill for older browsers --> <script> if (!('inert' in HTMLElement.prototype)) { const script = document.createElement('script'); document.head.appendChild(script); } </script></head><body> <div inert> Content that works in all browsers </div></body></html>Related Attributes
Section titled “Related Attributes”hidden- Hide elements completely (not visible)disabled- Disable form controls specificallyaria-hidden- Hide from screen readers onlyaria-modal- Indicate modal dialog (use with inert)