jQuery event.data

Beginner
⏱️ 10 min read
📚 Updated: Jul 2026
🎯 5 Examples
🚀 5 Try-it labs
Event object · jQuery 1.1+

What You’ll Learn

event.data holds the optional object passed to .on() when a handler is bound. This tutorial covers the official for-loop demo, fixing closure bugs, delegation with eventData, multiple properties, and how event.data differs from .data().

01

Property

event.data

02

eventData

.on(…, { }, fn)

03

Official

Loop index fix

04

Delegate

Action payloads

05

vs .data()

Not the same

06

Since 1.1

Object return

Introduction

Handlers often need extra context — which row was clicked, what ID to delete, or which menu action to run. jQuery lets you attach a plain object when binding: .on("click", { id: 42 }, handler). Inside the callback, read it as event.data.id.

The official jQuery API documents event.data since version 1.1. Its most famous use is fixing the classic loop closure bug: without eventData, every button in a for loop may report the same final index. Passing { value: i } preserves each iteration’s value on the event object.

Understanding event.data

event.data is a snapshot of the object you supplied to .on() at bind time:

  • Direct bind$("#btn").on("click", { action: "save" }, fn)event.data.action is "save".
  • Delegation$("#list").on("click", "li", { mode: "edit" }, fn) → same event.data for every matching child click.
  • Loop fix.on("click", { value: i }, fn) stores each i separately per handler.
  • Not element storage — unlike $("#el").data(), it travels with the handler binding, not the DOM node.
💡
Beginner Tip

If every button in a loop logs the same number, pass { value: i } to .on() and read event.data.value — that is exactly what the official jQuery demo teaches.

📝 Syntax

Pass eventData as the argument before the handler in .on() (since 1.7):

jQuery
.on( events [, selector ], eventData, handler )

// direct binding:
$( "#btn" ).on( "click", { id: 1, label: "Save" }, function ( event ) {
  console.log( event.data.id, event.data.label );
});

// delegation:
$( "#menu" ).on( "click", "button", { action: "delete" }, function ( event ) {
  console.log( event.data.action );
});

Type

  • Object — any plain object jQuery can copy onto the event; strings, numbers, nested objects, and booleans are fine.

Return value

  • Not a method — a read-only property on the event object for the current handler invocation.
  • Reflects the eventData object passed when this specific handler was bound.

Official jQuery API example

jQuery
var logDiv = $( "#log" );
for ( var i = 0; i < 5; i++ ) {
  $( "button" ).eq( i ).on( "click", { value: i }, function ( event ) {
    var msgs = [
      "button = " + $( this ).index(),
      "event.data.value = " + event.data.value,
      "i = " + i
    ];
    logDiv.append( msgs.join( ", " ) + " " );
  });
}

⚡ Quick Reference

GoalCode
Pass data on bind$("#x").on("click", { id: 1 }, fn)
Read in handlerevent.data.id
Delegation + data$("#p").on("click", "li", { a: "x" }, fn)
Fix loop index.on("click", { value: i }, fn)
Not the same as$("#el").data("key") — element storage

📋 event.data vs closures, .data(), and attributes

Four ways to attach context — pick the right tool.

event.data
.on("click", { id: 1 }, fn)

Bind-time payload

IIFE closure
(function(v){ ... })(i)

Capture loop var

.data()
$("#el").data("id", 1)

On DOM element

data-* attr
data-id="1"

HTML markup

Examples Gallery

Example 1 follows the official jQuery event.data demo. Examples 2–5 cover simple payloads, delegation actions, multiple properties, and comparing closure i with event.data.value.

📚 Official jQuery Demo

Five buttons in a loop — event.data.value preserves each index.

Example 1 — Official Demo: loop with { value: i }

Official jQuery demo — each button logs its index via event.data.value while closure i shows the loop pitfall.

jQuery
var logDiv = $( "#log" );
for ( var i = 0; i < 5; i++ ) {
  $( "button" ).eq( i ).on( "click", { value: i }, function ( event ) {
    var msgs = [
      "button = " + $( this ).index(),
      "event.data.value = " + event.data.value,
      "i = " + i
    ];
    logDiv.append( msgs.join( ", " ) + " " );
  });
}
Try It Yourself

How It Works

jQuery stores a copy of { value: i } per binding. When the handler runs, event.data.value reflects the i from that iteration — not the shared loop variable after the loop finishes.

Example 2 — Simple Payload: { id, label }

Pass a small object when binding — read fields inside the handler without global variables.

jQuery
$( "#save" ).on( "click", { id: 42, label: "Save draft" }, function ( event ) {
  console.log( event.data.id + ": " + event.data.label );
});
Try It Yourself

How It Works

The object is attached at bind time. The handler receives it on every click through event.data — no need to close over outer variables.

📈 Practical Patterns

Delegation, rich payloads, and closure comparison.

Example 3 — Delegation: shared { action: "delete" }

One parent handler — every .remove button click reads the same action from event.data.

jQuery
$( "#rows" ).on( "click", ".remove", { action: "delete" }, function ( event ) {
  if ( event.data.action === "delete" ) {
    $( this ).closest( "tr" ).remove();
  }
});
Try It Yourself

How It Works

Delegation syntax is .on(event, selector, eventData, handler). The data object is shared by all delegated matches — use $(this) for row-specific DOM work.

Example 4 — Multiple Fields: API endpoint + method

Pass several properties — endpoint, method, and confirm flag — in one eventData object.

