Attributes & properties
Learn the difference between HTML attributes and DOM properties: getAttribute, setAttribute, reflection, dataset, input defaults, and boolean attributes.
- 01Read the right sideChoose attributes for markup/defaults and properties for live state.
- 02Predict reflectionExplain id, className, href, style, value, and checked sync rules.
- 03Use safe custom dataStore small strings with data-* and handle boolean attributes correctly.
Two places for information
An element can carry information in two closely related places. An attribute is the name/value text you write in HTML, such as id="save" or value="Ada". A property is a field on the live DOM object JavaScript receives, such as button.id or input.value.
The difference matters because the DOM is alive. Users type, click, scripts assign values, and the browser normalizes some properties. Attributes are usually string defaults and configuration. Properties are often the current state and can be booleans, numbers, objects, or resolved values.
Imagine a toy box labelled “red car.” That label is useful, but once the child paints the car blue, the object in their hands is blue even if the box still says red. For an input, the value attribute is the label; the value property is what is in the field right now.
- In real life: Label printed at the factory
- In JavaScript: The HTML attribute you wrote, like
value="Ada" - In real life: The product's current state after you open it
- In JavaScript: The DOM property, like
input.valueafter typing - In real life: A label saying default color
- In JavaScript:
defaultValueordefaultChecked - In real life: Changing the thing in your hands
- In JavaScript: Assigning a property, such as
input.value = "Grace"
Where the analogy stops: Many standard attributes and properties reflect each other, so the label and product can stay in sync. Inputs are the memorable exception: the current value can split away from the default.
This lesson builds on The browser environment, The DOM tree, Walking & searching the DOM, and Node properties & contents. Up next, Creating & changing elements uses these reads and writes while building new nodes.
getAttribute and setAttribute
INTERACTIVEAttribute methods read and write the attribute map on an element. element.getAttribute(name) returns a string when the attribute exists and null when it does not. element.setAttribute(name, value) converts the value to a string before storing it. Attribute names on HTML elements in HTML documents are case-insensitive, so getAttribute("ID") and getAttribute("id") look at the same attribute.
element.attributes is a NamedNodeMap of Attr nodes. You usually use getAttribute, setAttribute, hasAttribute, and removeAttribute instead of editing those nodes directly.
Try the inspector. Type before pressing setAttribute value, then press write input.value. The first changes the label/default side. The second changes what the user sees now.
const input = document.querySelector("input"); input.value; // property: current typed textinput.getAttribute("value"); // attribute: default from HTMLinput.defaultValue; // property reflecting that default input.setAttribute("value", "Linus");input.value = "Grace";- input.value
""- input.getAttribute("value")
null- input.defaultValue
""- checkbox.checked
false- checkbox.getAttribute("checked")
null- checkbox.defaultChecked
false
Type in the input or tick the checkbox, then compare the live properties with the original attributes.
How attributes and properties sync
TABLEMany standard HTML attributes reflect to DOM properties. Reflect means changing one side updates the other according to rules in the DOM and HTML specs. The rule is not always “same name, same type,” so read the API you need.
| Case | Attribute read | Property read | What to remember |
|---|---|---|---|
id | getAttribute("id") is the written string | el.id is the same string | Reflects both ways for normal IDs. |
class | class attribute string | className string and classList token helper | Renamed because class is special JavaScript syntax. |
href | As written, such as /docs | Resolved absolute URL | Use the attribute for source text; property for navigation target. |
value | Default value | Current typed value | Use defaultValue for the reflected default property. |
checked | Default checked when present | Current checked boolean | Use defaultChecked for the default. |
style | CSS text string | CSSStyleDeclaration object | el.style.color = ... edits one declaration. |
for | for attribute on <label> | htmlFor property | Renamed because for is a keyword. |
tabindex | String or null | Number from tabIndex | Some properties are typed. |
color on <div> | Custom non-standard string | Usually undefined | Not every attribute gets a matching property. |
The table is the map; the next replay is the movie. It uses a tiny pure model of an input so the server can record the steps without a DOM. The browser behavior was checked against real inputs for this lesson.
function makeInput(defaultValue) { return { attributes: { value: String(defaultValue) }, value: String(defaultValue), defaultValue: String(defaultValue), getAttribute(name) { return Object.hasOwn(this.attributes, name) ? this.attributes[name] : null; }, setAttribute(name, value) { this.attributes[name] = String(value); if (name === "value") this.defaultValue = String(value); }, removeAttribute(name) { delete this.attributes[name]; if (name === "value") this.defaultValue = ""; }, };} const input = makeInput("Ada");input.value = "Grace";input.setAttribute("value", "Linus");console.log(input.value);console.log(input.getAttribute("value"));console.log(input.defaultValue);Inputs, checks, and defaults
STEP THROUGHForm controls are where learners most often trip. input.value is the current text. input.getAttribute("value") is the default attribute. input.defaultValue is the property that reflects that default. Checkboxes mirror the same idea: checked is current, while the checked attribute and defaultChecked are the default.
This is a pure model of the input value/default-value split. It mirrors the browser behavior verified for this lesson.
script
function makeInput(defaultValue) { return { attributes: { value: String(defaultValue) }, value: String(defaultValue), defaultValue: String(defaultValue), getAttribute(name) { return Object.hasOwn(this.attributes, name) ? this.attributes[name] : null; }, setAttribute(name, value) { this.attributes[name] = String(value); if (name === "value") this.defaultValue = String(value); }, removeAttribute(name) { delete this.attributes[name]; if (name === "value") this.defaultValue = ""; }, };} input.value = "Grace";input.setAttribute("value", "Linus");console.log(input.value);console.log(input.getAttribute("value"));console.log(input.defaultValue);- Text typed in an input right now
- The
valuewritten in the original HTML - A link URL resolved against the page
- The exact
href="/docs"text as written data-user-idread asuserId- Whether a checkbox is checked after a click
- Whether the checkbox started checked in HTML
dataset.lastSeen = 7creatingdata-last-seen- Can the button be clicked right now?
Sort each read by the side of the DOM it belongs to.
data-* and dataset
INTERACTIVEdata-* attributes are the safe place for small custom strings that belong to an element. JavaScript exposes them through element.dataset. A dash followed by a lowercase letter becomes an uppercase letter: data-user-id becomes dataset.userId. Setting dataset.someValue creates data-some-value.
You attach a small note to the element so your code can find it later without inventing non-standard attributes like userId="42".
- In real life: Sticky note: customer #42
- In JavaScript:
data-user-id="42" - In real life: Reading the note later
- In JavaScript:
element.dataset.userId - In real life: Adding a new note
- In JavaScript:
element.dataset.lastSeen = "today"
Where the analogy stops: Sticky notes are visible to anyone looking at the page source or DevTools. Do not store secrets in data-*. Values are always strings, not rich objects.
const card = document.querySelector("[data-user-id]"); card.dataset.userId; // "42"card.dataset.role; // "admin"card.dataset.lastSeen = 7; // creates data-last-seen="7"delete card.dataset.role; // removes data-role- dataset.userId
undefined- dataset.role
undefined- dataset.lastSeen
undefined- data-* attributes
{}
Read the sticky notes: data-user-id becomes dataset.userId, and data-role becomes dataset.role.
Boolean attributes
INTERACTIVEBoolean attributes are controlled by presence. If disabled, checked, hidden, required, or a similar boolean attribute exists at all, it is on. The strings "false" and "0" do not turn it off. To turn it off, remove the attribute or assign the reflected property to false.
For boolean attributes, the browser asks one question: “is the switch there?” If yes, the feature is on.
- In real life: A switch installed on the wall
- In JavaScript: A boolean attribute present on the element
- In real life: A label on the switch saying false
- In JavaScript:
disabled="false"still means disabled - In real life: Removing the switch
- In JavaScript:
removeAttribute("disabled")
Where the analogy stops: Real switches have an up/down position. Boolean HTML attributes use presence only; the property gives you the convenient true/false value.
const button = document.querySelector("button"); button.setAttribute("disabled", "false");button.hasAttribute("disabled"); // truebutton.disabled; // truebutton.removeAttribute("disabled");button.disabled = false;button.toggleAttribute("disabled", true);- hasAttribute("disabled")
false- getAttribute("disabled")
null- button.disabled
false
A boolean attribute is on when the attribute exists at all.
Where you will use this
Use attributes for markup-facing configuration, accessibility states, selectors, and defaults. Use properties for current state and browser-normalized values. Real components often mix both: a tab might read a data-panel attribute, set an ARIA attribute, write a boolean property, and reveal a panel through the reflected hidden property.
const tab = document.querySelector("[data-panel]");const panelId = tab.dataset.panel;const panel = document.getElementById(panelId); tab.setAttribute("aria-selected", "true");tab.disabled = true;panel.hidden = false;dataset.panelreads your code’s sticky note.setAttribute("aria-selected", "true")writes an accessibility state that assistive technology can read.tab.disabled = truewrites the live boolean property and reflects to the attribute.panel.hidden = falsechanges the current DOM object state.
Common misconceptions
- “Attributes and properties are the same thing.” They are connected for many standards, but attributes are string/default markup and properties are live object fields.
- “
setAttribute("value")always changes the visible input.” Once the input’s current value is dirty, set thevalueproperty instead. - “
disabled="false"means enabled.” Boolean attributes are on when present. - “Dataset stores numbers and objects.” Dataset values are always strings; parse JSON yourself if you choose to store JSON text.
- “Every attribute has a same-named property.” Some are renamed, typed, object-shaped, or not reflected at all.
- “
getAttributereturnsundefinedwhen missing.” It returnsnull.
Practice exercises
5 EXERCISESPredict the value returned for an attribute that was never written.
document.createElement("p").getAttribute("title");getAttribute returns null when the requested attribute is missing.
Run the program mentally. What type is stored after a number goes through dataset?
const dataset = {};
dataset.count = String(5);
console.log(typeof dataset.count);
console.log(dataset.count);const dataset = {};
dataset.count = String(5);
console.log(typeof dataset.count);
console.log(dataset.count);The first log is string; the second is 5. Dataset stringifies values.
The developer expected an enabled button. Predict the value and name the bug.
button.setAttribute("disabled", "false");
console.log(button.disabled);The button is disabled, so the property is true. disabled="false" is still present.
Use the replay idea to predict the three console lines.
function makeInput(defaultValue) {
return {
attributes: { value: String(defaultValue) },
value: String(defaultValue),
defaultValue: String(defaultValue),
getAttribute(name) {
return Object.hasOwn(this.attributes, name) ? this.attributes[name] : null;
},
setAttribute(name, value) {
this.attributes[name] = String(value);
if (name === "value") this.defaultValue = String(value);
},
removeAttribute(name) {
delete this.attributes[name];
if (name === "value") this.defaultValue = "";
},
};
}
const input = makeInput("Ada");
input.value = "Grace";
input.setAttribute("value", "Linus");
console.log(input.value);
console.log(input.getAttribute("value"));
console.log(input.defaultValue);Grace
Linus
LinusThe current value remains Grace; the attribute and defaultValue become Linus.
Fill in the three comments with the API that best matches each job.
const item = document.querySelector("[data-price]");
// 1. Read the price note.
// 2. Disable the buy button.
// 3. Store a selected default in markup.const price = item.dataset.price;
button.disabled = true;
item.setAttribute("data-default-selected", "yes");This uses the attribute-facing API for sticky notes/defaults and the property-facing API for current disabled state.
Quiz
7 QUESTIONSQuestion 1 of 7Which statement best describes an HTML attribute?
Choose an answer to see the explanation.
Question 2 of 7What does this print?
Read the code, then predictconsole.log(String(7)); console.log(null);Choose an answer to see the explanation.
Question 3 of 7For
<a href="/docs">, which read gives the resolved absolute URL?Choose an answer to see the explanation.
Question 4 of 7What does this dataset-style program print?
Read the code, then predictconst dataset = {}; dataset.count = String(5); console.log(typeof dataset.count); console.log(dataset.count);Choose an answer to see the explanation.
Question 5 of 7What is true of
disabled="false"in HTML?Choose an answer to see the explanation.
Question 6 of 7Which property name reflects the
forattribute on a<label>?Choose an answer to see the explanation.
Question 7 of 7After a user edits an input, what does
setAttribute("value", "new")update?Choose an answer to see the explanation.
Key takeaways
- Attributes are markup-side strings; properties are fields on live DOM objects.
getAttributereturns a string ornull;setAttributestringifies values.- Reflection rules vary: names can change, values can be typed, and URLs/styles can be normalized objects.
- For inputs and checkboxes,
value/checkedare current; attributes anddefaultValue/defaultCheckedare defaults. data-*maps todatasetcamelCase properties, always as strings.- Boolean attributes are true when present; remove them or set the property to
falseto turn them off.
An attribute is an element’s string/default information from markup; a property is the live JavaScript field on the DOM object.
Up next: Creating & changing elements.