Event Listeners and Interactive Web Pages

Make web pages respond to people. Learn to attach event listeners, read the event object, and follow events as they bubble through the page so one listener can handle many elements. Handle key presses, clicks, touches and pen input, build a drag interaction, read and submit forms in JavaScript, and validate input with the browser's built-in rules and your own messages. Finish by building custom controls that work with a keyboard and a screen reader as well as a mouse.

BeginnerLesson 6 of 9 in JavaScript Programming Course

What you will learn

  1. Make a page react when something happens: attach listeners to elements, read what the event object reports, and decide how long each listener stays active.
    • Attach an event listener to an element
      Use addEventListener to run a function each time a click, key press, form action or page event happens on an element.
    • Read the information in the event object
      Use the event object a listener receives to find out what happened and where: its type, target, current target and event-specific details.
    • Remove listeners and control how they run
      Stop listening when a listener is no longer needed, use the once, signal and passive options, and fire your own custom events.
  2. Explain how an event moves down to its target and back up through the page, cancel built-in browser behavior when you replace it, and use bubbling to handle many elements with one listener.
    • Explain bubbling and capturing
      Describe the three phases of event propagation and predict the order in which listeners on nested elements run.
    • Prevent default actions and stop propagation
      Cancel the browser's built-in response to an event when your code replaces it, and stop an event from traveling further only when that is really needed.
    • Handle many elements with event delegation
      Use one listener on a shared ancestor and event.target.closest() to handle events from many child elements, including elements added later.
  3. React to key presses, focus changes, clicks, touches and pen input, and build a drag interaction that works with every kind of pointer.
    • Respond to key presses and focus changes
      Use keydown, keyup and the focus events, choose between event.key and event.code, and build keyboard shortcuts that do not get in the user's way.
    • Handle clicks, touches and pen input with pointer events
      Use click for activation and pointer events for pressing, moving and releasing with a mouse, finger or pen; read coordinates and buttons; and choose the right hover events.
    • Build a drag interaction with pointer events
      Combine pointerdown, pointermove and pointerup with pointer capture to move an element, make it work on touch screens and offer a way to do the same without dragging.
  4. Read what users enter as they type, handle form submission in JavaScript, and check input with the browser's validation features and your own rules, with clear error messages.
    • Read form fields and react as users type
      Read text fields, numbers, checkboxes, radio buttons and selects, use the input and change events at the right moments, and collect a whole form with FormData.
    • Handle form submission in JavaScript
      Listen for the form's submit event, stop the page from reloading, read the submitted data and know what triggers a submission.
    • Use the browser's built-in validation
      Describe rules with HTML attributes, read the result in the validity object, check fields from code, and style invalid fields at the right moment.
    • Write custom validation with clear error messages
      Add rules the HTML attributes cannot express, write helpful messages, show them at the right moment and connect them to their fields.
  5. Make interactive elements that work with a mouse, a keyboard and a screen reader: prefer native HTML, add roles, keyboard support and state when you build your own, and keep focus where users expect it.
    • Prefer native elements and make custom controls focusable
      Know what native interactive elements give you for free and what a custom control built from a <div> or <span> must add: a role, focusability, an accessible name and a visible focus indicator.
    • Add keyboard support and state to custom controls
      Make custom buttons, toggles, disclosures, checkboxes and groups of items work from the keyboard, and keep their ARIA state in step with what users see.
    • Keep focus where users expect it when the page changes
      Move focus on purpose when content opens, closes, is removed or shows errors, and never move it unexpectedly while users are typing or choosing.

Event Listeners study guide

5 chapters

1

Respond to events with event listeners

Web pages respond through functions registered for events. Setup chooses the element and event type; each later occurrence brings an event object describing what happened. Reading that object lets a listener act on the right control, even when a click lands on an icon inside it. Listener references and options then let a feature keep its responses active for exactly the time it needs, and custom events let one part of a page notify another.

1.1 Listeners turn actions into code

A page can wait for a person to act. An event reports something that happened, and an event listener is the function called in response. Registering a listener sets up that response; it does not perform the action immediately.

Add the function to the element

Select an element that already exists, choose the event's exact name, and pass a function:

const button = document.querySelector("#like");
let likes = 0;
button.addEventListener("click", function () {
  likes = likes + 1;
  button.textContent = "Likes: " + likes;
});

The selection, variable setup, and registration run when the script runs. Each later click runs the function body. After three clicks the button displays "Likes: 3". Keeping likes outside the function preserves the total between clicks; putting let likes = 0 inside would restart it each time.

Pass a function reference, such as showMenu, without parentheses. In button.addEventListener("click", showMenu()), JavaScript calls showMenu immediately and passes its return value as the listener. To supply an argument later, wrap the call: button.addEventListener("click", () => showMenu("main"));.

Choose an event that actually occurs

Names are case-sensitive strings without an "on" prefix. Use "click", not "Click" or "onclick". A made-up name is accepted without an error, but the browser will not emit it for a click. Common families include pointer events such as pointerdown and pointerup, keyboard events such as keydown, form events such as input and submit, and focus events such as focusin. DOMContentLoaded has its own exact capitalization.

Set up the right element at the right time

A script in the head can run before a button exists. Selection then returns null, and calling addEventListener on it throws an error. Load an external script with defer, place it after the markup, or register setup on the document's DOMContentLoaded event. An element created later needs setup after creation or a listener on an ancestor.

querySelectorAll returns a NodeList, not one event target. Register on its elements:

document.querySelectorAll(".tab").forEach((tab) => {
  tab.addEventListener("click", selectTab);
});
Let listeners coexist
MethodWhat registration does
addEventListener("click", fn)Adds a listener alongside others and accepts options.
button.onclick = fnReplaces the single property handler; assign null to clear it.
An HTML onclick attributePlaces JavaScript inside the markup.

Listeners with the same capture setting run in registration order. At the event target, capture listeners run before non-capture listeners. Adding the same function object again with the same type and capture setting does not add a second copy.

Key Takeaways

  • Pass the function itself; use a wrapper when the later call needs arguments.
  • Event names require exact spelling and capitalization.
  • An element must exist when its listener is registered.
  • Loop over a NodeList to attach listeners to its elements.
  • addEventListener permits multiple listeners; a handler property holds one.

1.2 Read where an event started

The browser passes an event object to your listener. Its properties explain the event that triggered this particular call. Name the parameter event or e; the parameter name is your choice.

button.addEventListener("click", (event) => {
  console.log(event.type);
  console.log(event.currentTarget);
});

This logs "click" and the button during the listener call. Logging the whole object can help you discover its other properties.

Separate the origin from the listener

Imagine a button containing an image:

<button id="save" data-id="7">
  <img src="disk.svg" alt=""> Save
</button>
PropertyMeaningWhen the image is clicked
event.targetWhere the event startedThe image
event.currentTargetWhere the running listener was attachedThe button, for a button listener

Clicking the text "Save" makes the button the target too. The text is part of the button; it does not turn into a separate element target.

This difference matters when reading data. The image does not have the button's data-id, so event.target.dataset.id is undefined on an image click. A listener attached directly to the button can read event.currentTarget.dataset.id. A listener on a larger container can find the intended button with event.target.closest("button") and check that a match exists.

Read the details for this event

Every event has a type, a target, and a currentTarget. The Boolean bubbles says whether it travels back up through ancestors. cancelable says whether code may cancel its default action, and defaultPrevented records whether that cancellation has happened. timeStamp records the event's creation time in milliseconds relative to the page's time origin.

Different event families add useful details. A modern click supplies a PointerEvent, which inherits mouse-event properties such as clientX, clientY, and button. A KeyboardEvent adds key, code, and modifier flags. An input listener can use event.target.value to read the field's new text.

For a gallery listener, event.target.tagName might be "IMG" when a photo is clicked or "DIV" when the gap is clicked. Handle the case actually reported. Do not assume every event inside a container came from the same kind of child.

Use the value while the listener runs

In a regular function listener, this equals event.currentTarget. An arrow function takes this from its surrounding code; it does not acquire the element as this. Using event.currentTarget works with either function style.

After dispatch, currentTarget is reset to null. A console may show that later value when you expand a previously logged event. Log event.currentTarget directly inside the listener to inspect the element during the call.

Key Takeaways

  • target identifies the event origin; currentTarget identifies the running listener’s element.
  • Read data from the element that actually owns it.
  • Event families add properties such as coordinates, key values, and modifiers.
  • currentTarget is available during the listener and becomes null afterward.

1.3 Control a listener’s lifetime

A listener should stay active for as long as its feature needs it. For a temporary keyboard interaction, keep the function reference so it can be removed when the interaction closes.

Remove the same function
function onKey(event) {
  console.log(event.key);
}
document.addEventListener("keydown", onKey);
// When the feature closes:
document.removeEventListener("keydown", onKey);

Removal matches the event type, function object, and capture setting. An anonymous function can also be removed when its reference is saved, as in const handler = () => save();. Writing a fresh () => save() in the removal call creates a different function, even though the code looks identical.

Choose the registration options
OptionEffect
once: trueRemoves this listener after its first call.
signal: controller.signalRemoves the listener when that controller is aborted.
capture: trueListens on the capturing path.
passive: truePromises the listener will not cancel the default action.

A one-time welcome banner can use { once: true }. A feature with several listeners can give them one signal:

const controller = new AbortController();
window.addEventListener("pointermove", moveCursor, {
  signal: controller.signal
});
window.addEventListener("keydown", onKey, {
  signal: controller.signal
});
// Close the feature:
controller.abort();

Aborting removes both registrations. An aborted controller cannot be reset; create a new controller for a new lifetime. For removal by removeEventListener, repeat capture: true if it was used on registration. once, signal, and passive do not form part of the removal match.

A passive listener's preventDefault() call is ignored. Browsers commonly default certain scrolling-related listeners on document-level targets to passive, including wheel, touchstart, and touchmove; explicitly use { passive: false } there when cancellation is needed.

Avoid accumulating callbacks

If each "Start" click adds a fresh anonymous board listener, starting twice makes one board click run two callbacks. Install the board listener once, reuse the same function object, or abort the previous group before replacing it. Distinct once callbacks can still accumulate before they fire.

Detaching an element preserves its listeners. They work if it is reinserted, and dispatchEvent() can reach them while it is detached. The element becomes eligible for garbage collection when it and its listeners are unreachable. A listener on window or document remains attached until explicitly removed.

Send a custom event
const event = new CustomEvent("cart:add", {
  detail: { id: 42 },
  bubbles: true
});
item.dispatchEvent(event);

Listeners read event.detail.id. Dispatch calls the listeners immediately, before returning. bubbles: true lets ancestors hear it too. An event sent with dispatchEvent() has isTrusted equal to false; browser-dispatched events can be trusted even when an API such as focus() caused them.