jQuery
$( ".sync" ).on( "click", {
  url: "/api/sync",
  method: "POST",
  confirm: true
}, function ( event ) {
  if ( event.data.confirm && !window.confirm( "Sync now?" ) ) return;
  $.ajax( { url: event.data.url, method: event.data.method } );
});
Try It Yourself

How It Works

event.data can hold any serializable fields your handler needs. Keep objects plain — jQuery copies them at bind time.

Example 5 — Side-by-Side: wrong closure vs event.data

Bind one button without eventData and one with — compare bare i against event.data.value.

jQuery
for ( var i = 0; i < 3; i++ ) {
  $( "#bad-" + i ).on( "click", function () {
    console.log( "bad i =", i );
  });
  $( "#good-" + i ).on( "click", { value: i }, function ( event ) {
    console.log( "good value =", event.data.value );
  });
}
Try It Yourself

How It Works

All bad handlers share one i variable. eventData gives each good handler its own frozen value — the pattern the official docs recommend for loops.

🚀 Common Use Cases

  • Loop bindings — pass { value: i } in for loops (official demo).
  • Toolbar actions — one handler function, different event.data.action per button.
  • Delegated tables.on("click", ".edit", { mode: "edit" }, fn).
  • Plugin config — pass defaults merged with per-element options at bind time.
  • Testing — bind with predictable payloads; trigger with extra args when needed.
  • Avoid globals — keep IDs and labels on event.data instead of window state.

🧠 How jQuery Sets event.data

1

Bind with .on()

You pass eventData as the object argument before the handler function.

Bind
2

jQuery stores a copy

Each handler keeps its own snapshot — loop indices and configs do not collide.

Store
3

Event fires

User click or .trigger() invokes the handler with a normalized event object.

Fire
4

Read event.data

Handler reads event.data.field — the payload from bind time for this handler.

📝 Notes

  • Available since jQuery 1.1 — returns an Object.
  • Only present when you pass eventData to .on() (or compatible bind APIs).
  • Not the same as $("#el").data() — that stores data on elements.
  • Changing event.data in the handler does not update the stored bind payload for future events.
  • Delegation form: .on(type, selector, data, handler) — data sits before the function.
  • Modern alternative in loops: let block scope or IIFE — eventData remains the jQuery-native fix.

Browser Support

event.data is part of jQuery’s normalized event object since jQuery 1.1+. It works consistently anywhere jQuery event binding runs — independent of browser, because jQuery attaches the payload when the handler is registered.

jQuery 1.1+ · all browsers

jQuery event.data

Bind-time payload on the event object — fix loop closures cleanly.

100% jQuery only
Google Chrome All versions · Desktop & Mobile
Full support
Mozilla Firefox All versions · Desktop & Mobile
Full support
Apple Safari All versions · macOS & iOS
Full support
Microsoft Edge All versions · Chromium & Legacy
Full support
Internet Explorer IE 6+ · Legacy environments
Full support
Opera All modern versions
Full support
event.data Object

Bottom line: Pass { value: i } in loops — read event.data.value in handlers.

Conclusion

event.data carries the optional object you pass to .on() when binding a handler. The official demo shows why it matters in loops: event.data.value keeps each button’s index while a bare closure variable does not.

Use eventData for toolbar actions, delegation, and any handler that needs config without globals. Remember it is bind-time data — not .data() on DOM nodes.

💡 Best Practices

✅ Do

  • Pass { value: i } in loops — official jQuery pattern
  • Use plain objects with clear property names
  • Put delegation data before the handler: .on("click", "li", data, fn)
  • Guard with if (event.data && event.data.id) when optional
  • Prefer data-* attributes when values live in HTML markup

❌ Don’t

  • Confuse event.data with $("#el").data()
  • Mutate event.data expecting bind storage to update
  • Share one mutable object across bindings unless intentional
  • Assume event.data exists without passing eventData
  • Rely on closure loop variables without let or eventData

Key Takeaways

Knowledge Unlocked

Five things to remember about event.data

Bind-time payload on the event object.

5
Core concepts
.on 02

eventData

3rd arg

Syntax
for 03

Loop fix

{ value: i }

Demo
li 04

Delegate

Shared action

Pattern
.data() 05

Not same

DOM storage

Compare

❓ Frequently Asked Questions

event.data is the optional object you pass as the third argument to .on() — .on('click', { value: 1 }, handler). jQuery attaches it to the event object so your handler can read custom values via event.data. Available since jQuery 1.1.
Use .on('click', { key: value }, function(event) { ... }). Inside the handler, read event.data.key. For delegation, use .on('click', 'selector', { key: value }, handler).
Without event.data, all handlers in a loop often share the same closure variable — every click reports the final loop index. Passing { value: i } to .on() captures each iteration value separately on event.data.value.
No. event.data is the object passed when binding a handler with .on(). jQuery .data() stores key-value pairs on DOM elements. They solve different problems — handler context vs element storage.
event.data may be undefined or an empty object depending on jQuery version and binding path. Always pass an object when you rely on custom fields, or guard with if (event.data && event.data.id).
Yes. .trigger('click', [{ id: 42 }]) can supply extra arguments that become available on the event object for triggered handlers — useful for testing. Direct user clicks use the data object from .on() binding.
Did you know?

Before let block scope was widely available, event.data was the idiomatic jQuery way to fix loop index bugs. The official demo still logs both event.data.value (correct) and bare i (wrong after the loop) to teach the difference side by side.

Next: event.delegateTarget

Learn the element where delegated .on() handlers are attached.

event.delegateTarget tutorial →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

6 people found this page helpful