0
# Event System
1
2
Custom event system and delegation utilities for managing user interactions and library events throughout Rails UJS.
3
4
## Capabilities
5
6
### Custom Event Firing
7
8
Creates and dispatches custom events with data, providing a way to communicate between Rails UJS and application code.
9
10
```javascript { .api }
11
/**
12
* Dispatch custom event on target element
13
* @param obj - EventTarget to dispatch event on
14
* @param name - Event name to dispatch
15
* @param data - Optional data to include in event.detail
16
* @returns True if event was not cancelled (defaultPrevented is false)
17
*/
18
function fire(obj: EventTarget, name: string, data?: any): boolean;
19
```
20
21
**Usage Examples:**
22
23
```javascript
24
const form = document.querySelector("form");
25
26
// Fire simple event
27
const shouldContinue = Rails.fire(form, "custom:before-submit");
28
if (!shouldContinue) {
29
console.log("Event was cancelled");
30
return;
31
}
32
33
// Fire event with data
34
Rails.fire(document, "rails:navigation", {
35
url: "/posts/1",
36
method: "GET"
37
});
38
39
// Listen for Rails UJS events
40
document.addEventListener("ajax:success", function(event) {
41
console.log("AJAX success:", event.detail);
42
});
43
44
// Cancel events by preventing default
45
document.addEventListener("confirm", function(event) {
46
if (!userIsAdmin) {
47
event.preventDefault(); // Cancels the action
48
}
49
});
50
```
51
52
### Event Delegation
53
54
Efficient event delegation system that handles events for dynamically created elements.
55
56
```javascript { .api }
57
/**
58
* Set up delegated event listener on container element
59
* @param element - Container element to listen on (usually document)
60
* @param selector - CSS selector for target elements
61
* @param eventType - Event type to listen for (click, submit, etc.)
62
* @param handler - Handler function to call when event matches
63
*/
64
function delegate(element: Element, selector: string, eventType: string, handler: Function): void;
65
```
66
67
**Usage Examples:**
68
69
```javascript
70
// Delegate click events for all remote links
71
Rails.delegate(document, "a[data-remote]", "click", function(event) {
72
console.log("Remote link clicked:", this.href);
73
// 'this' refers to the clicked link
74
});
75
76
// Delegate form submissions
77
Rails.delegate(document, "form[data-remote]", "submit", function(event) {
78
console.log("Remote form submitted:", this.action);
79
});
80
81
// Custom delegation for application logic
82
Rails.delegate(document, ".toggle-button", "click", function(event) {
83
this.classList.toggle("active");
84
});
85
86
// Delegation works with complex selectors
87
Rails.delegate(document, "button[data-confirm]:not([disabled])", "click", confirmHandler);
88
```
89
90
### Event Stopping
91
92
Comprehensive event stopping utility that prevents all forms of event propagation.
93
94
```javascript { .api }
95
/**
96
* Stop event propagation, prevent default, and fire stopping event
97
* @param event - Event to stop completely
98
*/
99
function stopEverything(event: Event): void;
100
```
101
102
**Usage Examples:**
103
104
```javascript
105
// Completely stop an event
106
document.addEventListener("click", function(event) {
107
if (event.target.disabled) {
108
Rails.stopEverything(event);
109
return;
110
}
111
});
112
113
// Used internally by Rails UJS
114
function handleDisabledElement(event) {
115
const element = this;
116
if (element.disabled) {
117
Rails.stopEverything(event); // Prevents any further processing
118
}
119
}
120
121
// Fires 'ujs:everythingStopped' event for debugging
122
document.addEventListener("ujs:everythingStopped", function(event) {
123
console.log("Event was completely stopped on:", event.target);
124
});
125
```
126
127
## Rails UJS Events
128
129
Rails UJS fires numerous custom events during its operation:
130
131
### Confirmation Events
132
133
```javascript
134
// Before showing confirmation dialog
135
document.addEventListener("confirm", function(event) {
136
// event.target is the element being confirmed
137
// Return false or preventDefault() to skip confirmation
138
});
139
140
// After confirmation dialog is handled
141
document.addEventListener("confirm:complete", function(event) {
142
const [answer] = event.detail; // true if user confirmed, false if cancelled
143
console.log("User response:", answer);
144
});
145
146
// Custom confirmation handling
147
document.addEventListener("confirm", function(event) {
148
event.preventDefault();
149
customConfirmDialog(event.target.dataset.confirm)
150
.then(confirmed => {
151
if (confirmed) {
152
Rails.fire(event.target, "confirm:complete", [true]);
153
// Continue with original action
154
}
155
});
156
});
157
```
158
159
### AJAX Events
160
161
```javascript
162
// Before AJAX request starts
163
document.addEventListener("ajax:before", function(event) {
164
console.log("About to make AJAX request");
165
// Return false to cancel request
166
});
167
168
// Before XHR is sent (after beforeSend callback)
169
document.addEventListener("ajax:beforeSend", function(event) {
170
const [xhr, options] = event.detail;
171
console.log("Sending request to:", options.url);
172
});
173
174
// When XHR is actually sent
175
document.addEventListener("ajax:send", function(event) {
176
const [xhr] = event.detail;
177
console.log("Request sent, readyState:", xhr.readyState);
178
});
179
180
// When AJAX request is stopped/cancelled
181
document.addEventListener("ajax:stopped", function(event) {
182
console.log("AJAX request was stopped");
183
});
184
185
// On successful AJAX response (2xx status)
186
document.addEventListener("ajax:success", function(event) {
187
const [data, statusText, xhr] = event.detail;
188
console.log("AJAX success:", data);
189
});
190
191
// On AJAX error response (non-2xx status)
192
document.addEventListener("ajax:error", function(event) {
193
const [response, statusText, xhr] = event.detail;
194
console.error("AJAX error:", xhr.status, statusText);
195
});
196
197
// When AJAX request completes (success or error)
198
document.addEventListener("ajax:complete", function(event) {
199
const [xhr, statusText] = event.detail;
200
console.log("AJAX complete:", xhr.status);
201
});
202
```
203
204
### Initialization Events
205
206
```javascript
207
// Check if Rails UJS should attach bindings
208
document.addEventListener("rails:attachBindings", function(event) {
209
// Return false to prevent Rails UJS from starting
210
console.log("Rails UJS is initializing");
211
});
212
```
213
214
## Event Delegation Implementation
215
216
Rails UJS uses a sophisticated delegation system:
217
218
```javascript
219
// Example of how Rails UJS sets up delegation internally
220
Rails.delegate(document, "a[data-method]", "click", Rails.handleMethod);
221
Rails.delegate(document, "form[data-remote]", "submit", Rails.handleRemote);
222
Rails.delegate(document, "[data-confirm]", "click", Rails.handleConfirm);
223
224
// The delegation system:
225
// 1. Listens on document for efficiency
226
// 2. Checks event.target against selector
227
// 3. Bubbles up DOM tree to find matches
228
// 4. Calls handler with matched element as 'this'
229
```
230
231
## CustomEvent Polyfill
232
233
Rails UJS includes a CustomEvent polyfill for older browsers:
234
235
```javascript
236
// Creates CustomEvent constructor if not available
237
// Ensures consistent behavior across all browsers
238
// Handles preventDefault() properly on polyfilled events
239
```
240
241
## Performance Benefits
242
243
- **Single listener per event type**: Uses delegation instead of individual listeners
244
- **Dynamic content support**: Automatically handles elements added after page load
245
- **Memory efficient**: No need to add/remove listeners when elements change
246
- **Event bubbling**: Leverages native browser event bubbling for efficiency
247
248
## Event Handler Context
249
250
In delegated event handlers, `this` refers to the matched element:
251
252
```javascript
253
Rails.delegate(document, "a[data-remote]", "click", function(event) {
254
// 'this' is the clicked <a> element that matches "a[data-remote]"
255
// event.target might be a child element (like <span> inside the <a>)
256
console.log("Clicked link:", this.href);
257
console.log("Actual target:", event.target.tagName);
258
});
259
```