Key Takeaways

  • Removal matches type, function identity, and capture setting.
  • once ends one registration after a call; aborting a signal ends its associated registrations.
  • A passive listener cannot cancel a default action.
  • Fresh callbacks added on each setup can accumulate.
  • Detaching an element preserves its listeners.
  • Custom events carry data in detail and dispatch synchronously.
2

Follow events as they travel through the page

Nested elements share an event path. A click can pass a container before reaching its target and visit it again while bubbling, which makes one listener enough for a changing list. Following that path helps explain why a child action also triggers a parent. Separate cancellation of browser behavior from control of propagation, then use the event origin to choose which actions a shared listener should handle.

2.1 An event’s path through nested elements

A click inside a button can also be heard by the button's ancestors. Propagation is the event's journey through the document tree. Following that journey explains both useful shared listeners and surprising extra actions.

Follow three phases
  1. Capture: the event travels from window down through the ancestors toward the target.
  2. Target: listeners run at the element where the event began.
  3. Bubble: a bubbling event travels back up through the ancestors to window.

The eventPhase property reports 1 for capture, 2 at the target, and 3 for bubbling. A listener registered without options runs at the target or on the bubbling path. A listener with { capture: true } runs on the downward path, including at the target if registered there.

Trace a concrete click
<div id="outer">
  <div id="inner">
    <button>Go</button>
  </div>
</div>

Suppose the button and each div have ordinary click listeners that log their names. Clicking "Go" logs "button", "inner", then "outer". Clicking an empty area of inner logs "inner", then "outer"; the button is not on that event's path.

Now add a capture listener to outer. A button click logs "outer (capture)" first, then "button", "inner", and "outer" from the ordinary listeners. The capture listener sees the click before the descendants do. After outer, the event can continue through body, html, document, and window if listeners are registered there.

It is one event, not a new click created at every ancestor. target remains the button throughout. currentTarget changes as each listener runs. In the outer listener, reading target still identifies the original button.

Check which events bubble
BubblesDoes not bubble
click, input, change, submitinvalid
keydown, pointerdownfocus, blur
focusin, focusoutpointerenter, pointerleave
pointerover, pointeroutmouseenter, mouseleave

The event's bubbles property tells you whether it has a bubbling phase. Non-bubbling events can still be observed by an ancestor's capture listener. To handle focus changes through an ordinary container listener, choose focusin or focusout.

Explain a nested-action bug

A product card opens a detail view when clicked. Its "Remove" button deletes the product. Clicking that button first deletes it, then reaches the card listener and opens the detail view. The event path explains why both callbacks run.

The card can ignore removal clicks with if (event.target.closest(".remove")) return;. Alternatively, the button can stop propagation. Filtering at the card preserves the event for other ancestors that may need it.

Key Takeaways

  • Propagation follows capture, target, and then bubbling for events that bubble.
  • Ordinary ancestor listeners hear descendant clicks on the upward path.
  • target stays fixed while currentTarget changes.
  • Use a bubbling focus event or capture to observe focus from an ancestor.

2.2 Cancel an action or stop a journey

A listener can change what happens after an event, or where the event travels. These are separate decisions. A link may keep bubbling even after navigation has been canceled.

Replace a browser action

A default action is behavior the browser supplies: following a link, submitting a form, toggling a checkbox, inserting text, scrolling with Space, or moving focus with Tab. Use preventDefault() when your code replaces that action:

help.addEventListener("click", (event) => {
  event.preventDefault();
  openHelpPanel();
});

For a link to /help, this opens the panel without navigating. The link still offers its destination when JavaScript does not run.

Only cancelable events support cancellation. Inspect event.cancelable; defaultPrevented records successful cancellation. input occurs after the value changes and is not cancelable. Calling preventDefault() there does not remove the inserted text. A cancelable beforeinput event is the appropriate place to intercept an edit; canceling a relevant keydown can prevent that key's default effect.

Control propagation separately
MethodStops the browser default?Stops later listeners?
preventDefault()Yes, if cancelable and not passiveNo
stopPropagation()NoStops travel to other elements; listeners on this element still run
stopImmediatePropagation()NoAlso stops remaining listeners on this element

A link whose listener only stops propagation still follows its URL. Canceling navigation alone still lets a document click listener hear the event. Returning false from an addEventListener callback does neither. A handler property or inline handler can cancel its default by returning false, but that still does not stop propagation.

Keep shared behavior working

An ancestor may close a menu or count interactions. Stopping propagation hides the event from that code. Often the ancestor can inspect target or defaultPrevented instead.

For example, a menu closes only when a click falls outside both the menu and its opener:

document.addEventListener("click", (event) => {
  if (!menu.contains(event.target) &&
      !menuButton.contains(event.target)) {
    menu.hidden = true;
  }
});

contains() includes the element itself and its descendants. Checking the opener matters because its opening click also bubbles to the document. Without that second test, the newly opened menu would close immediately.

Cancel only a handled action

In a key listener, check the key before canceling. Canceling every key blocks ordinary typing and can prevent Tab from moving focus. In a passive listener, cancellation is ignored altogether.

Key Takeaways

  • preventDefault() cancels an eligible default action while preserving propagation.
  • stopPropagation() stops travel, not the default action.
  • stopImmediatePropagation() also skips remaining listeners on the current element.
  • input is too late to cancel an edit.
  • Outside-click checks must include both a popup and its opener.
  • Cancel only the specific keyboard actions your code handles.

2.3 One listener for a changing list

Event delegation places a listener on a stable ancestor and uses the event's origin to choose an action. It is useful when a list has many items or when code creates new items after setup.

Find the intended control

A bubbling click reaches the list no matter which of its descendants was clicked. Start at event.target, find the nearest matching control, and reject unrelated clicks:

const list = document.querySelector("#todos");
list.addEventListener("click", (event) => {
  const button = event.target.closest(".remove");
  if (!button || !list.contains(button)) return;
  const item = button.closest("li");
  console.log("Removing item " + item.dataset.id);
  item.remove();
});

The list contains items such as <li data-id="7">Buy milk <button class="remove">Remove</button></li>. The listener belongs to the list, so a new item with the same markup works immediately after insertion.

Trace an icon click through the code. The target is the icon, closest(".remove") finds its button, and button.closest("li") finds the item. Its data-id identifies the item in the message. Finally, remove() changes the page.

Keep the two guards
TestWhy it matters
!buttonA click on a list gap or ordinary item text may find no removal button.
!list.contains(button)closest() can climb beyond the list and find a matching outer ancestor.

Testing only event.target.matches(".remove") loses icon clicks because the icon itself does not match. closest() checks the target and walks up its ancestors. In a delegated listener, currentTarget is the list; it does not become the matched button.

Route several actions

Data attributes let one toolbar listener choose among known actions:

const actions = { bold: makeBold, italic: makeItalic,
  clear: clearText };
toolbar.addEventListener("click", (event) => {
  const button = event.target.closest("button[data-action]");
  if (!button || !toolbar.contains(button)) return;
  const action = actions[button.dataset.action];
  if (typeof action === "function") action();
});

A button with data-action="clear" selects clearText. The function check handles an unknown action without attempting to call undefined.

Choose a suitable event

Delegation works with bubbling events beyond clicks. A single form input listener can pass event.target to a field-checking function. To delegate focus changes, use focusin or focusout; ordinary focus and blur do not bubble. For hover transitions use bubbling over/out events when delegation is required.

A descendant's stopPropagation() can prevent an event from reaching the container. When a delegated listener seems inactive, check the event type, the target, the selector match, and whether something below the container stopped the journey.

Key Takeaways

  • Attach the listener to an ancestor that survives changes to its children.
  • closest() finds a control even when a nested icon is clicked.
  • Reject missing matches and matches outside the container.
  • Delegation automatically includes matching children added later.
  • Data attributes can route a shared listener to known actions.
  • Delegation needs an event that reaches the ancestor.
3

Handle keyboard and pointer input

An interaction can arrive through keys, a mouse, a finger, or a pen. The event you choose determines whether you receive an activation, a changed text value, or the movement of a particular pointer. Coordinate frames and stored starting positions make dragging predictable. Focus and alternative controls keep the same page usable when a person navigates with the keyboard or cannot perform a dragging gesture.

3.1 Keys, text, and focus

Keyboard input goes to the focused element, available as document.activeElement, then bubbles. A document listener can therefore hear keys from fields as well as from other controls. That reach makes keyboard shortcuts useful, but it also means they can interrupt typing.

Choose the property by its meaning
PropertyReportsExample
keyThe produced value, with layout and modifiers"a", "A", "Enter", " "
codeThe physical key position"KeyA", "Enter", "Space"

On an AZERTY keyboard, the position labeled Q on a US keyboard produces key "a" and code "KeyQ". Use key for meaning, such as Escape to close or a letter shortcut. Use code for fixed positions, such as movement keys in a game. The space bar's key is a single space, not "Space".

keydown fires on the initial press and can repeat while held. Repeated calls have event.repeat === true. keyup fires on release. Use keydown instead of the deprecated keypress event.

Handle a shortcut precisely
document.addEventListener("keydown", (event) => {
  if ((event.ctrlKey || event.metaKey) &&
      event.key.toLowerCase() === "s") {
    event.preventDefault();
    saveDraft();
  }
});

This handles Ctrl+S and Command+S. Lowercasing allows "S" too. Cancellation replaces the browser's Save Page action only for the matching shortcut. Other modifier flags include shiftKey and altKey.

For a page-wide single-letter shortcut, skip typing contexts before acting:

if (event.target.matches("input, textarea, select") ||
    event.target.isContentEditable) return;
if (event.repeat) return;

The first guard protects text entry. The second makes an action run once per physical press. Do not cancel Tab across the page; it is how keyboard users move on.

Listen for text after it changes

For an ordinary printable key, the sequence is keydown, beforeinput, input, then keyup. Reading value in keydown gives the value before that character was inserted. Use input for a text preview or character count. It also handles edits such as paste and dictation that need no matching key press.

Observe focus changes

focus and blur report receiving and losing focus without bubbling. focusin and focusout bubble, so a form can observe its fields through one listener. relatedTarget identifies the other element in the change, or null when there is none.

password.addEventListener("focus", () => {
  hint.hidden = false;
});
password.addEventListener("blur", () => {
  hint.hidden = true;
});

The hint is available when the field receives focus, including through Tab. Calling element.focus() moves focus from code; the next keyboard input then goes to that element.

Key Takeaways

  • Use key for the produced meaning and code for physical position.
  • Guard page-wide letter shortcuts against typing contexts and unwanted repeats.
  • Use input to react to edited text, including paste.
  • focusin and focusout bubble; focus and blur do not.
  • relatedTarget reports the other focus destination or origin.

