HTML comparisons
11 old vs modern HTML techniques, side by side.
Browser compatibility:
HTML
Beginner
Better mobile keyboards without JavaScript
Old
// JS: detect mobile, swap type
if (/Mobi/.test(navigator.userAgent)) {
input.type = 'tel';
}
if (/Mobi/.test(navigator.userAgent)) {
input.type = 'tel';
}
Modern
<input
inputmode="numeric"
enterkeyhint="search"
/>
see modern →
inputmode="numeric"
enterkeyhint="search"
/>
HTML
Beginner
Live form output without DOM writes
Old
// JS: write to div on every input
input.addEventListener('input', () => {
display.textContent = input.value;
});
input.addEventListener('input', () => {
display.textContent = input.value;
});
Modern
<input type="range" id="vol"/>
<output for="vol">50</output>
see modern →
<output for="vol">50</output>
HTML
Beginner
Lazy load images without JavaScript
Old
// JS: IntersectionObserver
new IntersectionObserver(([entry]) => {
img.src = img.dataset.src;
}).observe(img);
new IntersectionObserver(([entry]) => {
img.src = img.dataset.src;
}).observe(img);
Modern
<img
src="photo.jpg"
loading="lazy"
/>
see modern →
src="photo.jpg"
loading="lazy"
/>
HTML
Beginner
Accordion disclosure without JavaScript
Old
// JS: toggle class + aria on click
btn.addEventListener('click', () => {
panel.classList.toggle('open');
});
btn.addEventListener('click', () => {
panel.classList.toggle('open');
});
Modern
<details>
<summary>What is CSS?</summary>
<!-- content shown on expand -->
</details>
see modern →
<summary>What is CSS?</summary>
<!-- content shown on expand -->
</details>
HTML
Beginner
Native autocomplete without JavaScript
Old
// Typeahead or Awesomplete
new Awesomplete('#input', {
list: ['Chrome', 'Firefox']
});
new Awesomplete('#input', {
list: ['Chrome', 'Firefox']
});
Modern
<input list="browsers" />
<datalist id="browsers">
<option>Chrome</option>
</datalist>
see modern →
<datalist id="browsers">
<option>Chrome</option>
</datalist>
HTML
Beginner
Exclusive accordions without JavaScript
Old
// JS: close all, open clicked
details.forEach(d => d.open = false);
this.open = true;
details.forEach(d => d.open = false);
this.open = true;
Modern
<details name="faq">...</details>
<details name="faq">...</details>
/* browser closes others */
see modern →
<details name="faq">...</details>
/* browser closes others */
HTML
Beginner
Modal controls without onclick handlers
Old
<button onclick="
document.querySelector('#dlg')
.showModal()">Open</button>
document.querySelector('#dlg')
.showModal()">Open</button>
Modern
<button commandfor="dlg"
command="show-modal">Open</button>
<dialog id="dlg">...</dialog>
see modern →
command="show-modal">Open</button>
<dialog id="dlg">...</dialog>
HTML
Beginner
Dialog light dismiss without click-outside listeners
Old
// JS: listen for click on ::backdrop
dialog.addEventListener('click',
(e) => { /* check bounds */ })
dialog.addEventListener('click',
(e) => { /* check bounds */ })
Modern
<dialog closedby="any">
Click outside to close
</dialog>
/* no JS listeners */
see modern →
Click outside to close
</dialog>
/* no JS listeners */
HTML
Intermediate
Customizable selects without a JavaScript library
Old
// Select2 or Choices.js
new Choices('#my-select');
/* rebuilds entire DOM */
new Choices('#my-select');
/* rebuilds entire DOM */
Modern
select,
select ::picker(select) {
appearance: base-select;
}
see modern →
select ::picker(select) {
appearance: base-select;
}
HTML
Beginner
Dropdown menus without JavaScript toggles
Old
.menu { display: none; }
.menu.open { display: block; }
/* + JS: click, clickOutside, ESC, aria */
.menu.open { display: block; }
/* + JS: click, clickOutside, ESC, aria */
Modern
button[popovertarget=menu] { }
#menu[popover] {
position: absolute;
}
see modern →
#menu[popover] {
position: absolute;
}
HTML
Intermediate
Modal dialogs without a JavaScript library
Old
.overlay { position: fixed; z-index: 999; }
/* + JS: open/close, ESC, focus trap */
/* + JS: open/close, ESC, focus trap */
Modern
dialog {
padding: 1rem;
}
dialog::backdrop { background: rgb(0 0 0 / .5); }
see modern →
padding: 1rem;
}
dialog::backdrop { background: rgb(0 0 0 / .5); }