Attributes, Properties & Data

jQuery · Lesson 10 of 24 6 min read Updated

Elements carry information in three places: attributes written in the HTML, properties on the live DOM object, and data you attach for your own scripts. jQuery gives you .attr(), .prop() and .data() for these, and mixing them up is behind many confusing bugs, especially with checkboxes and data-* values.

This lesson explains each one with a clear rule for when to use it, and shows the plain JavaScript versions: getAttribute(), ordinary properties and dataset.

Attributes with attr()

An attribute is a name="value" pair in the HTML, such as href="/fees" or alt="Logo". Attributes are always strings.

JavaScript
$("#logo").attr("alt");                        // read
$("#logo").attr("alt", "Rohtak Public School"); // write
$("#logo").attr({ width: 120, height: 40 });   // write several at once
$("#logo").removeAttr("title");                // delete

// Plain JavaScript
const logo = document.querySelector("#logo");
logo.getAttribute("alt");
logo.setAttribute("alt", "Rohtak Public School");
logo.removeAttribute("title");
logo.toggleAttribute("hidden", true);          // add when true, remove when false

In jQuery 4, passing false as the value removes the attribute, except for ARIA attributes. .attr("aria-expanded", false) sets the text "false", which is exactly what screen readers need.

Properties with prop()

A property belongs to the DOM object that the browser builds from the HTML. Many properties start from an attribute but then live their own life. The classic example is a checkbox:

  • The checked attribute says whether the box was ticked when the page loaded. It does not change when the user clicks.
  • The checked property says whether the box is ticked right now.

Run this and tick or untick the box a few times:

HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>attr vs prop</title>
</head>
<body>
  <label><input type="checkbox" id="sms" checked> Send me SMS alerts</label>
  <p><a id="fees" href="fees.html">Fee structure</a></p>
  <pre id="out"></pre>

  <script src="https://cdn.jsdelivr.net/npm/jquery@4.0.0/dist/jquery.min.js"></script>
  <script>
    function report() {
      const $box = $("#sms");
      $("#out").text(
        "checked attribute present: " + $box.is("[checked]") + "\n" +
        "checked property:          " + $box.prop("checked") + "\n" +
        "href attribute: " + $("#fees").attr("href") + "\n" +
        "href property:  " + $("#fees").prop("href")
      );
    }
    $("#sms").on("change", report);
    report();
  </script>
</body>
</html>

The attribute line never changes, but the property follows your clicks. The link shows a second difference: the href attribute is exactly what you wrote, while the href property is the full, resolved address.

Rule of thumb: use .prop() (or plain properties) for live state such as checked, selected, disabled and value; use .attr() for everything else, especially custom and ARIA attributes.

TaskjQueryPlain JavaScript
Is the box ticked?$cb.prop("checked") or $cb.is(":checked")cb.checked
Disable a button$btn.prop("disabled", true)btn.disabled = true
Read an image's alt text$img.attr("alt")img.getAttribute("alt") or img.alt
Mark a menu as open$btn.attr("aria-expanded", "true")btn.setAttribute("aria-expanded", "true")

Custom data with data-* attributes

HTML lets you invent attributes that start with data-, such as data-price="1299". They are the standard place to keep information your script needs about an element.

jQuery's .data() reads them, but with two surprises:

  1. It converts types. "1299" becomes the number 1299, "true" becomes true, and JSON text becomes an object or array. A value is converted to a number only if that does not change how it looks, so "007" and "1.50" stay strings.
  2. It reads once, then caches. The first .data() call copies the attributes into jQuery's internal storage. After that, .data("price", 999) updates only that storage, not the HTML attribute, and later changes to the attribute are not seen by .data().

The plain dataset property is simpler: it always reads and writes the real attribute, and every value is a string.

HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>data() vs dataset</title>
</head>
<body>
  <div id="phone" data-price="12999" data-in-stock="true" data-colours='["Black","Blue"]' data-code="007">
    Smartphone 5G
  </div>
  <button id="discount">Apply ₹1,000 discount with .data()</button>
  <pre id="out"></pre>

  <script src="https://cdn.jsdelivr.net/npm/jquery@4.0.0/dist/jquery.min.js"></script>
  <script>
    const $phone = $("#phone");
    const phone = document.querySelector("#phone");

    function show() {
      const d = $phone.data();
      $("#out").text(
        "jQuery .data():\n" +
        "  price " + d.price + " (" + typeof d.price + ")\n" +
        "  inStock " + d.inStock + " (" + typeof d.inStock + ")\n" +
        "  colours " + d.colours.join(" & ") + " (array: " + Array.isArray(d.colours) + ")\n" +
        "  code " + d.code + " (" + typeof d.code + ")\n\n" +
        "Plain dataset:\n" +
        "  price " + phone.dataset.price + " (" + typeof phone.dataset.price + ")\n" +
        "  inStock " + phone.dataset.inStock + " (" + typeof phone.dataset.inStock + ")\n\n" +
        "HTML attribute: data-price=\"" + phone.getAttribute("data-price") + "\""
      );
    }

    $("#discount").on("click", function () {
      $phone.data("price", $phone.data("price") - 1000);
      show();
    });
    show();
  </script>
</body>
</html>

After clicking, .data() shows the new price but the HTML attribute still says 12999. If other code (or your CSS, with [data-price] selectors) reads the attribute, it sees the old value.

Choosing between data() and dataset

  • Use .data() in jQuery code when you want typed values or need to store objects, such as a whole product record, on an element without putting it in the HTML.
  • Use .attr("data-x", value) or dataset when the value must appear in the HTML, for example to style with [data-status="paid"] in CSS.
  • Do not mix both styles for the same key. Pick one per value.
JavaScript
// Store a full object on an element (jQuery only keeps it in memory)
$("#order-1042").data("order", { id: 1042, city: "Kochi", items: 3 });
$("#order-1042").data("order").city;          // "Kochi"

// Plain JavaScript equivalent: a Map keyed by element
const orders = new Map();
orders.set(document.querySelector("#order-1042"), { id: 1042, city: "Kochi", items: 3 });

Common mistakes

  • .attr("checked") to test a checkbox. It reflects the starting HTML, not the current state. Use .prop("checked").
  • Expecting .data("x", v) to change the HTML. It does not; use .attr("data-x", v) if you need the attribute.
  • Doing maths on dataset values. They are strings: "12999" + 1 is "129991". Convert with Number().
  • Expecting "007" to become a number with .data(). jQuery keeps it as a string because converting would change it.
  • Writing data-inStock in HTML. Attribute names are lowercase; write data-in-stock and read it as inStock.

Key points

  • Attributes are the HTML text (.attr(), getAttribute()); properties are the live DOM state (.prop(), el.checked).
  • Use .prop() for checked, selected, disabled and value; .attr() for custom and ARIA attributes.
  • In jQuery 4, .attr(name, false) removes a normal attribute but writes "false" for ARIA attributes.
  • .data() converts types and caches values without updating the HTML; dataset always uses strings and the real attribute.
  • data-in-stock in HTML becomes inStock in both .data() and dataset.

Exercise

Create a "Book a seat" button with data-seats-left="3" and an aria-live paragraph. Each click should reduce the seat count by one using .attr() to read and write the data-seats-left attribute (so the HTML stays in sync), update the paragraph, and when it reaches 0, disable the button with .prop().

Show solution
HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Seats</title>
</head>
<body>
  <button id="book" data-seats-left="3">Book a seat</button>
  <p id="msg" aria-live="polite">3 seats left in the Jaipur workshop.</p>
  <script src="https://cdn.jsdelivr.net/npm/jquery@4.0.0/dist/jquery.min.js"></script>
  <script>
    $("#book").on("click", function () {
      const left = Number($(this).attr("data-seats-left")) - 1;
      $(this).attr("data-seats-left", left);
      $("#msg").text(left + " seats left in the Jaipur workshop.");
      if (left === 0) {
        $(this).prop("disabled", true).text("Fully booked");
      }
    });
  </script>
</body>
</html>

Reading with .attr() gives a string, so Number() converts it before subtracting; writing back with .attr() keeps the HTML attribute up to date.

Quick Quiz

1. A user unticks a checkbox that had checked in the HTML. What does .prop("checked") return?

2. What does $("#d").data("code") return for data-code="007"?

3. After $el.data("price", 500), what does $el.attr("data-price") return if the HTML said data-price="800"?

4. Which plain JavaScript line disables a button?

Learn jQuery with a mentor

Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call