3.2 Activation and pointer movement

A person may use a mouse, a finger, a pen, or a keyboard. Choose an event by the interaction you need. click expresses activation; pointer events describe pressing, moving, and releasing.

Keep actions available through the keyboard

A native button emits click when activated with Enter or Space. A focused link does so with Enter. Wiring an action only to pointerdown or mousedown misses those keyboard activations. Use click for actions such as opening a panel; use pointerdown when work must begin at the press, such as dragging.

A pointer click follows a press and release. When they occur on different elements, the click can target their nearest shared ancestor. dblclick follows two quick clicks; event.detail counts quick successive clicks. auxclick handles non-primary clicks. contextmenu can come from a secondary click, a touch long press, or a keyboard menu command.

Use one pointer family

pointerdown, pointermove, pointerup, and pointercancel cover mouse, touch, and pen. pointerType identifies the device as "mouse", "touch", or "pen"; pointerId distinguishes active pointers, including two fingers.

Older mouse and touch event families still exist. Browsers may emit compatibility mouse events after pointer events. Registering both pointerdown and mousedown for the same action can therefore run it twice.

Match coordinates to the job
CoordinatesOrigin
clientX, clientYTop-left of the visible viewport
pageX, pageYTop-left of the document, including scrolling
offsetX, offsetYThe target element's padding edge

When the document is scrolled down 300 px and a click is 100 px below the viewport top, clientY is 100 and pageY is 400. Keep both positions in the same coordinate frame when calculating movement. A changing nested target can also change the origin of offset coordinates.

button reports which button changed on a down or up event: 0 primary, 1 middle, 2 secondary. buttons reports the currently held combination: primary contributes 1, secondary 2, and middle 4. Thus buttons === 3 means primary and secondary are held; it does not mean the middle button changed.

Choose hover behavior for a whole control

Over/out events bubble and can fire as the pointer crosses child boundaries. Enter/leave events do not bubble and describe entering or leaving the whole element, including its children. A tooltip can avoid child-boundary flicker with pointerenter and pointerleave.

Touch and keyboard users do not hover. Offer essential information on click or focus too. For a visual hover effect with no extra behavior, CSS :hover is simpler than a listener.

Key Takeaways

  • Use click for activation, including native keyboard activation.
  • Pointer events cover mouse, touch, and pen with one event family.
  • Choose coordinates from the same frame for calculations.
  • button identifies a change; buttons reports the held combination.
  • Enter/leave treat an element and its children as a whole.
  • Essential information needs access beyond hover.

3.3 A drag that keeps following the pointer

Dragging needs memory across several events. Store the pointer's starting coordinates and the element's starting position. Each move then applies the difference from that starting point, so the element does not jump to place its corner under the pointer.

Work out the displacement

A box starts at left: 100px; top: 50px. The pointer starts at viewport coordinates 220, 140 and moves to 260, 125. The horizontal change is 40 px; the vertical change is −15 px. Adding those changes to the box's starting position gives left: 140px; top: 35px.

Use consistent client coordinates for both pointer readings. The general calculation is element start plus pointer displacement. Do not add each full displacement to the box's already updated position, which would count earlier movement again.

Remember and capture one pointer

For a margin-free, absolutely positioned box inside a relatively positioned container:

let start = null;
box.addEventListener("pointerdown", (event) => {
  if (start || event.button !== 0) return;
  start = { id: event.pointerId, x: event.clientX,
    y: event.clientY, left: box.offsetLeft,
    top: box.offsetTop };
  box.setPointerCapture(event.pointerId);
});
box.addEventListener("pointermove", (event) => {
  if (!start || event.pointerId !== start.id) return;
  box.style.left = start.left +
    (event.clientX - start.x) + "px";
  box.style.top = start.top +
    (event.clientY - start.y) + "px";
});
function endDrag(event) {
  if (start && event.pointerId === start.id) start = null;
}
box.addEventListener("pointerup", endDrag);
box.addEventListener("pointercancel", endDrag);

The stored ID rejects movement and release from a second pointer. offsetLeft and offsetTop provide starting positions as numbers; styles need the "px" suffix.

Pointer capture keeps subsequent events for that pointer directed to the box even when it moves outside the box. The browser releases capture after pointer up or cancellation. releasePointerCapture(id) can release it earlier.

Handle the browser taking over

A touch gesture may become page scrolling or zooming, causing pointercancel. End the drag there as well as on pointerup; otherwise stale state can keep moving the box later. Apply touch-action: none to the drag surface before interaction to reserve its gesture. Keep it local so the rest of the page can scroll.

Use user-select: none on the draggable surface to avoid selecting text. Set draggable="false" on its images to prevent the native image-drag behavior.

Offer another way to move

Provide keyboard movement for a focused control and clickable or tappable move buttons. Arrow keys alone do not supply a single-pointer alternative to dragging. WCAG 2.2 SC 2.5.7 requires such an alternative unless dragging is essential or is unmodified browser behavior. Native move buttons can serve both keyboard and pointer users.

The separate HTML Drag and Drop API uses dragstart, dragover, and drop to transfer data, such as files. Its event sequence is different from this pointer-based position change.

Key Takeaways

  • Compute each position from the stored start plus pointer displacement.
  • Capture and track the active pointer ID.
  • End the drag on both pointerup and pointercancel.
  • Apply touch and selection rules to the drag surface.
  • Provide keyboard access and a clickable or tappable alternative.
4

Handle forms and validate input before submission

Forms combine several kinds of state: text, numeric values, checked choices, and repeated names. Read those values at the right event, then handle the form’s submission as one action. HTML constraints supply common validation rules, while JavaScript adds relationships between fields and clearer feedback. A useful form preserves the user’s work, explains each correction, and puts focus where the next edit belongs.

4.1 Read values when they change

Form controls expose different kinds of state. A text field's value is a string; a checkbox's checked is a Boolean. Reading the right property is the first step toward reliable calculations and previews.

Convert numbers deliberately

Even <input type="number"> has a string value. With the text "3", adding 1 gives "31". Number(input.value) + 1 gives 4. A number input also exposes valueAsNumber.

Empty input needs its own decision. Number("") gives 0, which can make a missing quantity look like a real zero. Check input.value === "" first, or use valueAsNumber, which gives NaN for an empty number field. Test that result with Number.isNaN().

Read the selected state
ControlProperty to read
Text, textarea, selectvalue
Number input used in arithmeticvalueAsNumber, or convert a nonempty value
Checkboxchecked for on/off state
Radio group named sizeform.elements.size.value for the selected value

A checkbox's value is its configured submission text, or "on" if none is supplied. It does not tell you whether it is checked. A select reports the chosen option's value. To find a selected radio element itself, query input[name="size"]:checked.

Match the event to the update

input reports user edits as they happen, including typing and paste. change reports a committed choice: after a changed text field loses focus, or immediately when a checkbox, radio button, or select is changed.

message.addEventListener("input", () => {
  counter.textContent = 280 - message.value.length +
    " characters left";
});

After "Hello", five characters have been used and the counter reads "275 characters left". A keydown listener would run before the new character enters the value and would miss pasting through a menu. Assigning message.value from code does not fire either input or change; call the update function directly too.

Collect a form's named fields

form.elements.email.value accesses a control by name or ID. Use form.elements["first-name"] for a hyphenated name. Submission uses names: an ID alone does not put a field in the data.

const data = new FormData(form);
const email = data.get("email");
const topics = data.getAll("topics");

FormData collects the current eligible name/value pairs. Unchecked checkboxes, disabled controls, and nameless fields are omitted. get() returns the first value for a name; getAll() preserves repeated values, such as several checked topics. File controls can supply file objects; ordinary text controls supply strings.

Object.fromEntries(data) is convenient for distinct names, but keeps only the last pair for repeated names. Choose getAll() when each selection matters. Because input bubbles, one form listener can read event.target to update whichever field changed.

Key Takeaways

  • Convert a field’s string before arithmetic and handle an empty number separately.
  • Read checkbox state from checked.
  • Use input for live edits and change for committed choices.
  • FormData includes eligible named controls, not every control.
  • getAll() preserves repeated names; object conversion does not.
  • Programmatic value assignment does not emit editing events.

4.2 Handle the form’s submission

Submission belongs to the form. Listen for its submit event to handle a submit-button click, Enter in a single-line field, and a scripted requestSubmit() through one route.

Replace navigation with a page update

Normally a form sends data to its action URL and loads the response. Without an action, the destination is the current page. The default GET method places the data in the URL. To update the current page with JavaScript, cancel that default action:

const form = document.querySelector("#new-todo");
form.addEventListener("submit", (event) => {
  event.preventDefault();
  const data = new FormData(form);
  const title = data.get("title").trim();
  if (title === "") return;
  addTodo(title);
  form.reset();
});

This form has an enabled text field named title. The listener trims surrounding spaces, ignores an empty result, adds an item, and resets fields to their initial values. reset() does not always mean empty; a control with an initial value returns to that value.

If the item appears briefly and disappears as the page reloads, check whether preventDefault() is missing. A button click listener alone is the wrong boundary for form handling, especially when another route submits the form.

Know what triggers submission

Implicit submission from Enter can click the enabled default submit button. Enter in a textarea inserts a newline. A button inside a form defaults to a submit button when its type is omitted, so a "Show password" button should explicitly use type="button".

When there are multiple submit buttons, event.submitter identifies the one used. It can be null for routes without a specific button, so code should check before reading it:

const button = event.submitter;
const intent = button ? button.value : "save";
const data = button ? new FormData(form, button) :
  new FormData(form);

A named submitter such as name="intent" value="publish" contributes that pair when passed to FormData. A FormData(form) call by itself does not include a submit button's name/value pair.

Preserve the validation route
Submission routeAutomatic validationsubmit listener
Submit activation or requestSubmit()Runs unless skipped by validation attributesRuns if validation allows it
form.submit()SkippedSkipped

Built-in validation runs before the submit event. If an empty required field fails, the browser reports the problem and does not dispatch submit. A custom check only inside that listener will not run on this failed attempt. novalidate on the form disables the automatic validation step.

Use requestSubmit() when code needs the normal validation and submit-event path. It requests submission without dispatching a button click. Pass a particular submit button to requestSubmit(button) when that button's submission settings should apply.

Key Takeaways

  • Handle submit on the form, covering user and scripted submission routes.
  • Cancel the default submission when JavaScript replaces navigation.
  • Give non-submit form buttons type="button".
  • Use submitter to distinguish submit buttons and include its data when needed.
  • Validation precedes submit; requestSubmit() preserves that route.
  • submit() bypasses validation and the submit event.
  • reset() restores initial values.

