Question
I am using the jQuery Quicksand plugin and need to read the data-id value from a clicked list item, then pass that value to a web service.
How can I correctly get the data-id attribute?
I am rebinding the click event with .on() because the items are sorted dynamically.
$("#list li").on('click', function() {
// ret = DetailsView.GetProject($(this).attr("#data-id"), OnComplete, OnTimeOut, OnError);
alert($(this).attr("#data-id"));
});
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
<ul id="list" class="grid">
<li data-id="id-40" class="win">
<a id="ctl00_cphBody_ListView1_ctrl0_SelectButton" class="project" href="#">
<img src="themes/clean/images/win.jpg" class="project-image" alt="get data-id" />
</a>
</li>
</ul>
Short Answer
By the end of this page, you will understand how to read custom data-* attributes in jQuery, when to use .attr() versus .data(), and how to attach click handlers correctly for dynamically updated elements.
Concept
Custom data-* attributes let you store extra information directly on HTML elements. In your example, the li element stores a project identifier in data-id:
<li data-id="id-40"></li>
In jQuery, you can read this value in two common ways:
$(this).attr('data-id')
$(this).data('id')
The main issue in the original code is this part:
$(this).attr("#data-id")
That is incorrect because .attr() expects the attribute name only, not a CSS selector. The correct attribute name is data-id, not #data-id.
Why this matters:
data-*attributes are widely used for IDs, flags, categories, and configuration.- They help connect HTML elements to JavaScript behavior.
- They are especially useful in lists, buttons, cards, and dynamically generated UI items.
If elements are replaced or sorted dynamically, direct event binding may stop working. In those cases, event delegation with .on() is often the safest approach.
Mental Model
Think of an HTML element as a labeled storage box.
- The element itself is the box.
- Normal attributes like
id,class, andhrefare built-in labels. data-*attributes are your own extra sticky notes.
So this element:
<li data-id="id-40"></li>
is like a box with a note that says:
data-id = id-40
When a user clicks the element, jQuery lets you open the box and read that note.
Syntax and Examples
The two most common ways to read a data-id value in jQuery are:
$(this).attr('data-id')
$(this).data('id')
Basic example
$("#list li").on("click", function () {
alert($(this).attr("data-id"));
});
This reads the data-id attribute from the clicked li.
Using .data()
$("#list li").on("click", function () {
alert($(this).data("id"));
});
This also works and is often cleaner when working with data-* attributes.
Better for dynamically changed items: delegated event binding
If items are re-rendered, sorted, or replaced, use delegated events:
$("#list").on("click", "li", function () {
const projectId = $(this).data("id");
alert(projectId);
});
This attaches the event handler to #list, and jQuery listens for clicks that bubble up from any li inside it.
Passing the value to a function
$("#list").on("click", "li", function () {
const projectId = $(this).data("id");
DetailsView.GetProject(projectId, OnComplete, OnTimeOut, OnError);
});
This is the common real-world pattern: read a value from the clicked element, then use it in an API or service call.
Step by Step Execution
Consider this code:
$("#list").on("click", "li", function () {
const projectId = $(this).data("id");
alert(projectId);
});
And this HTML:
<ul id="list">
<li data-id="id-40">Project A</li>
</ul>
Here is what happens step by step:
- jQuery attaches one click listener to
#list. - You click on the
lielement. - The click event bubbles up to
#list. - jQuery checks whether the clicked target matches the selector
li. - Since it does match, the handler runs.
- Inside the handler,
thisrefers to the clickedli. $(this).data("id")reads the value fromdata-id.- The value
id-40is stored inprojectId. alert(projectId)showsid-40.
Trace:
- Clicked element:
li this: clickedli$(this).data("id"):"id-40"- Result: alert shows
id-40
Real World Use Cases
data-* attributes are very common in real applications.
Common uses
- Storing a database record ID on a row or card
- Attaching product IDs to "Add to cart" buttons
- Keeping category names for filtering items
- Marking elements with status flags like
data-active="true" - Passing small configuration values from HTML to JavaScript
Example: product card
<button class="add-to-cart" data-product-id="101">Add to cart</button>
$(document).on("click", ".add-to-cart", function () {
const productId = $(this).data("product-id");
console.log("Add product", productId);
});
Example: table row actions
<tr data-user-id="55">
<td>Alice</td>
<td><button class="view-user">View</button></td>
</tr>
$(document).on("click", ".view-user", function () {
const userId = $(this).closest("tr").data("user-id");
console.log("Load user", userId);
});
This pattern is useful when the clicked element is inside another element that holds the data.
Real Codebase Usage
In real projects, developers usually combine data-* attributes with event delegation and small handler functions.
Common pattern: delegated click handling
$("#list").on("click", "li", function () {
const projectId = $(this).data("id");
if (!projectId) return;
DetailsView.GetProject(projectId, OnComplete, OnTimeOut, OnError);
});
This uses a guard clause:
if (!projectId) return;
That avoids making a bad request if the attribute is missing.
Common pattern: click on child, read from parent
Sometimes the click happens on an inner link or image, but the ID is stored on the parent:
$("#list").on("click", "a.project", function (event) {
event.preventDefault();
const projectId = $(this).closest("li").data("id");
if (!projectId) return;
DetailsView.GetProject(projectId, OnComplete, OnTimeOut, OnError);
});
Validation before sending to a service
$("#list").on("click", "li", function () {
const projectId = $(this).data("id");
if (typeof projectId !== "string" || projectId.trim() === "") {
console.error("Missing or invalid project ID");
return;
}
DetailsView.GetProject(projectId, OnComplete, OnTimeOut, OnError);
});
These patterns make code more reliable when markup changes or data is incomplete.
Common Mistakes
1. Using #data-id instead of data-id
Broken code:
$(this).attr("#data-id")
Why it is wrong:
#data-idlooks like a CSS selector..attr()expects just the attribute name.
Correct code:
$(this).attr("data-id")
or:
$(this).data("id")
2. Binding directly to elements that get replaced
Broken approach:
$("#list li").on("click", function () {
alert($(this).data("id"));
});
This works only for the li elements that exist when the code runs. If Quicksand replaces or reorders elements, the handler may stop working.
Better approach:
$("#list").on("click", "li", function () {
alert($(this).data("id"));
});
3. Reading from the wrong element
If the user clicks the inner a or img, the ID may be on the parent li, not the clicked child.
Example:
$("#list").on("click", "a.project", function () {
alert($(this).data("id")); // undefined if data-id is on li
});
Correct:
$("#list").on("click", "a.project", function () {
alert($(this).closest("li").data("id"));
});
4. Forgetting to stop link navigation
If you click an <a href="#">, the page may jump.
$("#list").on("click", "a.project", function (event) {
event.preventDefault();
const projectId = $(this).closest("li").data("id");
alert(projectId);
});
5. Assuming the value always exists
Always check before using it in a request.
const projectId = $(this).data("id");
if (!projectId) {
console.error("No data-id found");
return;
}
Comparisons
| Approach | Example | Best use | Notes |
|---|---|---|---|
.attr('data-id') | $(this).attr('data-id') | Read the raw attribute value | Good when you want the exact attribute string |
.data('id') | $(this).data('id') | Work with data-* values in jQuery | Cleaner and more semantic for custom data |
| Direct binding | $('#list li').on('click', fn) | Static elements already on the page | May fail if elements are replaced dynamically |
| Delegated binding | $('#list').on('click', 'li', fn) | Dynamic or re-rendered elements | Preferred for sorted, filtered, or injected items |
.attr() vs .data()
- Use
.attr('data-id')when you want the literal HTML attribute value. - Use
.data('id')when you are working with custom data in a jQuery-friendly way.
Direct binding vs delegated binding
- Direct binding attaches the event to each matched element now.
- Delegated binding attaches one event to a stable parent and works for current and future matching children.
For Quicksand-style DOM updates, delegated binding is usually the safer choice.
Cheat Sheet
Reading data-* in jQuery
$(element).attr('data-id')
$(element).data('id')
Correct examples
$(this).attr('data-id')
$(this).data('id')
Incorrect example
$(this).attr('#data-id')
Event binding
Static elements
$('#list li').on('click', function () {
console.log($(this).data('id'));
});
Dynamic elements
$('#list').on('click', 'li', function () {
console.log($(this).data('id'));
});
If the click is on a child element
$('#list').on('click', 'a.project', function (event) {
event.preventDefault();
console.log($(this).closest('li').data('id'));
});
Rules to remember
- Use
data-id, not#data-id #is for selectors, not attribute names- Use event delegation for dynamic DOM updates
- Use
.closest()when the data is stored on a parent element - Validate the value before sending it to a service
FAQ
How do I get a data-id attribute in jQuery?
Use either:
$(this).attr('data-id')
or:
$(this).data('id')
Why does $(this).attr('#data-id') return undefined?
Because #data-id is not an attribute name. The # symbol is used in CSS selectors for IDs. The correct attribute name is data-id.
Should I use .attr() or .data() in jQuery?
For data-* attributes, .data() is usually more readable. .attr() is also valid when you want the raw attribute value.
How do I handle clicks on elements added after page load?
Use delegated events:
$('#list').on('click', 'li', function () {
console.log($(this).data('id'));
});
What if the data-id is on the parent li, but I click the link inside it?
Use .closest() to move up to the parent:
$(this).closest('li').data('id')
Can I store more than one custom value with data-* attributes?
Yes. For example:
<li data-id="40" data-status="active" data-category="design"></li>
Then read them with jQuery:
$(this).data('id')
$(this).data('status')
$(this).data('category')
Mini Project
Description
Build a small project list where each item has a data-id. When a user clicks a project link, your script should read the ID and display which project would be loaded. This demonstrates reading data-* attributes, delegated click handling, and accessing data stored on parent elements.
Goal
Create a clickable project list that reads data-id values correctly and uses them in JavaScript logic.
Requirements
- Create a list with at least three
liitems, each containing a uniquedata-id. - Put a clickable link inside each list item.
- Use delegated event binding with
.on()on the parent list. - Prevent the default link behavior.
- Read the
data-idfrom the clicked item's parentliand show it on the page.
Keep learning
Related questions
Add a Favicon to a Static HTML Page
Learn how to add a favicon to static HTML pages, use correct paths and icon formats, and troubleshoot browser caching and local-file issues.
Allow Only Numeric Input (0-9) in HTML Input Using jQuery
Learn how to allow only digits 0-9 in an HTML input using jQuery, with examples, validation tips, common mistakes, and best practices.
Apply CSS !important with jQuery .css()
Learn why jQuery .css() cannot set !important and how to use CSSStyleDeclaration.setProperty() to apply inline important styles safely.