Core ยท jQuery-compatible

Attributes, properties & data

Three different things, three methods: .attr() for HTML attributes, .prop() for live DOM properties, .data() for values you attach to elements.

Which one do I use?

  • .attr() reads and writes what is in the HTML: href, aria-*, role. Screen readers and CSS selectors see these.
  • .prop() touches the live DOM object: checked, disabled, value, scrollHeight. Use it for state that changes after load.
  • .data() reads data-* attributes (JSON is parsed for you) and stores anything โ€” objects, dates, functions โ€” against an element.

Leak-free by construction

Stored data lives in a WeakMap keyed by the element. When the element is gone, so is its data. Nothing is written onto your nodes, and there is no global cache to clean up.

Live ยท Account card inspector

Ada Lovelace

pro ยท 5 seats

Plan settings
View the source for this demo
HTML
<article id="user-card" class="plate" data-user='{"name":"Ada","plan":"pro","seats":5}' style="box-shadow:none">
  <h4 style="margin:0">Ada Lovelace</h4>
  <p class="plan muted" style="margin:4px 0 12px">pro ยท 5 seats</p>
  <fieldset id="plan-fields" style="border:0;padding:0;display:flex;gap:16px;flex-wrap:wrap;align-items:center">
    <legend class="sr-only">Plan settings</legend>
    <label>Seats <input type="number" value="5" min="1" style="width:90px"></label>
    <label><input type="checkbox" checked> Billing emails</label>
  </fieldset>
</article>
<div class="row" style="margin-top:14px">
  <button type="button" id="read">Read data</button>
  <button type="button" id="upgrade">Upgrade plan</button>
  <button type="button" id="lock">Lock fields</button>
  <button type="button" id="fav" aria-pressed="false">โ˜† Favourite</button>
</div>
<pre class="out" id="attr-out" aria-live="polite"></pre>
JavaScript
const $card = $('#user-card');

function inspect(note) {
  $('#attr-out').text(note + '\n\n' +
    ".data('user')         โ†’ " + JSON.stringify($card.data('user')) + '\n' +
    ".attr('data-user')    โ†’ " + $card.attr('data-user') + '\n' +
    ".prop('disabled')     โ†’ " + $('#plan-fields').prop('disabled') + '\n' +
    ".attr('aria-pressed') โ†’ " + $('#fav').attr('aria-pressed'));
}

$('#read').on('click', () => inspect('data-* JSON is parsed into an object for you.'));

$('#upgrade').on('click', () => {
  const user = $card.data('user');
  $card.data('user', { ...user, plan: 'team', seats: user.seats + 5 });   // stored in a WeakMap
  $card.find('.plan').text('team ยท ' + $card.data('user').seats + ' seats');
  inspect('.data() stored a new object โ€” the HTML attribute is untouched.');
});

$('#lock').on('click', function () {
  const locked = !$('#plan-fields').prop('disabled');
  $('#plan-fields').prop('disabled', locked);          // live DOM property
  $(this).text(locked ? 'Unlock fields' : 'Lock fields');
  inspect('.prop() flipped the live disabled property.');
});

$('#fav').on('click', function () {
  const on = $(this).attr('aria-pressed') !== 'true';
  $(this).attr('aria-pressed', String(on)).text(on ? 'โ˜… Favourite' : 'โ˜† Favourite');
  inspect('.attr() wrote aria-pressed โ€” screen readers announce the new state.');
});

inspect('Ready. Try the buttons.');

More examples

Everyday calls

JavaScript
$('a.external').attr({ target: '_blank', rel: 'noopener' });
$('#agree').prop('checked');                 // true / false
$('#save').prop('disabled', true);
$('#chart').data('series', bigArrayOfPoints); // any value, no serialization
$('#chart').removeData('series');

API

.attr(name [, value]) ยท .attr(object) โ†’ string | self

Get an attribute from the first element, or set one or many on all elements.

Reference: .attr() โ†’
.removeAttr(name) โ†’ self

Remove an attribute from every element in the set.

Reference: .removeAttr() โ†’
.prop(name [, value]) โ†’ any | self

Get or set a DOM property (as opposed to an HTML attribute) โ€” checked, disabled, selectedIndex, etc.

Reference: .prop() โ†’
.data([key [, value]]) ยท .data(object) โ†’ any | self

Read data-* attributes (JSON auto-parsed) or store arbitrary values against elements. Stored values live in a WeakMap โ€” no memory leaks, no cleanup.

Reference: .data() โ†’
.removeData(key) โ†’ self

Delete a stored data value.

Reference: .removeData() โ†’
.val([value]) โ†’ any | self

Get or set form values. Checkboxes read/write booleans; multi-selects read as arrays.

Reference: .val() โ†’

Migrating

Coming from jQuery.data() behaves the same (JSON parsing, object stores). One deliberate difference: .val() on a checkbox returns true/false rather than its value attribute โ€” use .attr("value") if you need that.

Related features