4.3 Let HTML describe validation rules

HTML can describe many input rules without a custom JavaScript test. The browser checks those rules during submission and exposes the results for your code. Start with the constraints that match the data you need.

Choose a constraint
AttributeRule
requiredA value or required selection must be supplied.
type="email" or "url"The value must have the expected syntax.
min, max, stepNumeric or date values must fit limits and increments.
minlength, maxlengthUser-entered text must fit a length rule.
patternA nonempty text value must match a whole-value expression.

For pattern="[0-9]{5}", "12345" passes; "1234", "123456", and "12345a" fail. The pattern covers the whole value, not any matching substring. Empty input skips the pattern check, so add required when omission is not allowed.

Email validation checks syntax, not whether an inbox exists. Both "ann@example.com" and "ann@example" pass the browser's email rule, while "ann.example.com" does not. An additional pattern can impose a more specific format when the application requires it.

Read the reason for failure

A field's validity object contains Boolean flags. valueMissing, typeMismatch, patternMismatch, tooShort, and tooLong describe missing or malformed text. rangeUnderflow, rangeOverflow, and stepMismatch describe numeric constraints. badInput covers an input the browser cannot turn into a usable value, and customError records a custom validity message. valid is true when no constraint fails.

For a number field with min="0" max="10" step="2", 12 is above the maximum, 7 is off the allowed step, and 6 passes. More than one flag can be true; do not assume every invalid field has exactly one problem.

Check quietly or report visibly

checkValidity() returns a Boolean and fires invalid on failing controls without showing the browser's message. reportValidity() checks and also reports the problem to the user. Call either on a field or a form. validationMessage gives the message in the browser's language. The invalid event does not bubble.

Text-length constraints apply to user-entered text. Assigning input.value = "ab" in code does not make tooShort true just because minlength is 3. maxlength normally also prevents further user typing beyond its limit.

Show errors at a helpful time

:invalid applies immediately, so an untouched required field already matches. :user-invalid waits for user interaction or a submission attempt and can avoid an early red field. Use text to explain the error as well as a visual style.

novalidate skips automatic submission validation for the form; formnovalidate skips it for one submit button. They do not remove constraints or disable checkValidity(). Browser checks help users correct mistakes; the server must validate received data again because a request can bypass the page.

Key Takeaways

  • Use HTML constraints for required values, syntax, ranges, steps, length, and patterns.
  • A pattern matches the whole nonempty value; use required to reject empty input.
  • Read validity flags to identify why a field failed.
  • checkValidity() checks; reportValidity() also displays feedback.
  • Validation-skipping attributes leave the validity API usable.
  • Validate received data on the server too.

4.4 Custom rules and useful error messages

A rule involving two fields needs code. For example, a confirmation must match a password, or an end date must follow a start date. Keep the rule separate from how and when its error is displayed.

Set and clear a custom error

A nonempty string passed to setCustomValidity() marks the field invalid and sets customError. Passing "" clears that custom failure; required, pattern, or other constraints can still fail.

function checkMatch() {
  const matches = confirm.value === password.value;
  confirm.setCustomValidity(matches ? "" :
    "The passwords do not match.");
}
password.addEventListener("input", checkMatch);
confirm.addEventListener("input", checkMatch);

Recheck both dependencies. Changing the first password can break a previously correct confirmation. A message that is never cleared keeps the field invalid after the person fixes the mismatch. Run the initial check too when fields can start with values.

Date inputs provide ISO strings such as "2026-03-10", regardless of the displayed local date format. Nonempty values from these controls can be compared as text. If the rule says the end must be after the start, reject end.value <= start.value, including equality. Handle missing dates through required or a separate empty-value check.

Explain the next useful action

A message should identify the problem and its correction. For a missing email, say "Enter your email address." For malformed email text, say "Enter an email address in the form name@example.com." Keep the entered value so the person can edit it.

A required name field accepts spaces because they form a nonempty string. Check name.value.trim() === "" when surrounding whitespace should not count. Clear this custom error when meaningful text appears.

Connect messages to controls
<label for="email">Email</label>
<input id="email" name="email" type="email"
  required aria-describedby="email-error">
<p id="email-error"></p>

Place the error in that paragraph and set aria-invalid="true" while the field has an error. Clear the text and remove or set aria-invalid to "false" after correction. aria-describedby connects the explanation to the field for assistive technology. Use text; a red border alone cannot tell everyone what failed.

Choose the validation flow

For fully custom messages, add novalidate and use a form submit listener. Update custom rules, inspect the fields' validity, write their messages, and prevent submission when any fail. Move focus to the first invalid field or to a linked error summary. If JavaScript handles successful submission locally too, cancel that default submission as well.

Avoid reporting incomplete email text on every initial keystroke. A useful sequence is to show errors after blur or submission, then recheck an already reported error on input.

Key Takeaways

  • Nonempty custom messages make a field invalid; "" clears only that custom error.
  • Recheck every field a cross-field rule depends on.
  • For a strict date order, reject equality as well as an earlier end.
  • Explain a correction in text and associate it with the field.
  • Clear visible errors and ARIA error state after correction.
  • Custom submission feedback needs an explicit validity and focus flow.
5

Build custom controls that everyone can use

Choosing a native element supplies much of a control’s behavior before any listener is written. Custom controls need the same care: a clear name, keyboard access, visible focus, and state that matches what assistive technology reports. Groups need predictable movement between items. When content opens, closes, or disappears, a deliberate focus destination lets the user continue the interaction without searching the page again.

5.1 Start with a native control

A control needs more than a click response. People must be able to find it, reach it, understand its purpose, operate it, and see where focus is. Native HTML supplies much of this behavior already.

Choose the element for the job

Use a <button> for an action, an <a href> for navigation, and a labeled form control for input. <details> with <summary> supplies a disclosure. Style these elements with CSS to match the page. A focused button activates through Enter or Space without an extra key listener.

A div with a click listener has none of the button's built-in semantics or keyboard behavior. Giving it role="button" changes what assistive technology announces, but does not make it focusable or turn key presses into clicks.

NeedNative buttonCustom div acting as a button
RoleBuilt inAdd role="button".
Keyboard focusBuilt inAdd tabindex="0".
Enter and SpaceBuilt inAdd keyboard handling.
Accessible nameUsually its textSupply meaningful text or an ARIA name.
Disabled behaviordisabledCheck aria-disabled in code.
Keep the focus order predictable

tabindex="0" adds an element to the normal document-order Tab sequence. tabindex="-1" permits programmatic focus without adding a Tab stop. Positive values move elements ahead of the usual sequence and are difficult to maintain; avoid them.

Neither setting adds activation behavior. A focusable custom button still needs a listener for its keyboard operation. A role, a Tab stop, and a callback solve different parts of the interaction.

Give every control a name

A screen reader uses an accessible name to distinguish controls. A text button can use its visible text. A text field or select needs an associated label, such as <label for="email">Email</label> paired with id="email". aria-labelledby can refer to visible text elsewhere; aria-label can supply a name when there is no suitable visible text.

An icon-only close button can be <button type="button" aria-label="Close">×</button>. The name describes the action instead of making the user infer what the symbol means.

Show and enforce state

Keep a visible focus indicator. If you replace the default outline, supply a clear replacement with :focus-visible, for example outline: 3px solid #1a73e8. Removing it without replacement hides the keyboard user's position.

A native disabled button leaves the Tab sequence and cannot be activated normally. aria-disabled="true" only communicates unavailability. A custom control's click and keyboard action must check that state and return without acting. Centralizing that check in the activation function keeps both input routes consistent.

Key Takeaways

  • Use a native element that matches the action or input.
  • An ARIA role supplies semantics, not behavior.
  • Use tabindex="0" for normal Tab access and -1 for code-only focus.
  • Provide an accessible name and a visible focus indicator.
  • Enforce aria-disabled in the activation code.

5.2 Keep keyboard behavior and state together

A custom control should give the same result regardless of how it is activated. Keep the action in one function, route keyboard and pointer use to it, and update the state that both styling and assistive technology read.

Give a custom button keyboard activation
fakeButton.addEventListener("click", activate);
fakeButton.addEventListener("keydown", (event) => {
  if (event.key === "Enter" || event.key === " ") {
    event.preventDefault();
    activate();
  }
});

This is for a custom focusable button. Canceling Space prevents page scrolling during its action. A native button already produces clicks from keyboard activation, so adding a second key route to it can run the action twice.

Choose the state that matches the control
ControlState
Toggle button with a stable labelaria-pressed="true" or "false"
Button opening a disclosurearia-expanded="true" or "false"
Custom checkbox or switcharia-checked="true" or "false"

A "Mute" toggle keeps that label and changes aria-pressed. If the design changes its action label to "Unmute", leave out aria-pressed. A checkbox uses role="checkbox" and Space to toggle; a switch uses role="switch" for on/off state.

Attributes contain strings. Compare getAttribute("aria-expanded") with "true", not the Boolean true:

detailsButton.addEventListener("click", () => {
  const open = detailsButton.getAttribute(
    "aria-expanded") === "true";
  detailsButton.setAttribute("aria-expanded", String(!open));
  panel.hidden = open;
});

When open was true, the new state is false and the panel becomes hidden. aria-controls can identify the panel by ID. Native <details> and <summary> supply disclosure behavior without this script.

Use the state attribute as the styling source, such as [aria-pressed="true"], so the visible and announced states change together. Close popups on Escape and return focus to their opener.

Give a group one Tab stop

Tabs, toolbars, and radio groups use Tab to enter or leave the group and arrow keys to move inside it. Roving tabindex keeps the current item at 0 and other items at -1. On movement, set the old item to -1, the new item to 0, and focus the new item.

For three items, right from index 2 wraps with (2 + 1) % 3 to 0. Left from 0 uses (0 - 1 + 3) % 3 to reach 2. Add the length before the remainder operation because JavaScript's -1 % 3 is −1.

Check with the keyboard: enter the control, see focus, activate it with the appropriate keys, move within a group, and close a popup with Escape. Check the visible state and the ARIA state on each change.

Key Takeaways

  • Route custom button click, Enter, and Space to one action.
  • Native buttons already provide keyboard click activation.
  • Choose aria-pressed, aria-expanded, or aria-checked for the matching state.
  • Compare ARIA values as strings and update styling from the same state.
  • Roving tabindex gives a group one Tab stop and arrow-key movement.
  • Escape closes a popup and restores its opener’s focus.

5.3 Move focus when the page changes

Focus decides where the next keyboard action goes. If the focused control disappears, the user can lose their place. Treat focus as part of the page update, especially when opening a dialog, deleting an item, or reporting an error.

Open a dialog and return to its opener

