Web Development

What Actually Makes a Web Page Interactive

Popups, animations, and chat widgets are examples of interactivity, not the mechanism behind it. Here's the real chain, from HTML to a server.

JavaScriptDOMEventsFrontend

Ask someone what makes a website "interactive" and they'll usually point at a popup, a hover animation, or a chat widget in the corner. Those are examples of interactivity. They aren't what interactivity actually is.

The more useful question is narrower: what causes a webpage to respond to something a person does, instead of sitting there like a printed document? The short answer runs through a fixed sequence — HTML, the browser's DOM, an event, a JavaScript handler, a change in state, sometimes a request to a server, and finally an updated interface. This guide walks through that sequence once, properly, then covers the two things that separate interactivity done well from interactivity done badly: accessibility and performance.

A static page already supports some interaction

It's worth being precise here, because "HTML can't do anything on its own" is a common but wrong shortcut. A browser ships with real, built-in behavior for a number of elements, with zero JavaScript involved:

  • Links — navigate. That's an interaction.
  • Forms — collect input and submit it, including a full page navigation to the result.
  • Buttons — inside a form, submit or reset it by default.
  • <details> / <summary> — expand and collapse, with no script attached.
  • Checkboxes and radio buttons — hold their own checked/unchecked state.
  • Native form validationrequired, type="email", minlength, and similar attributes block submission and show a browser-native error, unassisted.

The infoMogli Website Launch Checker's "Why this matters" explanations and its FAQ section are both plain <details> elements — expand-and-collapse interactivity with no JavaScript behind it at all.

The real distinction isn't "HTML is static, JavaScript is interactive." It's this: HTML gives you a fixed set of built-in behaviors. JavaScript is what you reach for when you need custom behavior, or a state change that HTML alone doesn't provide — updating one part of the page in response to another, remembering something between visits, or talking to a server without leaving the page.

The browser turns HTML into the DOM

Before any of that custom behavior can happen, the browser has to do something with the HTML it received. It parses the markup and builds a live, in-memory tree of objects representing every element, attribute, and piece of text on the page. That tree is the DOM — the Document Object Model. It's not the HTML file; it's the browser's working model of it, and JavaScript's entire job is reading and changing that model.

Take a small example:

HTML
html
<button id="save">Save</button>
<p id="status"></p>

On its own, this button does nothing — it's not inside a form, so clicking it has no built-in effect. But once the browser has parsed it into the DOM, that button and that paragraph are addressable objects that JavaScript can find by id and act on. That's the hook the next section uses.

Events are how the browser tells your code something happened

The browser constantly notices things — a click, a key press, a field losing focus, a form being submitted — and it can announce each one as an event. Common ones: click, input, change, submit, keydown, focus. JavaScript doesn't have to poll the page asking "did anything happen yet?" — it registers a listener for a specific event on a specific element, and the browser calls that listener when the event fires.

script.js
javascript
const button = document.querySelector('#save');
const status = document.querySelector('#status');

button.addEventListener('click', () => {
    status.textContent = 'Saved';
});

Walk through what happens, in order: document.querySelector finds the two DOM objects from the HTML above and stores references to them. addEventListener registers a function to run specifically when a click event fires on button — nothing runs yet. When a visitor clicks the button, the browser fires the event, calls the registered function, and that function sets status.textContent — which mutates one specific node in the DOM. The browser then repaints only the part of the screen affected by that change. Nothing else on the page is touched, and no page reload happened.

State is what makes interfaces feel alive

The example above changed one paragraph's text once. Real interfaces track many small facts that change over time and decide what to show based on them — that's state. A menu is open or closed. A checkbox is done or not done. A tab is selected or not. A form field holds whatever the visitor typed. A cart holds a quantity. A session knows whether someone is logged in.

On infoMogli's Website Launch Checker, this isn't abstract — it's the whole mechanism. Each checkbox's checked/unchecked value is state. That state gets written to localStorage on every change, so it survives a page reload. The progress percentage, the per-group counts, and the strikethrough styling on completed items aren't separately maintained — they're all recalculated from the same state every time it changes. Nothing about the checker's UI is hardcoded; all of it is a function of "what's currently checked."

Not every interaction needs a server

It's worth separating two categories of interaction that get lumped together:

  • Browser-only — an accordion opening, a calculator computing a result, tabs switching content already on the page, form validation, reading or writing localStorage, filtering a list of items that already loaded. None of this needs to leave the browser.
  • Server/API-involved — logging in, saving account data, fetching search results from a database, submitting an order, loading content that only exists on a server. These can't be done with JavaScript alone, no matter how it's written.

infoMogli's own Learn page is a browser-only example: the topic filter chips and pagination show and hide rows that are already sitting in the page's HTML — no request goes anywhere. When a server is genuinely needed, the shape is: a user action triggers JavaScript, JavaScript sends a request to an API, a backend processes it and sends a response, and JavaScript updates the DOM with whatever came back. That request/response exchange — and everything that happens on the server side of it — is covered in more depth in How Websites Actually Work; this guide won't repeat it.

