The distinction between HTML attributes and DOM properties is one of the most common sources of confusion for web developers. While they are closely related, they represent two fundamentally different layers of the web: the initial source code (HTML) and the active living object (the DOM).
1. The Core Definition
To understand the difference, you must think of them as a “mapping” process that happens when a web page loads.
- HTML Attributes: These are defined in the markup (the .html file). They are the “initial configuration” sent from the server to the browser. Attributes are always strings.
- DOM Properties: Once the browser parses the HTML, it creates a JavaScript object (the DOM node). The properties on this object are the “live state.” Properties can be strings, booleans, numbers, or even objects.
2. The Relationship: Initialization vs. Synchronization
Most standard attributes have a corresponding property (e.g., id, title, className). However, the way they sync varies depending on the specific attribute.
A. One-to-One Synchronization
For properties like id, changing the attribute updates the property, and changing the property updates the attribute. They stay perfectly in sync.
const input = document.querySelector('input');
// Update attribute
input.setAttribute('id', 'new-id');
console.log(input.id); // "new-id" (Property updated)
// Update property
input.id = 'another-id';
console.log(input.getAttribute('id')); // "another-id" (Attribute updated)
B. The “Value” Exception (Crucial for Forms)
This is where most bugs occur. The value attribute and the value property behave differently once the user interacts with the page.
- The Attribute (value): Represents the default/initial value. It rarely changes after the page loads.
- The Property (value): Represents the current value. It changes every time a user types in a field.
// HTML: <input type="text" value="Default">
const input = document.querySelector('input');
// User types "Hello" into the box
console.log(input.value); // "Hello" (Current property)
console.log(input.getAttribute('value')); // "Default" (Original attribute)
3. Custom Data and Non-Standard Attributes
The DOM object only creates properties for standard HTML attributes defined in the specification (like src, href, id). If you invent a custom attribute, the DOM will not automatically create a property for it.
// HTML: <div my-custom-attr="123"></div>
const div = document.querySelector('div');
console.log(div.getAttribute('my-custom-attr')); // "123"
console.log(div['my-custom-attr']); // undefined
To handle custom data properly, modern developers use the data-* attribute convention, which the DOM makes accessible via the dataset property.
4. Visualizing the Disconnect: The href Attribute
The href attribute in HTML is often a relative path, but the DOM property is usually the resolved, absolute URL.
// HTML: <a href="contact.html">Contact</a>
const link = document.querySelector('a');
console.log(link.getAttribute('href')); // "contact.html" (The literal string)
console.log(link.href); // "https://example.com/contact.html" (Resolved property)
5. The dataset API: A Special Case
To bridge the gap between custom HTML attributes and clean JS properties, the data-* attribute was introduced. You define it as an attribute in HTML, and JavaScript provides a clean dataset property to access it.
// HTML: <div data-user-id="55"></div>
const div = document.querySelector('div');
console.log(div.dataset.userId); // "55" (The kebab-case is converted to camelCase)