Remember the focused element before opening. Move into the new interaction, then restore focus on close:

let opener;
openButton.addEventListener("click", () => {
  opener = document.activeElement;
  dialog.showModal();
});
dialog.addEventListener("close", () => {
  if (opener && opener.isConnected) opener.focus();
});

showModal() on a native dialog moves focus inside and makes the rest of the page inert, so it cannot be clicked or focused while the modal is open. Escape normally causes a cancel event followed by closure and a close event. A cancel listener can prevent closing if the application needs that behavior.

Choose a useful initial focus location in the dialog, such as its first relevant control. For a custom popup, code must move focus deliberately. When the original opener no longer exists, choose a nearby useful control instead of trying to focus a detached element.

Make a useful destination focusable

A heading or error summary is not ordinarily focusable. Give it tabindex="-1" before calling .focus(). It can receive the deliberate focus move without becoming an extra stop during normal Tab navigation. Use 0 only when it belongs in that normal sequence.

Preserve position after deletion

Before removing a list item whose delete button has focus, find the next useful destination. Focus the next item's button; if there is no next item, use the previous item's button. If the list becomes empty, focus its heading with tabindex="-1".

Finding the destination before removal avoids losing the relationship to neighboring items. Do not send focus to the top of the document after every deletion; the person should be able to continue working near the change.

Keep focus within a continuing interaction

A popup may close when focus leaves its whole area. The bubbling focusout event provides the destination:

menu.addEventListener("focusout", (event) => {
  if (!menu.contains(event.relatedTarget)) {
    menu.hidden = true;
  }
});

Moving between two menu controls does not close it because the destination remains inside. A null destination is outside too; contains(null) is false. Do not automatically return focus to the opener when the user has intentionally moved to another page control.

Make changes predictable

After failed custom form validation, focus the first invalid field or an error summary that links to the errors. Built-in validation already focuses a failing field. Keep error text available so the person knows what to correct.

Do not jump to a new field while the user is typing or navigate as soon as a select changes. Let a clear action confirm the change. Focus movement should support a deliberate action or recovery, not interrupt ordinary input.

Key Takeaways

  • Remember the opener and restore an available destination when a dialog closes.
  • Use tabindex="-1" for a heading or summary focused from code.
  • After deletion, focus a neighboring item or the empty list’s heading.
  • Use relatedTarget to distinguish internal focus moves from leaving a popup.
  • Focus an error destination after failed validation; avoid unexpected focus jumps.

Event Listeners examples

5 Walkthroughs

1

Build and inspect event listeners

13 steps
Register a counter, inspect nested clicks, and repair listener lifetimes.
  1. 1
    IntroductionMake a button respond

    A listener connects an event to a function. Build a small counter by choosing the button, registering its action, and keeping the total between calls. Then inspect a nested click and control the listener’s lifetime.

    Watch the difference between setup code, which runs now, and the callback, which the browser calls when the chosen event happens.

  2. 2
    ExampleStore the total outside the callback

    Select the existing button and store its count before registration:

    const button = document.querySelector("#like");
    let likes = 0;
    button.addEventListener("click", () => {
      likes = likes + 1;
      button.textContent = "Likes: " + likes;
    });
    

    Each call adds to the previous total. After three clicks the label is "Likes: 3". A count declared inside the callback would start again on every call.

  3. 3
    TipPass the action without calling it

    addEventListener("click", showMenu) passes a function for later use. showMenu() would call it during setup. When arguments are needed, use () => showMenu("main").

    Load the script after the button exists, such as with defer, and keep the exact event spelling. A listener for "Click" does not receive ordinary "click" events.

  4. 4
    ChallengeRepair a registration

    A button must open the tips panel on every activation. Setup currently uses button.addEventListener("click", openTips()). What should change, and why?

    ---reveal---

    Pass openTips without parentheses. That supplies the callback to run for a later click. Calling openTips() during setup opens the panel immediately and passes its result instead of preserving the action for the event.

  5. 5
    ExplanationRead the two element properties

    For a button containing an image, clicking the image makes it the target. In the button’s listener, currentTarget is the button.

    The target says where the event began; the current target says where this callback was registered. That difference explains why reading a button’s dataset from target can fail when the click lands on its image.

  6. 6
    ExampleRead the owning button’s data

    Suppose button has data-id="7" and contains an icon:

    button.addEventListener("click", (event) => {
      console.log(event.currentTarget.dataset.id);
    });
    

    The result is "7" for an icon click as well as a button-background click. The same expression works in an arrow or regular function listener. An arrow function’s this does not automatically identify the button.

  7. 7
    ChallengeIdentify the listener owner

    A section contains a button with a span. A click listener is attached only to the section, and the span is clicked. What do target and currentTarget identify during that callback?

    ---reveal---

    target identifies the span where the event began. currentTarget identifies the section where the listener was attached. The event’s origin is not replaced by the ancestor that receives it.

  8. 8
    ExplanationKeep the reference for removal

    Removal needs the same event type, callback object, and capture setting:

    const handler = () => closePanel();
    button.addEventListener("click", handler);
    button.removeEventListener("click", handler);
    

    An anonymous function can be removable when saved this way. Writing a new () => closePanel() in the removal call creates another object and does not match the earlier registration.

  9. 9
    ConnectionEnd several registrations together

    A temporary feature can pass one AbortController signal to several registrations. Calling controller.abort() removes those listeners, including saved or anonymous callbacks.

    Use a fresh controller for another lifetime. For a single one-time response, { once: true } removes that registration after it runs. Neither option prevents distinct callbacks from accumulating before their lifetime ends.

  10. 10
    ChallengeDiagnose repeated setup

    Opening a panel registers a new anonymous document key listener each time. Closing hides the panel but leaves the listener. After three openings, a key triggers three actions. How can the lifecycle be repaired?

    ---reveal---

    Remove the registrations when the panel closes, for example by aborting their shared signal, and create a new controller on reopening. Alternatively, save a callback and remove that same reference. Hiding the panel does not remove a document listener.

  11. 11
    Key InsightDispatch a notification immediately

    To notify other code, create a CustomEvent with its data in detail. Set bubbles: true if ancestors should hear it:

    item.dispatchEvent(new CustomEvent("cart:add", {
      detail: { id: 42 }, bubbles: true
    }));
    

    The listeners run before the call returns. The dispatched event has isTrusted false. It is a notification your code sends, not a request for the browser to wait for a user action.

  12. 12
    ChallengeCombine origin and lifetime

    A button’s click handler uses its own data-key and should act only on the first activation. It contains an SVG icon. What property and option keep both requirements correct?

    ---reveal---

    Read event.currentTarget.dataset.key in the direct button listener and register with { once: true }. The property identifies the owning button even for an icon click. The option removes that registration after its first call.

  13. 13
    RecapCheck a listener in three places

    When an interaction fails, inspect setup, the callback’s data, and its lifetime. Confirm that the element exists and the event name is exact; inspect target and currentTarget during the call; then look for missing removal or repeated registration. These checks distinguish a listener that never receives an event from one that receives it correctly but acts on the wrong element.

2

Trace events and delegate actions

13 steps
Predict listener order, replace browser defaults, and handle new list items.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionTrace one click through a card
  2. 2
    ExampleRead the upward path
  3. 3
    ExplanationAdd the downward path
  4. 4
    ChallengeTrace a smaller tree
  5. 5
    ExplanationReplace navigation
  6. 6
    Key InsightStopping travel is a different choice
  7. 7
    ChallengeKeep a parent observer active
  8. 8
    ExplanationMove the listener to the list
  9. 9
    ExampleFind and guard the removal button
  10. 10
    ChallengeHandle a nested icon
  11. 11
    TipUse an event that reaches the container
  12. 12
    ChallengeKeep opening and closing separate
  13. 13
    RecapChoose a boundary for each action
3

Handle keys and build a pointer drag

16 steps
Choose input events, work with coordinates, and make a drag robust and usable.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionChoose the right input level
  2. 2
    ExplanationSeparate meaning from position
  3. 3
    TipProtect typing and handle repeats
  4. 4
    ChallengeChoose an event for a preview
  5. 5
    ConnectionLet activation cover the keyboard
  6. 6
    ExampleConvert the coordinate frame
  7. 7
    ChallengeInspect a scrolled click
  8. 8
    Key InsightKeep hover from becoming the only route
  9. 9
    ExampleCalculate one drag movement
  10. 10
    ExplanationStore the start and capture its pointer
  11. 11
    ExampleApply displacement on every move
  12. 12
    TipEnd even if the browser takes over
  13. 13
    ChallengePredict a continuing drag
  14. 14
    ConnectionOffer movement without dragging
  15. 15
    ChallengeDiagnose two drag failures
  16. 16
    RecapMatch the event to the task
4

Build a complete form interaction

16 steps
Read fields, handle submission, and guide correction through native and custom validation.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionFollow a form from editing to submission
  2. 2
    ExampleRead a number without concatenation
  3. 3
    ExplanationRead choices and repeated names
  4. 4
    ChallengePreserve every selected topic
  5. 5
    ExplanationHandle submission at the form
  6. 6
    ExampleRead, add, and reset
  7. 7
    ChallengeKeep the normal submission route
  8. 8
    ExampleRead independent constraint failures
  9. 9
    Key InsightA pattern does not require a value
  10. 10
    ChallengeCheck a stepped range
  11. 11
    ExplanationAdd a rule connecting two fields
  12. 12
    TipSeparate validity from error timing
  13. 13
    ChallengeRepair a stale date rule
  14. 14
    ConnectionTake responsibility when skipping browser feedback
  15. 15
    ChallengeComplete the recovery flow
  16. 16
    RecapCheck both valid and invalid routes
5

Build controls and preserve keyboard position

14 steps
Choose native behavior, synchronize custom state, and manage focus through page changes.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionMake an interaction work through the keyboard
  2. 2
    ExplanationUse the behavior HTML already supplies
  3. 3
    TipSet focusability for the intended route
  4. 4
    ChallengeRepair an unreachable action
  5. 5
    ExampleKeep one activation function
  6. 6
    ExplanationExpress the right kind of state
  7. 7
    ChallengeInvert a disclosure correctly
  8. 8
    ExampleMove one Tab stop inside a group
  9. 9
    ConnectionRemember where a dialog came from
  10. 10
    ExampleKeep an internal focus move inside
  11. 11
    ChallengeChoose focus after removal
  12. 12
    ChallengeTrace a group update
  13. 13
    ChallengeKeep a popup predictable
  14. 14
    RecapTest the complete control sequence

Event Listeners practice questions

50 Practice Questions in 5 sets

Set 1: Set up and inspect event listeners10 Practice Questions

Question 1 of 10

