Attributes and properties
Every HTML element is written with attributes:
<a id="docs" href="/learn/" title="Courses">Learn</a>
<input id="agree" type="checkbox" checked>When the browser reads that HTML, it creates an element object in memory. That object has properties, many of which start out copied from the attributes. But they are not the same thing:
- The attribute is the text in the HTML. It usually does not change unless code changes it.
- The property is the live value. It changes as the user interacts: ticking a box changes the
checkedproperty, typing changes thevalueproperty.
jQuery gives you .attr() for attributes and .prop() for properties.
Reading and setting attributes
<a id="docs" href="/learn/" title="Courses">Learn</a>
<img id="logo" src="logo-small.png" alt="">
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
console.log("href:", $("#docs").attr("href"));
console.log("missing:", $("#docs").attr("data-missing"));
$("#docs").attr("href", "/learn/jquery/");
$("#logo").attr({ src: "logo-large.png", alt: "Company logo", width: 200 });
$("#docs").removeAttr("title");
console.log("new href:", $("#docs").attr("href"));
console.log("logo:", $("#logo").attr("src"), "|", $("#logo").attr("alt"), "|", $("#logo").attr("width"));
console.log("title after removeAttr:", $("#docs").attr("title"));
</script>href: /learn/
missing: undefined
new href: /learn/jquery/
logo: logo-large.png | Company logo | 200
title after removeAttr: undefined.attr(name)reads an attribute; a missing attribute givesundefined..attr(name, value)sets it on every selected element..attr({ ... })sets several at once..removeAttr(name)deletes it.
Use .attr() for things that really are attributes: href, src, alt, title, id, rel, target, aria-* and role.
The checkbox problem
Here is the bug that has confused many developers. The checkbox starts ticked in the HTML. Then the user (here, our code) unticks it:
<label><input id="agree" type="checkbox" checked> I agree</label>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
$("#agree")[0].click(); // the user unticks it
console.log("attr:", JSON.stringify($("#agree").attr("checked")));
console.log("prop:", $("#agree").prop("checked"));
console.log("is(':checked'):", $("#agree").is(":checked"));
</script>attr: ""
prop: false
is(':checked'): false.attr("checked")still reports the original HTML: the attribute is there (with an empty value), even though the box is no longer ticked..prop("checked")reports the live state:false..is(":checked")also checks the live state and reads nicely inifstatements.
So to read whether a box is ticked, always use .prop("checked") or .is(":checked"). Older tutorials show .attr("checked"); that pattern is wrong and produces bugs as soon as the user changes the box.
Setting state with .prop()
The same rule applies when you change state. Use .prop() with true or false:
<label><input id="terms" type="checkbox"> I accept the terms</label>
<button id="send" disabled>Send</button>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
$("#terms").on("change", function () {
const accepted = $(this).prop("checked");
$("#send").prop("disabled", !accepted);
console.log("accepted:", accepted, "| button disabled:", $("#send").prop("disabled"));
});
</script>accepted: true | button disabled: falseThe button starts disabled and becomes usable when the box is ticked. This "enable the button once the form is valid" pattern is everywhere.
The main boolean properties to manage with .prop() are checked, selected, disabled, readonly, required and multiple.
Properties that are not attributes
Some properties have no attribute at all, so only .prop() can read them:
<select id="size">
<option>Small</option>
<option selected>Medium</option>
<option>Large</option>
</select>
<a id="rel" href="/learn/">Learn</a>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
console.log("selectedIndex:", $("#size").prop("selectedIndex"));
console.log("tagName:", $("#size").prop("tagName"));
console.log("href attr:", $("#rel").attr("href"));
console.log("href prop ends with:", $("#rel").prop("href").endsWith("/learn/"));
</script>selectedIndex: 1
tagName: SELECT
href attr: /learn/
href prop ends with: trueNotice the href difference: the attribute is exactly what you wrote (/learn/), while the property is the full resolved address including the page's origin. Use .attr("href") when you want what is written, and .prop("href") when you want the absolute URL.
A quick rule
| You want to... | Use |
|---|---|
read or change href, src, alt, title, id, aria-*, data-* (as text) |
.attr() |
read or change checked, selected, disabled, readonly |
.prop() |
| read or change a form field's current value | .val() (next lesson) |
| remove an attribute entirely | .removeAttr() |
Common mistakes
- Reading
.attr("checked")to see if a box is ticked. Use.prop("checked"). - Setting
.attr("disabled", false). The attribute's presence is what disables the element, so this does not enable it. Use.prop("disabled", false)or.removeAttr("disabled"). - Using
.removeProp()on built-in properties such aschecked. It cannot remove them; set them tofalseinstead. - Using
.attr()for data you want as numbers or objects. The.data()lesson shows a better tool.
Exercise
Make a Show password checkbox. When it is ticked, change the password field's type attribute to text; when unticked, back to password.
Show one possible answer
<input id="pw" type="password" value="secret123">
<label><input id="show" type="checkbox"> Show password</label>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
$("#show").on("change", function () {
$("#pw").attr("type", $(this).prop("checked") ? "text" : "password");
});
</script>The type is an attribute, so .attr() changes it; the checkbox state is a property, so .prop() reads it.
Key points
- Attributes are what is written in the HTML; properties are the live values on the element object in the browser.
- Use .attr() for attributes such as href, src, alt, title, id and aria-*; use .removeAttr() to delete one.
- Use .prop() for live state: checked, selected, disabled and the current value.
- .attr("checked") reports the original HTML, not whether the box is ticked now; .prop("checked") gives true or false.