Where React fits

None of the above requires a framework. JavaScript, on its own, is what creates interactivity — a framework's job is to help organize it once an interface has enough moving state that keeping it all in sync by hand gets error-prone.

The difference in mental model is roughly this. In plain JavaScript, you think: "this piece of state changed, so I now need to go find these specific DOM elements and update them." You're responsible for every update, and it's easy to miss one. In React, you think: "this state changed — render whatever UI corresponds to that state," and the framework works out what needs to change in the DOM.

That's a real advantage once a page has a lot of interdependent state — but it's not a requirement for interactivity itself. A page with one accordion and a contact form is usually better served by the plain HTML and JavaScript in this article than by pulling in a framework to render it.

Good interactivity includes accessibility

This is the part that's easiest to skip and most damaging when skipped. An interactive element that only some visitors can use isn't finished — it's broken for the people it excludes.

  • Keyboard access. Anything clickable needs to also be reachable and operable by keyboard — tab to it, activate it with Enter or Space. This is free if you use a real <button> or <a>; it's work you have to rebuild by hand if you use a <div onclick> instead.
  • Focus visibility. Whatever element currently has keyboard focus needs a visible indicator, or keyboard navigation becomes guesswork. infoMogli's own stylesheet defines this once, globally: a 2px accent outline with a 3px offset on :focus-visible, applied to links, buttons, inputs, and anything with a tabindex.
  • Semantic controls over clickable divs. A <button> announces itself to a screen reader as a button, is keyboard-operable by default, and behaves correctly with zero extra code. A styled <div> with a click handler has none of that unless you rebuild it manually — role, tabindex, key handling, all of it.
  • Real labels. Every input needs a label a screen reader can announce — either a visible <label for>, or a visually-hidden one. Every checkbox on the Website Launch Checker has one; that's not a nicety, it's what makes the checkbox exist at all to someone using a screen reader.
  • Respect reduced motion. Some visitors have genuine vestibular reactions to animation, and browsers let them say so via a system setting. infoMogli's stylesheet checks for prefers-reduced-motion: reduce once, globally, and collapses every animation and transition duration to effectively zero when it's set — instead of every component needing to remember to check.
  • Don't make hover the only way in. Touchscreens don't have hover. Anything that only reveals itself on mouse hover is invisible on a phone unless there's also a tap/focus path to the same information.

None of this is exotic. It's mostly the default behavior the browser already gives you, as long as you don't override it by reaching for custom markup where a native element would have done the job.

Interactivity has a performance cost

More JavaScript is not automatically a better experience. Every script has to be downloaded, parsed, and executed before it can do anything — on a fast laptop that's easy to not notice; on an older phone on a slow connection, it's the difference between a page that responds and one that doesn't. A few concrete costs that add up quietly: pulling in a library for something three lines of plain JavaScript would do, attaching far more event listeners than a page needs, animation that runs continuously instead of only when something's changing, and third-party widgets (chat, ads, trackers) that each bring their own script weight regardless of how small they look.

The practical principle, and it's the same one from the first section of this guide: use the browser's built-in behavior when it already solves the problem — a native <details> instead of a hand-rolled accordion, a real <form> instead of intercepting every keystroke. Add JavaScript specifically where it earns its cost, not by default.

Interactivity is not the same as engagement

This is where the old version of this article — written in 2020, promising "magical effects" — had the right instinct buried in the wrong advice. It claimed that adding subscription popups, chat widgets, and social-share buttons would reliably lower bounce rate and raise engagement. That's not how it works.

Interactive elements can genuinely help a visitor complete a task — that's the whole point of everything above. But bolting on more popups, more buttons, and more animation doesn't automatically reduce bounce rate, increase conversions, or improve the experience. Done carelessly, it makes all three worse: a popup that interrupts someone mid-read, an animation that delays the content they came for, a chat widget that covers the button they were about to click. Interactivity is a tool for making something work better, not a checklist you complete to make a metric move. That's a big enough question to deserve its own guide — for now, the short version is that fewer, well-placed interactive elements consistently outperform more of them.

A practical mental model

Put back together, in order: HTML defines structure and a small set of built-in native behaviors. The browser parses that HTML into the DOM — a live, in-memory model JavaScript can read and change. Events are how the browser reports that something happened — a click, a keystroke, a submit. JavaScript listens for specific events and responds to them. State is what your code remembers between those responses — what's open, what's checked, what's been typed. The interface is re-rendered to match that state. A server or API only enters the picture when data genuinely needs to leave the browser or come back from somewhere else. Frameworks like React exist to manage this chain once an interface has more state than is comfortable to track by hand — they don't replace any step in it, they organize it.

The notes

Practical web notes, without the noise.

Occasional useful notes about web development, websites, tools and workflows.

No daily emails. No hype. Unsubscribe whenever you like.

Sign-up isn't open yet.
These notes aren't being sent to a mailing list at the moment. Nothing to enter here yet — new guides appear on Learn as they're published.