A button should call showHints only when activated. What registration achieves that?
  1. button.addEventListener("click", showHints());
  2. button.addEventListener("click", showHints);
  3. button.addEventListener("onclick", showHints);
  4. button.addEventListener("click", () => showHints);
Show answerHide answer
Answer: B
Pass the function reference so the browser can call it when click occurs. Writing showHints() calls it during registration and passes its return value. A wrapper written as () => showHints only returns the function; it never invokes the intended action.

Question 2 of 10

A head script selects a button whose markup appears later and then fails on addEventListener. What loading change lets selection wait until the HTML is parsed?
  1. Rename the listener parameter to event.
  2. Use onclick as the registered event name.
  3. Move the selection inside an immediate function call.
  4. Add defer to the external script tag.
Show answerHide answer
Answer: D
An external script loaded with defer runs after document parsing, so the button exists when selected. The original selection returned null because the markup had not been parsed. Calling an immediate function from the same early script does not change that timing.

Question 3 of 10

An external script uses const items = document.querySelectorAll(".choice"); items.addEventListener("click", choose);. All choices already exist. Why does this fail, and how should setup change?
  1. The result is a NodeList; register on each item.
  2. The result is a NodeList; use the event name "onclick".
  3. The callback lacks parentheses; register choose() on the list.
  4. The result is an element; register a capture listener on it.
Show answerHide answer
Answer: A
The selection returns a collection, which has no addEventListener method. Iterate through its elements and register choose on each, or delegate from an appropriate ancestor. Calling choose() does not turn the collection into an event target and also invokes the function too early.

Question 4 of 10

Two distinct functions are assigned in order: button.onclick = first; button.onclick = second;. What happens when the button is clicked once?
  1. first runs, followed by second.
  2. Only first runs.
  3. Only second runs.
  4. second runs, followed by first.
Show answerHide answer
Answer: C
The onclick property stores one callback, so the second assignment replaces the first. Only second runs from that property on the click. Registering two distinct functions with addEventListener would allow both to coexist, but these statements assign a single handler property.

Question 5 of 10

A click listener is registered on a button containing a span. The span is clicked. What are target and currentTarget during that listener?
  1. target: button; currentTarget: span.
  2. target: span; currentTarget: button.
  3. target: span; currentTarget: span.
  4. target: button; currentTarget: button.
Show answerHide answer
Answer: B
The click originates on the span, so the event’s target identifies that span. The listener was attached to the button, making the button currentTarget during the call. The listener’s location does not replace the event origin when a nested element is clicked.

Question 6 of 10

An arrow-function listener on <button data-code="r8"><span>Run</span></button> reads event.target.dataset.code. Why can it return undefined, and what direct-listener expression reads the button’s code?
  1. The span can be the target; read event.currentTarget.dataset.code.
  2. The button loses its data; read event.type.dataset.code.
  3. The span can be the target; read this.dataset.code in the arrow.
  4. The button is the target; read event.currentTarget.value.code.
Show answerHide answer
Answer: A
Clicking the span makes it the target, and the span has no data-code. currentTarget identifies the button that owns this listener, so its dataset contains the code. An arrow function does not acquire the button as this, so switching to this.dataset does not reliably fix the problem.

Question 7 of 10

A listener logs its event object. After the interaction finishes, expanding the log shows currentTarget: null. How can the developer inspect the listener owner during the call?
  1. Read event.target after the call as the listener owner.
  2. Add once: true so the object keeps its current target.
  3. Log the event object again after dispatch completes.
  4. Log event.currentTarget inside the listener.
Show answerHide answer
Answer: D
The browser sets currentTarget for each listener call and resets it afterward. Log the property directly inside the callback to see the owner during that call. A later event-object inspection can show the reset state; target is the origin and may be a different element.

Question 8 of 10

A callback was registered with { capture: true }. The same type and function are passed to removeEventListener, but no third argument is given. Why does the callback remain?
  1. Removal requires every original option to be repeated.
  2. Capture callbacks can only be removed with abort().
  3. Removal looks for the non-capturing registration.
  4. Removal matches the function’s source text instead of identity.
Show answerHide answer
Answer: C
The capture setting participates in the removal match. Omitting it requests removal of a non-capturing registration, leaving the capture listener intact. Repeat true or { capture: true }; there is no need to repeat once, passive, or signal to identify that listener.

Question 9 of 10

Every click on "Begin" registers board.addEventListener("click", () => mark()). Beginning twice makes one board click mark twice. What setup change prevents that accumulation?
  1. Add once: true to each newly created board callback.
  2. Remove a newly written () => mark() before adding it.
  3. Set board.onclick = null before adding each callback.
  4. Register one saved callback during initial setup.
Show answerHide answer
Answer: D
Registering once avoids creating another callback on every Begin click. A saved callback can also be removed or reused because its identity stays stable. Adding once to two distinct callbacks still leaves two callbacks waiting for the next click, so both can mark before removing themselves.

Question 10 of 10

An otherwise empty button gets these registrations, then is clicked twice. How many times does log run in total?

button.addEventListener("click", log, { once: true });
button.addEventListener("click", log);
button.addEventListener("click", log, true);
button.removeEventListener("click", log);
  1. 2
  2. 1
  3. 3
  4. 4
Show answerHide answer
Answer: A
The second registration duplicates the first function/type/capture combination, so it does not create a new listener. Removal then removes that non-capture registration, including its once setting. The separate capture registration remains and runs once on each of the two clicks, giving two calls.
Set 2: Trace nested actions and delegation10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

A button is inside a section inside a main element. Each has an ordinary click listener. What is their order when the button is clicked?
  1. Main → section → button.
  2. Section → button → main.
  3. Button → section → main.
  4. Button → main → section.
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

A container has capture and ordinary click listeners. Its child button has an ordinary click listener. What is logged when the button is clicked?
  1. Container capture → button → container ordinary.
  2. Button → container capture → container ordinary.
  3. Container ordinary → button → container capture.
  4. Container capture → container ordinary → button.
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

A form has an ordinary focus listener, but focusing its child input does not call it. How can the form observe descendant focus without using capture?
  1. Listen for blur on the form.
  2. Add bubbles: true as a listener option.
  3. Read currentTarget in the existing focus callback.
  4. Listen for focusin on the form.
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

A link listener calls only event.stopPropagation(). What happens to its normal navigation?
  1. The navigation is canceled.
  2. The link still navigates.
  3. The link needs two clicks.
  4. Ancestor listeners block it.
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

A form’s submit listener calls preventDefault(). An ordinary submit listener also exists on a surrounding section. What can the section’s listener observe?
  1. It is skipped because cancellation stops bubbling.
  2. It runs and reads defaultPrevented as false.
  3. It runs only if it also calls preventDefault().
  4. It runs and can read defaultPrevented as true.
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

A button opens a menu. A document click listener then closes it on the same click. The menu must also close on genuine outside clicks, and document-level logging must keep working. What change meets all three needs?
  1. Close only when neither the menu nor its opener contains the target.
  2. Stop propagation on every menu-button click before opening.
  3. Cancel the menu-button click before toggling the hidden state.
  4. Close only when the menu itself does not contain the target.
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

A delegated toolbar listener gets a click whose target is an SVG inside a button. What expression finds the button from that target?
  1. event.target.closest("button")
  2. event.currentTarget.closest("button")
  3. event.target.matches("button")
  4. event.target.querySelector("button")
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

An empty list receives a delegated click listener. Code later appends a matching remove button inside an item. What registration is needed for the new button?
  1. Register the same callback on the new button as well.
  2. No new registration; its click reaches the list.
  3. Remove and re-add the list listener after insertion.
  4. Dispatch DOMContentLoaded again after adding the button.
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

A delegated listener uses closest(".action") inside a container nested in an outer .action element. A gap click finds that outer element. What guard keeps handling local?
  1. Return when event.currentTarget === container.
  2. Return only when the match equals event.target.
  3. Return when the match has no child elements.
  4. Return when the match is outside container.
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

A list delegates removal clicks with closest(".remove") and containment guards. New buttons work, except one row whose icon listener calls stopPropagation(). The same row’s button padding works. Why does this difference occur?
  1. The containment guard rejects every descendant SVG.
  2. New rows require direct listeners on all their icons.
  3. Icon clicks are stopped before reaching the list.
  4. closest searches downward when the target is an icon.
Show answerHide answer

Save this lesson to see this answer.

Set 3: Handle keyboard, pointer, and drag input10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

The document is scrolled down 420 px. A pointer event reports clientY 75. What is its document-relative vertical coordinate?
  1. 495 px
  2. 345 px
  3. 75 px
  4. 420 px
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

A shortcut must follow the produced letter across keyboard layouts. What property should it test?
  1. event.code
  2. event.repeat
  3. event.button
  4. event.key
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

A preview misses text pasted from a context menu and shows typing one character late. It currently reads the field in keydown. What change addresses both symptoms?
  1. Read the field in a keyup listener.
  2. Read event.code in the keydown listener.
  3. Read the field in an input listener.
  4. Call preventDefault() for each keydown.
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

A document shortcut on plain "n" should open a panel once per press without interrupting text entry. Holding N in an editable div currently opens it repeatedly. What conditions belong before opening?
  1. Skip editable targets and repeated keydown calls.
  2. Skip repeated calls only; allow all editable targets.
  3. Skip input elements only; allow all repeated calls.
  4. Cancel every key first, then skip editable targets.
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

A native button’s action works by mouse but not when activated with Space. Its only listener uses pointerdown. What event should handle the action?
  1. pointermove
  2. click
  3. mousedown
  4. pointerenter
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

A help card hides its tooltip when the pointer moves from the card background into a child badge. Its hide listener uses pointerout. What event better detects leaving the whole card?
  1. pointerover
  2. pointermove
  3. pointerleave
  4. pointerdown
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

A box starts at left: 65px; top: 90px. Its pointer starts at 180, 210 and moves to 155, 245 in client coordinates. What position should the box reach?
  1. left: 155px; top: 245px
  2. left: 90px; top: 55px
  3. left: 25px; top: 35px
  4. left: 40px; top: 125px
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

A box stores the correct starts and captures its first touch. A second touch then overwrites the starts, and releasing either touch ends movement. What state change addresses both faults?
  1. Capture each new pointer and overwrite the starts on every down.
  2. Store the active pointer ID and guard down, move, and end.
  3. Use page coordinates and let any pointer end the interaction.
  4. Ignore pointercancel and keep all movement listeners passive.
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

A touch drag has pointer capture and correct arithmetic, but a browser scroll gesture cancels it. The next pointer movement can still move the box. What paired changes address gesture ownership and stale state?
  1. Apply local user-select: none; clear state only on pointerup.
  2. Capture the pointer again on every move; ignore cancellation.
  3. Apply local touch-action: none; clear state on pointercancel.
  4. Cancel every document keydown; clear state when a key is released.
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

