Attributes & properties
Learn the difference between HTML attributes and DOM properties: getAttribute, setAttribute, reflection, dataset, input defaults, and boolean attributes.
- 01Read the right sideChoose the attribute for the starting value and the property for the value now.
- 02Predict how they linkSay what
id,className,href,style,value, andcheckedgive on each side. - 03Use your own data safelyKeep small text in
data-*attributes, and turn boolean attributes on and off correctly.
Two places for information
An element is one piece of a web page, such as a button or a text box. Each element can keep information in two places:
- An attribute is a name and a value written in the HTML, such as
id="save"orvalue="Ada". Its value is always text. - A property is a field on the element's object in JavaScript, such as
button.idorinput.value. The browser makes one object for each element. All these objects together are called the DOM.
Why two places? A page changes while people use it. A user types in a box. A user ticks a checkbox. Your code changes a value.
- Attributes usually keep the starting setup, as text.
- Properties usually keep how things are right now. A property can also be another type:
trueorfalse, a number, or an object.
const input = document.createElement("input");
input.setAttribute("value", "Ada");
input.value = "Grace";
console.log(input.getAttribute("value"));
console.log(input.value);Line 1 makes a new text box, an input element. Line 2 sets its value attribute to Ada. Line 3 sets its value property to Grace, just as typing would. Line 4 reads the attribute and prints Ada. Line 5 reads the property and prints Grace. The rest of this lesson keeps coming back to this split.
"Ada", the value from line 2. The property says "Grace", what is in the box right now.A toy box has the label “red car.” A child paints the car blue. The car is now blue, but the box still says red. For a text box, the value attribute is the label on the box. The value property is what is in the text box right now.
- In real life: The label printed on the box
- In JavaScript: The attribute you wrote, like
value="Ada" - In real life: The toy as it is now, after you paint it
- In JavaScript: The property, like
input.valueafter typing - In real life: Reading the box label again later
- In JavaScript:
defaultValueordefaultChecked, which give the starting value - In real life: Painting the toy
- In JavaScript: Setting a property, like
input.value = "Grace"
Before this lesson, you should know The browser environment, The DOM tree, Walking & searching the DOM, and Node properties & contents. The next lesson, Creating & changing elements, uses these reads and writes to build new elements.
getAttribute and setAttribute
INTERACTIVEFour methods work with attributes:
element.getAttribute(name)reads an attribute. It gives back text, ornullif the attribute is not there.nullmeans "no value".element.setAttribute(name, value)writes an attribute. It turns the value into text first.element.hasAttribute(name)givestrueif the attribute is there, andfalseif it is not.element.removeAttribute(name)deletes an attribute.
Upper and lower case don't matter in attribute names on HTML elements. getAttribute("ID") and getAttribute("id") read the same attribute.
const card = document.createElement("article");
card.setAttribute("data-count", 3);
console.log(card.getAttribute("data-count"));
console.log(card.hasAttribute("data-count"));
card.removeAttribute("data-count");
console.log(card.getAttribute("data-count"));
card.setAttribute("color", "teal");
console.log(card.getAttribute("color"));
console.log(card.color === undefined);Line 2 writes the number 3. The attribute stores it as the text "3", so line 3 prints 3. Line 4 prints true because the attribute is there. Line 5 removes it. Line 6 prints null because the attribute is now gone.
Lines 7 to 9 use a made-up attribute, color. HTML has no color attribute for this element. Line 8 still reads it and prints teal. But the browser makes no property for it, so card.color is undefined and line 9 prints true.
Use hasAttribute when you only need to know if an attribute is there. Use getAttribute when you need its text.
element.attributes holds every attribute of an element. It is a NamedNodeMap: a list you can look up by name. Each item in it is an Attr object with a name and a value. Most code uses the four methods above instead.
Try the inspector below. It has a real text box and a real checkbox. Type something in the text box first. Then press setAttribute value: only the attribute and the starting value change. Then press write input.value: the text in the box changes.
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 text box or tick the checkbox. Then compare the properties (the values now) with the attributes (the starting values).
How attributes and properties stay linked
TABLEMany built-in attributes are linked to a property. When you change one, the browser updates the other. This link is called reflection: we say the property reflects the attribute.
But the two sides don't always have the same name or the same type. Check the rule for each one you use. In the table below, each row is one attribute. Read across to see what each side gives you.
| Attribute | Reading the attribute gives | Reading the property gives | What to remember |
|---|---|---|---|
id | The text you wrote. | el.id gives the same text. | The two sides stay the same both ways. |
class | The class text, like "quiet". | el.className gives the same text. el.classList helps add or remove one class. | The name changes because class already means something in JavaScript. |
href | The text as written, like /docs. | a.href gives the full web address. | Use the attribute for the written text. Use the property for where the link really goes. |
value | The starting value. | input.value gives the text in the box now. | input.defaultValue is the property for the starting value. |
checked | Whether the box started ticked. The attribute is there if it did. | checkbox.checked gives true or false for now. | checkbox.defaultChecked is the property for the starting state. |
style | The CSS as one piece of text. | el.style gives a CSSStyleDeclaration object, with one field per CSS setting. | el.style.color = ... changes one CSS setting. |
for on <label> | The for text. | label.htmlFor gives the same text. | The name changes because for already means something in JavaScript. |
tabindex | Text, or null if it is not there. | el.tabIndex gives a number. | Some properties are numbers, not text. |
color on <div> | Your made-up text. | Usually undefined. | Made-up attributes get no property. |
The next code block tries three of these rules. It first adds a <base> element to the page. A <base> element sets the page's base address: the address that short links like /docs are joined to.
const base = document.createElement("base");
base.href = "https://example.test/app/";
document.head.append(base);
const link = document.createElement("a");
link.setAttribute("href", "/docs");
link.className = "quiet";
link.style.color = "teal";
console.log(link.getAttribute("href"));
console.log(link.href);
console.log(link.className);- Line 8 reads the
hrefattribute and prints/docs, just as written. - Line 9 reads the
hrefproperty and printshttps://example.test/docs. The browser joined/docsto the base address. - Line 10 prints
quiet.classNameis the property for theclassattribute, set on line 6. - Line 7 uses
styleas an object:link.style.colorchanges just the color.
/docs. The property gives the full web address: the browser joins /docs to the page's base address.The step-through in the next section can't use a real page. So it uses a small plain object that copies how a text box behaves. Here is its full code. A real text box gives the same results.
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);Lines 1 to 18 build the stand-in text box. Lines 20 to 25 use it. Line 21 acts like typing. Line 22 changes the attribute.
Text boxes, checkboxes, and starting values
STEP THROUGHText boxes and checkboxes are where people get mixed up most. A text box is an input element. It has three values to know:
input.valueis the text in the box now.input.getAttribute("value")is the starting text, from the attribute.input.defaultValueis a property that also gives the starting text.
A checkbox works the same way:
checkbox.checkedistrueif the box is ticked now.- The
checkedattribute says whether the box started ticked. checkbox.defaultCheckedis a property that also gives the starting state.
const checkbox = document.createElement("input");
checkbox.type = "checkbox";
checkbox.setAttribute("checked", "");
checkbox.checked = false;
console.log(checkbox.checked);
console.log(checkbox.defaultChecked);
console.log(checkbox.hasAttribute("checked"));Line 2 makes the input a checkbox. Line 3 adds the checked attribute, so the box starts ticked. Line 4 unticks it by setting only the property. Line 5 prints false: the box is not ticked now. Lines 6 and 7 print true and true: defaultChecked and the attribute still say it started ticked.
Now step through the stand-in text box from the last section. Watch the attribute and the property split apart at line 21.
This plain object copies how a real text box keeps its value now apart from its starting value. A real text box prints the same three lines.
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);- The text typed in a text box right now
- The
valuewritten in the HTML at the start - The full web address a link goes to
- The exact
href="/docs"text as written data-user-idread asuserId- Whether a checkbox is ticked after a click
- Whether the checkbox started ticked in the HTML
dataset.lastSeen = 7creatingdata-last-seen- Can the button be clicked right now?
Sort each item by where you would read it: the attribute (the starting value), the property (the value now), or dataset (your own data-* notes).
data-* and dataset
INTERACTIVESometimes your own code needs to keep a small piece of text on an element, such as a user's ID. Use an attribute whose name starts with data-, like data-user-id. These are called data-* attributes. HTML never uses these names for anything else, so they are safe to use.
JavaScript reads them through element.dataset. The name changes from HTML style to JavaScript style, which is called camelCase:
- Drop
data-from the front. - A dash followed by a small letter becomes that letter as a capital.
- So
data-user-idbecomesdataset.userId.
It works the other way too. Setting dataset.someValue creates the attribute data-some-value.
const item = document.createElement("li");
item.dataset.userId = 42;
console.log(item.getAttribute("data-user-id"));
console.log(typeof item.dataset.userId);
console.log(Number(item.dataset.userId));Line 2 sets dataset.userId to the number 42. Line 3 reads the attribute data-user-id and prints 42. Line 4 prints string: the number was stored as text. Line 5 uses Number(...) to turn the text back into a number and prints 42. Only do that when you need to do maths with it.
userId through dataset. The browser stores it as the attribute data-user-id, and the number 42 becomes the text "42".Anyone can see data-* attributes in the page. Never keep passwords or other secrets in them.
You stick a small note on the element. Later, your code reads the note. You don't have to make up your own attribute names like userId="42", which HTML doesn't know.
- In real life: A 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"
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
INTERACTIVESome attributes work like on/off switches. They are called boolean attributes, because true and false are called booleans. Examples are disabled, checked, hidden, and required.
For these, only one thing matters: is the attribute there? If it is there, the feature is on. Its text does not matter. Even "false" or "0" leave it on. To turn it off, do one of these:
- Remove the attribute with
removeAttribute. - Set the property to
false, likebutton.disabled = false.
disabled attribute is there. It never reads the text "false". Removing the attribute is what turns it off.toggleAttribute(name, on) adds or removes an attribute in one step. If the second value is true, it adds the attribute. If it is false, it removes it.
const button = document.createElement("button");
button.toggleAttribute("disabled", true);
console.log(button.hasAttribute("disabled"));
button.toggleAttribute("disabled", false);
console.log(button.disabled);Line 2 adds disabled, so line 3 prints true. Line 4 removes it, so line 5 prints false from the disabled property. The second value is handy when you already have a true or false in a variable, such as isSaving.
When a shop has a “Closed” sign on its door, people see the sign and walk away. Nobody reads the small writing on it. To open the shop, you take the sign down.
- In real life: A “Closed” sign hanging on the door
- In JavaScript: A boolean attribute on the element, like
disabled - In real life: Someone writes “false” on the sign
- In JavaScript:
disabled="false": the button is still disabled - In real life: Taking the sign down
- In JavaScript:
removeAttribute("disabled")
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 whenever it is there.
Where you will use this
Real code often uses both sides. Use attributes for:
- Starting values and settings written in the HTML.
- ARIA attributes. These describe the page for screen readers and other tools that help people use it.
- Anything a CSS selector should match, like
[data-panel].
Use properties for how things are right now, and for values the browser works out, like a full link address. Here is a tab button that shows a panel. It uses both sides:
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;- Line 2:
tab.dataset.panelreads the sticky note. It holds the ID of the panel to show. - Line 5:
setAttribute("aria-selected", "true")writes an ARIA attribute. A screen reader can then say that the tab is selected. - Line 6:
tab.disabled = truesets the property. The browser also adds thedisabledattribute. - Line 7:
panel.hidden = falsesets the property, so the panel shows now.
Common misconceptions
- “Attributes and properties are the same thing.” Many are linked. But attributes are text from the HTML, usually the starting values. Properties are fields on the live object, usually the values now.
- “
setAttribute("value", ...)always changes the text in the box.” Once the user has typed, it only changes the starting value. Set thevalueproperty to change the text in the box. - “
disabled="false"means not disabled.” A boolean attribute is on whenever it is there. Its text is ignored. - “
datasetcan store numbers and objects.”datasetvalues are always text. Turn them back withNumber(...), or withJSON.parse(...)if you stored JSON text. - “Every attribute has a property with the same name.” Some properties have a new name, like
className. Some have another type, liketabIndex, which is a number. Made-up attributes have no property at all. - “
getAttributegivesundefinedwhen the attribute is missing.” It givesnull.
Practice exercises
5 EXERCISESA new paragraph has no title attribute. What does this line give back?
document.createElement("p").getAttribute("title");getAttribute gives null when the attribute is not there.
This program acts like dataset: it turns the value into text before storing it. What type does the first line print?
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 line prints string. The second prints 5. dataset turns every value into text.
The developer wanted a button that works. What does this code print, and what is the bug?
button.setAttribute("disabled", "false");
console.log(button.disabled);It prints true: the button is disabled. The disabled attribute is there, and its text "false" is ignored.
Think back to the step-through. What three lines does this program print?
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
Linusvalue is still Grace. The attribute and defaultValue both become Linus.
Replace each comment with the code that fits the job best.
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");dataset reads the sticky note. The disabled property changes how the button works now. setAttribute writes a starting value as an attribute.
Quiz
7 QUESTIONSQuestion 1 of 7Which sentence best describes an HTML attribute?
Choose an answer to see the explanation.
Question 2 of 7This code acts like reading back
setAttribute("size", 7), then reading a missing attribute. What does it 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 full web address?Choose an answer to see the explanation.
Question 4 of 7This program acts like
dataset. What does it 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 does
disabled="false"do in HTML?Choose an answer to see the explanation.
Question 6 of 7Which property is linked to the
forattribute on a<label>?Choose an answer to see the explanation.
Question 7 of 7A user has typed in a text box. What does
setAttribute("value", "new")change now?Choose an answer to see the explanation.
Key takeaways
- Attributes are text written in the HTML, usually the starting values. Properties are fields on the element's live object, usually the values now.
getAttributegives text ornull.setAttributeturns any value into text.- Linked pairs don't always match. A property can have a new name (
className), another type (tabIndex), or a worked-out value (the fullhref). - For text boxes and checkboxes,
valueandcheckedare the state now. The attributes,defaultValue, anddefaultCheckedare the starting state. data-*attributes show up indatasetwith camelCase names, always as text.- Boolean attributes are on when they are there. Remove them, or set the property to
false, to turn them off.
An attribute is text on an element, written in the HTML, that usually holds a starting value. A property is a field on the element's live JavaScript object that usually holds the value now.
Up next: Creating & changing elements.