Learning Objectives
- Distinguish
getAttribute()/setAttribute()from the mapped property, and know which one holds the live state - Predict the property name for any attribute: camelCase for multiword, remapped for reserved words (
className,htmlFor) - Manage classes with
classListinstead of string-mashingclassName - Attach your own metadata with
data-*attributes and read it back throughdataset - Bridge into CSS: computed styles, custom properties, and
matchMedia
Two doors to the same data
Take a checkbox written in markup:
The checked attribute is a piece of text in the document — getAttribute('checked') returns a string, and always will, because attributes are inherently strings. The checked property on the mapped object is a boolean, and it is the live one: it changes when the user clicks, while the attribute keeps recording only the initial state. The demos in this series always read the property side:
And because the map is bound both ways, assignment works too — set $('#fCheck').checked = false and the box unticks on screen. No render call, no sync step. The object is the element.
Number() wrapper above: even an <input type="number"> hands you its value as a string. When a value must do math, convert it at the boundary.The property names follow two rules worth memorizing. Multiword attributes camelCase: tabindex becomes el.tabIndex, maxlength becomes el.maxLength. And attributes whose names were already reserved words in JavaScript got remapped: class becomes el.className, and a label's for becomes el.htmlFor.
Live demo: the mapping at work in forms
Forms are where the mapping earns its keep — and where it has the most conveniences layered on. The demo reads and writes typed properties, walks form.elements, accesses a field by its name with elements['title'], and finishes with the constraint-validation API: checkValidity() asks quietly, reportValidity() asks out loud with the browser's own bubble, and validationMessage tells you what it said.
The class attribute, done right
className is the whole class attribute as one string, which makes adding or removing a single class an exercise in string surgery. classList is the same data as a set, with the verbs you actually want:
Why does flipping one class name matter so much? Because a class is the handle CSS already holds. Change the class and every rule written against it applies at once — colors, weights, animations — without JavaScript touching a single style property. Design stays in the stylesheet; script just moves the state.
classList.toggle() can retheme a component. A pile of el.style.* assignments does the same job as inline styles that override your stylesheet, resist theming, and hide design decisions inside script. Reach for style only for values that are genuinely computed at runtime.Crossing into CSS
The mapping has a style-facing side too, and it comes with a trap: el.style only reflects inline styles. The truth — after the cascade, the classes, and the media queries have all voted — comes from getComputedStyle():
Custom properties are the cleanest JS-to-CSS channel going the other way: set one variable at the root and every rule using it updates. The demo's fourth section closes the loop with matchMedia, which hands you media queries as objects that fire change events — the same event model from the Events series, now reporting on dark mode and viewport width.
One deliberate lesson hides in the demo's third button: replace('fancy', 'pretty') swaps in a class that no stylesheet defines, and the styling simply vanishes. Class names are only handles — they do nothing until CSS gives them meaning.
Applied: data attributes
The data-* family is the sanctioned place for your own metadata — ids, roles, flags — readable in script as el.dataset.role and in CSS as [data-role="developer"]. The mini-demo below plays both sides: dataset reads and writes the values, while attribute selectors like [data-theme="dark"] restyle the box the moment the value changes. One attribute, one source of truth, two consumers.
data-active by comparing against 'true' — the string — because that is what dataset stores. Same rule as every other attribute.Next Steps
In Step 5: Modification, we move from state to substance — changing what an element contains:
- Set text safely with
textContent, and know exactly wheninnerHTMLis a hazard - Swap nodes with
replaceWith - Discover that appending an existing node moves it
- Redact a document with a single class flip