A reorderable list supports dragging and arrow-key movement. A touch user cannot drag precisely. What addition gives that user a suitable alternative?
  1. Clickable or tappable move buttons.
  2. A longer drag handle on each item.
  3. Instructions describing the arrow keys.
  4. A hover-only menu of movement hints.
Show answerHide answer

Save this lesson to see this answer.

Set 4: Read form data and diagnose validation10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

A number input contains "6". What does input.value + 2 produce?
  1. The string "62".
  2. The number 8.
  3. The number 62.
  4. The string "8".
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

A form has checked topic inputs named topic with values "art" and "code" in document order, an unchecked topic valued "music", and a disabled named note. What does new FormData(form).getAll("topic") return, and why is object conversion risky?
  1. ["art", "code", "music"]; object conversion keeps only the first repeated name.
  2. ["art", "code"]; object conversion adds disabled controls to the collection.
  3. ["code"]; object conversion collects all topic values into an array.
  4. ["art", "code"]; object conversion keeps only the last repeated name.
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

A "Show details" button inside a form should only reveal a panel. What explicit type prevents it from submitting the form?
  1. type="submit"
  2. type="reset"
  3. type="text"
  4. type="button"
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

A form has an empty required field. A toolbar calls form.submit(), and the form’s listener that prevents navigation never runs. What call restores the expected flow, and what happens before that field is fixed?
  1. Use requestSubmit(); the submit listener runs before validation blocks it.
  2. Use submit() again; the second call runs the listener after validation.
  3. Use requestSubmit(); validation blocks submission before submit fires.
  4. Use dispatchEvent(new Event("submit")); it performs normal built-in validation.
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

A text input has pattern="[A-Z]{3}" and is optional. Does an empty value pass this pattern constraint?
  1. No; every pattern implies required input.
  2. Yes; the pattern skips empty values.
  3. No; the empty value causes typeMismatch.
  4. Yes; the pattern applies only to typed digits.
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

A number field has min="2" max="14" step="4". The user enters 12. What fails?
  1. stepMismatch, because allowed steps are 2, 6, 10, 14.
  2. rangeOverflow, because 12 exceeds the last step below 14.
  3. No constraint; 12 is between the minimum and maximum.
  4. rangeUnderflow, because the step starts at zero.
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

A form uses novalidate and checks fields in document order on submit. Its quantity is required with min="2" max="14" step="4"; it now contains 10 and an old custom message was cleared with setCustomValidity(""). The next field is a required email that is empty. What should block this attempt and receive focus?
  1. The quantity, because clearing a custom message leaves it invalid.
  2. The email, because it is missing; the quantity passes its constraints.
  3. Neither field, because novalidate disables the validity API.
  4. The quantity, because 10 misses the step of four from its minimum.
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

A password confirmation was invalid. The values now match, but an old nonempty custom validity message remains. What must the checking function do?
  1. Set confirm.value to itself again.
  2. Set confirm.required = false only.
  3. Call confirm.setCustomValidity("").
  4. Call form.requestSubmit() repeatedly.
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

A reservation requires a nonempty end date strictly after its start date. Both date inputs are required. The current check rejects only end.value < start.value and runs only on end edits. What two changes fix the remaining cross-field faults?
  1. Reject <= and recheck when either date changes.
  2. Reject < and recheck only when the end loses focus.
  3. Reject >= and recheck when either date changes.
  4. Reject === and recheck only when the start loses focus.
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

An invalid email message appears while the field is being corrected. What timing removes it promptly without showing errors from the first keystroke on untouched fields?
  1. Recheck only on blur, even after an error has been shown.
  2. Show every empty field as erroneous at initial page load.
  3. Clear all messages on any keydown before reading validity.
  4. Recheck reported errors on input; start feedback on blur or submit.
Show answerHide answer

Save this lesson to see this answer.

Set 5: Build keyboard access and preserve focus10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

A div has role="button" and a click listener. What does the role itself provide?
  1. A normal Tab stop and visible focus ring.
  2. Button semantics for assistive technology.
  3. Enter and Space activation without more code.
  4. Native disabled behavior from aria-disabled.
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

A form error heading should receive focus after a failed submission but should not add a stop during normal Tab navigation. What setup fits?
  1. Set tabindex="1" and call .focus().
  2. Set role="alert" and rely on Tab focus.
  3. Set tabindex="-1" and call .focus().
  4. Set tabindex="0" and remove its outline.
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

A custom focusable button routes click and keydown to activate(). It has aria-disabled="true". The function starts with if (button.getAttribute("aria-disabled") === true) return;, yet both routes still act. What repair enforces the disabled state while allowing the string "false" state to act?
  1. Compare with "true" inside the shared activation function.
  2. Test the returned attribute for truthiness inside activate.
  3. Compare with Boolean true only in the click listener.
  4. Move the unchanged check into a pointerdown listener.
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

A custom disclosure button opens a help panel. What ARIA attribute reports whether that panel is open?
  1. aria-checked
  2. aria-pressed
  3. aria-disabled
  4. aria-expanded
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

A disclosure currently has aria-expanded="true" and a visible panel. Its click handler reads the old expanded state. What should the next state be?
  1. aria-expanded="true"; panel.hidden = true.
  2. aria-expanded="false"; panel.hidden = false.
  3. aria-expanded="true"; panel.hidden = false.
  4. aria-expanded="false"; panel.hidden = true.
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

A four-item toolbar uses roving tabindex. Item 0 is focused. After ArrowLeft, what index and tabindex changes keep navigation working?
  1. Focus −1; leave item 0 at 0 and all others at −1.
  2. Focus 3; set item 0 to −1 and item 3 to 0.
  3. Focus 3; leave both item 0 and item 3 at 0.
  4. Focus 1; set item 0 to 0 and item 1 to −1.
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

A native toggle button calls the same inversion function from click and Enter keydown. Enter leaves its pressed state unchanged, while a pointer click toggles it once. What change fixes the keyboard path without losing access?
  1. Keep the click action and remove the extra Enter activation.
  2. Keep both listeners and add a Space keydown inversion.
  3. Replace click with pointerdown and keep no key listener.
  4. Keep both listeners and call preventDefault after each click.
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

A modal dialog closes while its opener still exists. Where should focus normally return?
  1. To the page body after every close.
  2. To the first link in the document.
  3. To the control that opened it.
  4. To the dialog’s hidden close button.
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

A popup has two fields. Focus moves from the first to the second, and a bubbling focusout listener hides the popup. What should the listener check before hiding it?
  1. Whether relatedTarget is outside the popup.
  2. Whether target equals the first field.
  3. Whether currentTarget equals the popup.
  4. Whether the key used to move was Tab.
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

A list has two items and a heading with tabindex="-1". The focused delete button in the last item removes that item; later the remaining item is deleted too. What focus sequence supports continued keyboard use?
  1. First the page body, then the previous deleted button.
  2. First the previous item’s button, then the list heading.
  3. First the heading, then the removed item’s next sibling.
  4. First the removed button, then the first document link.
Show answerHide answer

Save this lesson to see this answer.

Stuck on your own question? Homework Help

Key Terms

80 Key Terms, each with its definition

A

AbortController
An AbortController provides a signal that can group listener lifetimes, removing associated listeners when its abort() method is called.
Closing a drawing tool aborts the signal used for its document and window listeners.
Accessible name
An accessible name is the text assistive technology uses to identify a control, supplied by suitable content, a label, or an ARIA name.
An icon-only close button uses aria-label="Close" to state what its symbol does.
Activation
Activation is the user’s request to perform a control’s action, represented by click for pointer and native keyboard use.
A native next button responds through click whether pressed with Space or a mouse.
ARIA role
An ARIA role communicates an element’s purpose to assistive technology without adding the keyboard behavior or focus rules for that purpose.
A div with role="button" still needs code for activation and an appropriate Tab stop.
ARIA state
An ARIA state is an attribute value communicating a control’s current condition, such as checked, expanded, pressed, or disabled.
The switch’s aria-checked value updates when its visible on/off appearance changes.
aria-describedby
The aria-describedby attribute associates a control with descriptive text by listing the IDs of the elements containing that text.
The email field references email-error so assistive technology can connect the error to it.
aria-invalid
The aria-invalid attribute communicates that a control’s entered value has an error; code must update it as the error changes.
The email field has aria-invalid="true" while its inline correction message is displayed.

B

Bubbling
Bubbling is an event’s upward traversal through ancestors after the target phase, available only for events whose bubbles value is true.
A child field’s input event bubbles to a listener registered on the enclosing form.
Button bitmask
A button bitmask represents the currently held pointer buttons by combining numeric flags in the event’s buttons property.
Holding primary and secondary buttons gives buttons 3, combining flags 1 and 2.

C

cancelable
The cancelable property is a Boolean indicating whether an event permits cancellation of its default action through preventDefault().
An input event reports an edit already made, so its cancelable property is false.
Capturing phase
The capturing phase is the event’s downward path through ancestors toward its target, observed by listeners registered for capture.
A container’s capture listener runs before a descendant button’s listener handles the click.
change event
A change event reports a committed user change, such as leaving an edited text field or selecting a new checkbox or select state.
A changed text field triggers change when the user leaves it for the next control.
checked
The checked property is the Boolean state indicating whether a checkbox or radio input is selected, independently of its submission value.
A preview reads checkbox.checked to decide whether to show an optional section.
checkValidity
The checkValidity() method returns whether constraints pass and fires invalid events for failures without displaying browser feedback.
A custom submission flow checks the form before deciding which inline errors to display.
closest
The closest() method checks an element and then its ancestors for the nearest selector match, returning null if none exists.
Starting from an icon, closest("button") finds the surrounding action button.
code
The code property identifies a physical key position independently of the character produced by the current keyboard layout.
A game uses code when its movement controls should stay in fixed physical positions.
Composite group
A composite group is a related set of controls, such as tabs or a toolbar, navigated internally with arrow keys while Tab enters and leaves it.
The tab group keeps one Tab stop so users can move past the group without visiting every tab.
Constraint validation
Constraint validation checks whether form values satisfy HTML rules such as required input, type syntax, ranges, steps, and patterns.
An out-of-range quantity blocks normal submission before the form’s submit listener runs.
contains
The contains() method tests whether a node is an element itself or one of its descendants, returning a Boolean result.
A delegated listener on a list checks list.contains(button) before allowing the button’s action.
currentTarget
The currentTarget property identifies the object whose listener is running and is reset to null after event dispatch ends.
Inside a listener registered on a button, currentTarget lets code read that button’s data.
Custom validity
Custom validity is an application-defined constraint set with setCustomValidity(), where a nonempty message marks the control invalid.
A mismatched confirmation gets a custom message that is cleared once both passwords match.
CustomEvent
A CustomEvent is a script-created event that can carry application data in detail and can be configured to bubble to ancestors.
A cart item dispatches "cart:add" with its item identifier in the event’s detail.

