You don't need JavaScript
11 old vs modern native HTML & CSS techniques, no library required, side by side.
Browser compatibility:
HTML inputmode and enterkeyhint for Mobile Keyboards
Widely available Old way
/* Hack: use type=tel to get numeric keyboard */<input type="tel" pattern="[0-9]*" />/* No way to control the return key label */if (/Mobi/.test(navigator.userAgent)) { submitBtn.textContent = 'Search';} Modern
<!-- Numeric pad, no type=tel hack --><input type="text" inputmode="numeric" /><!-- Search keyboard with Search return key --><input type="search" inputmode="search" enterkeyhint="search" /> HTML output Element for Live Form Output
Widely available Old way
<input type="range" id="volume" /><div id="display"></div> const input = document.getElementById('volume');const display = document.getElementById('display');input.addEventListener('input', () => { display.textContent = input.value;}); Modern
<input type="range" id="volume" name="volume" /><output for="volume">50</output> Lazy Load Images in HTML with loading="lazy"
Widely available Old way
<!-- HTML: data-src instead of src --><img data-src="photo.jpg" class="lazy" /> const observer = new IntersectionObserver(([entry]) => { if (entry.isIntersecting) { entry.target.src = entry.target.dataset.src; observer.unobserve(entry.target); }});document.querySelectorAll('.lazy').forEach(img => observer.observe(img)); Modern
<!-- Above the fold: no lazy --><img src="hero.jpg" alt="Hero" /><!-- Below the fold: lazy load --><img src="product.jpg" alt="Product" loading="lazy" /> CSS Accordion with details and summary (No JavaScript)
Widely available Old way
<!-- HTML: custom markup --><button aria-expanded="false" aria-controls="panel"> What is CSS?</button><div id="panel" hidden> <p>CSS is a stylesheet language.</p></div> Modern
<!-- No JS, no aria wiring --><details> <summary>What is CSS?</summary> <p>CSS is a stylesheet language.</p></details> HTML datalist: Native Autocomplete (No JavaScript)
Limited availability Old way
/* Add autocomplete library (Awesomplete, Typeahead, etc.) */import Awesomplete from 'awesomplete';new Awesomplete(document.querySelector('#country'), { list: ['Afghanistan', 'Albania', 'Algeria', ...]});/* Plus custom CSS for the dropdown */ Modern
<input list="countries" id="country" name="country" /><datalist id="countries"> <option value="United States" /> <option value="Canada" /> <option value="Mexico" /></datalist> CSS Exclusive Accordion with the details name Attribute
Widely available Old way
// Close all on toggle, reopen the clicked onedocument.querySelectorAll('details') .forEach(d => { d.addEventListener('toggle', () => { if (d.open) others.forEach(o => o.open = false); }); }); Modern
<details name="faq"> <summary>Question 1</summary> <p>Answer 1</p></details><details name="faq"> <summary>Question 2</summary> <p>Answer 2</p></details> CSS Modal Controls with commandfor and command Attributes
Limited availability Old way
<!-- Inline onclick or JS event listener --><button onclick=" document.querySelector('#dlg') // [!code --] .showModal()"> // [!code --] Open Dialog</button><dialog id="dlg">...</dialog> Modern
<button commandfor="dlg" command="show-modal"> Open Dialog</button><dialog id="dlg">...</dialog> CSS Dialog Light Dismiss with closedby Attribute
Limited availability Old way
/* JS: detect click outside dialog bounds */dialog.addEventListener('click', (e) => { const rect = dialog.getBoundingClientRect(); if (e.clientX < rect.left || e.clientX > rect.right || e.clientY < rect.top || e.clientY > rect.bottom) { dialog.close(); }}); Modern
<dialog closedby="any"> Click outside or press ESC to close</dialog> Customizable CSS Select with appearance: base-select
Limited availability Old way
/* JS: replace native select with custom DOM */import Choices from 'choices.js';new Choices('#my-select', { searchEnabled: false,});/* Plus ~30 lines of custom CSS for .choices__inner, .choices__list, etc. */ Modern
select,select ::picker(select) { appearance: base-select;}select option:checked { background: var(--accent);} CSS-Only Dropdown Menu with the Popover API
Newly available Old way
.menu { display: none;}.menu.open { display: block;}/* JS: toggle class, document click-outside, keydown ESC, aria-expanded, aria-hidden */ Modern
/* <button popovertarget="menu"> ... <div popover> */#menu[popover] { position: absolute; margin: 0.25rem 0;}/* Toggle, light dismiss, ESC: built in. */ CSS Modal Dialog with the Native dialog Element
Widely available Old way
.overlay { position: fixed; inset: 0; background: rgb(0 0 0 / .5);}/* JS: addEventListener click, keydown ESC, focus trap, aria-hidden, body scroll lock, z-index stacking */ Modern
dialog { padding: 1rem;}dialog::backdrop { background: rgb(0 0 0 / .5);}/* JS: dialog.showModal(); dialog.close(); */