D

Data attribute
A data attribute is an HTML attribute beginning with data- that stores application information available through an element’s dataset.
A toolbar reads data-action="clear" as button.dataset.action to select a known action.
Default action
A default action is browser-supplied behavior associated with an event, such as following a link, entering text, or submitting a form.
The help link’s default action would navigate to its destination unless the click is canceled.
defaultPrevented
The defaultPrevented property is a Boolean recording whether an event’s default action has been successfully canceled.
An ancestor listener can inspect defaultPrevented before deciding how to handle a click.
Disclosure
A disclosure is a control and panel that reveal or hide content, with the controlling button communicating its state through aria-expanded.
A details button changes its expanded state together with the visibility of the help panel it controls.
Document coordinates
Document coordinates are pointer positions measured from the document’s top-left corner through pageX and pageY, including scrolling.
A click 100 px below the viewport top after 300 px of scrolling has pageY 400.
Drag state
Drag state is stored information identifying an active drag and its initial pointer coordinates and element position.
During a drag, each pointer move compares current coordinates with the saved start to position a dragged box.

E

Error summary
An error summary is a page region collecting form problems and links to their fields, providing a useful focus destination after failure.
After invalid submission, the focusable summary lets the user choose which field to correct.
Event
An event is a signal reporting something that happened, such as a click, a field edit, a key press, or completion of HTML parsing.
A click event lets the page respond when a person activates the gallery’s next button.
Event delegation
Event delegation handles events from multiple descendants through a shared ancestor listener that identifies the relevant control.
A list’s single click listener continues to work for new remove buttons inserted later.
Event listener
An event listener is a function registered on an event target so it can run when a specified type of event is dispatched there.
The counter’s listener increases the stored total each time its button receives a click.
Event object
An event object is the value passed to a listener containing shared event properties and details specific to that event family.
A keyboard listener reads the received event’s key to decide whether to close a panel.

F

Focus
Focus is the page state identifying the element that currently receives keyboard input, available through document.activeElement.
Calling search.focus() directs the next typed character into the search field.
Focus indicator
A focus indicator is a visible mark identifying the currently focused element so keyboard users can locate their position on the page.
A :focus-visible outline shows which toolbar button will receive the next key action.
Focus management
Focus management deliberately chooses a useful keyboard destination when content opens, closes, disappears, or presents errors to the user.
Deleting a focused row moves focus to a neighboring row’s control instead of losing position.
Form reset
A form reset restores controls to their initial values and selection states, which can include nonempty defaults from the markup.
After a form adds an item, form.reset() restores the form’s starting field values.
FormData
FormData is a collection of eligible form name/value pairs that can preserve repeated names and omits controls that are not submitted.
When several checkboxes share the name "topics", data.getAll("topics") retrieves all the selected ones together.
Function reference
A function reference is a value identifying a function object, allowing code to pass that function for later use without calling it.
Passing openPanel registers the function; passing openPanel() calls it during setup.

H

Handler property
A handler property is an element property such as onclick that holds one event callback and replaces it when assigned again.
Assigning another function to button.onclick replaces that property’s earlier handler.

I

Inert background
An inert background is page content temporarily unavailable for ordinary clicking and focus while a modal interaction is active.
The page behind an open native modal dialog cannot receive normal focus until it closes.
input event
An input event reports a user edit after a control’s value changes, including text edits made without a physical key press.
Pasting a sentence into a message field updates a counter through the field’s input listener.

K

key
The key property reports a key’s produced value, taking the keyboard layout and modifiers such as Shift into account.
Shift can change a letter’s key value from "a" to "A" while its position stays the same.
keydown
A keydown event reports a key press before its normal editing effect and may repeat while the physical key remains held down.
The shortcut checks for Escape in keydown before closing the open panel.

L

Listener identity
Listener identity combines the event type, callback object, and capture setting, determining duplicate registration and removal.
A new arrow function cannot remove an earlier callback merely because their bodies match.

M

Modal dialog
A modal dialog is a dialog interaction that makes the rest of the page inert while open, limiting user interaction to its active area.
Calling showModal() opens the native dialog and moves focus into its content.
Modifier flag
A modifier flag is a Boolean keyboard-event property describing whether Shift, Control, Alt, or Meta is held during the event.
The save shortcut accepts either ctrlKey or metaKey together with the letter S.

N

Native control
A native control is an HTML element with built-in interaction semantics and behavior, such as a button, link, checkbox, or select.
A styled native button retains its Enter and Space activation without a custom key listener.
novalidate
The novalidate form attribute skips automatic constraint validation during submission while leaving constraints and validity methods available.
A custom error flow uses novalidate so its submit listener can inspect every field itself.

O

once
The once listener option requests automatic removal of that registration after its first invocation for the selected event type.
A welcome banner can use once: true for the click that dismisses it for this lifetime.
Opener
An opener is the control that starts a popup or dialog interaction and is usually the appropriate focus destination when that interaction closes.
The dialog stores the opening button so it can restore focus after the close event.
Outside-click test
An outside-click test checks whether an event originated outside both an open popup and its opener before closing that popup.
A document listener that ignores clicks on a menu’s button keeps the opening click from closing the menu.

P

Passive listener
A passive listener is a listener registered with a promise not to cancel the default action, allowing the browser to proceed without waiting for cancellation.
Calling preventDefault() in a passive touch listener does not stop the browser’s scrolling.
pattern
The pattern attribute defines a whole-value regular-expression constraint for supported text inputs, applied only when their value is nonempty.
A five-digit pattern rejects six digits but needs required to reject an empty field.
Pointer capture
Pointer capture directs subsequent events from a specified active pointer to an element even after the pointer moves beyond its bounds.
A dragged box captures the initiating pointer so a fast movement does not leave its event area.
Pointer event
A pointer event describes pointing-device interaction through one event family shared by mouse, touch, and pen input.
The same pointermove listener can read movement from a mouse or a touch contact.
pointercancel
A pointercancel event reports that the browser has canceled the pointer interaction, requiring a drag to clear its active state.
A gesture taken over for scrolling can end the custom drag through pointercancel.
pointerId
The pointerId property identifies an individual pointer during an interaction so simultaneous contacts can be handled separately.
Two fingers have different IDs, allowing the drag code to follow only the one that started it.
preventDefault
The preventDefault() method cancels an eligible event’s default action in a non-passive listener without stopping its propagation.
A submit listener calls preventDefault() before adding a local item without navigation.
Propagation
Propagation is an event’s traversal through the document tree, including the capturing path, target phase, and any bubbling path.
One button click can be observed by listeners on both the button and its enclosing card.

R

relatedTarget
The relatedTarget property identifies the other element involved in a focus transition, or is null when no such element is available.
A menu’s focusout listener checks whether the destination remains inside the menu.
reportValidity
The reportValidity() method checks constraints and also asks the browser to display validation feedback for unhandled failures.
An explicit check button calls reportValidity() to help the user find a failing field.
requestSubmit
The requestSubmit() method requests form submission through the normal validation and submit-event path without firing a button click.
A toolbar invokes form.requestSubmit() so the form’s validation and submit listener still run.
Roving tabindex
Roving tabindex is a group-navigation technique that gives one item a normal Tab stop and moves that stop and focus as arrow keys change items.
In a toolbar, Tab enters the current item and arrow keys move to its neighboring controls.

S

Single-pointer alternative
A single-pointer alternative performs a dragging function through clicks or taps without requiring the pointer to stay pressed while moving.
Move buttons let a touch user reposition an item without carrying out a dragging gesture.
stopImmediatePropagation
The stopImmediatePropagation() method stops event travel and also skips remaining listeners registered on the current event target.
When the first callback invokes this method, a later callback on the same element is skipped.
stopPropagation
The stopPropagation() method prevents an event from traveling to further elements while leaving other listeners on its current element active.
A nested action can stop its click from reaching an enclosing card’s bubbling listener.
submit event
A submit event is a cancelable form event dispatched during the normal submission path after automatic validation allows submission.
A form’s submit listener handles a valid submission by adding an item locally and canceling navigation.
Submitter
A submitter is the submit button used for a submission, exposed as event.submitter when a particular button initiated that route.
A form distinguishes draft and publish by reading the submitting button’s value.
Synchronous dispatch
Synchronous dispatch means listeners run within the dispatchEvent() call, before that method returns control to the calling code.
A message logged after dispatchEvent() appears after messages from its listeners.

T

tabindex
The tabindex attribute controls ordinary Tab participation and programmatic focusability, with 0 in normal order and −1 outside that sequence.
The error heading uses tabindex="-1" so code can focus it without adding a regular Tab stop.
target
The target property identifies the event’s origin, which can be a nested element inside the control whose action is being handled.
Clicking the image inside a save button makes the image the click’s target.
Target phase
The target phase is the part of dispatch at the event’s origin, where capture listeners run before non-capture listeners.
Both listeners on the clicked button report eventPhase 2 while they are called.
Toggle button
A toggle button switches an action state on or off, commonly using a stable visible label with aria-pressed reporting the current state.
A mute button keeps the word "Mute" while its aria-pressed value changes between true and false.
touch-action
The touch-action CSS property declares which browser touch gestures are permitted when a gesture starts on an element.
Using touch-action: none on the drag surface reserves that area’s movement for the drag.

V

ValidityState
A ValidityState is the object exposed through a control’s validity property, with Boolean flags identifying failed constraints.
A quantity can report stepMismatch even though it is inside its minimum and maximum.
value
The value property exposes a form control’s current value as text, including number inputs whose numeric content still needs conversion.
A quantity field holding "8" needs conversion before code adds a numeric increment.
valueAsNumber
The valueAsNumber property reads a supported input’s numeric value directly, returning NaN when no usable numeric value is present.
An empty number field produces NaN, which code detects with Number.isNaN().
Viewport coordinates
Viewport coordinates are pointer positions measured from the visible area’s top-left corner through clientX and clientY.
A movement calculation subtracts two client-coordinate readings without adding page scroll.

W

Whitespace trimming
Whitespace trimming removes leading and trailing whitespace from text before a rule or action uses its meaningful content.
Checking name.value.trim() === "" catches a name made entirely of spaces.

About this lesson

By StudeliReport a mistake

Make your own Study Materials

Turn your notes, slides and documents into Study Guides, Practice Questions, Flashcards and 6 more tools.

Get Started