0
# Feature Handlers
1
2
High-level handlers that implement the core Rails UJS behaviors for method overrides, remote requests, confirmations, and element disabling through HTML data attributes.
3
4
## Capabilities
5
6
### Confirmation Handler
7
8
Handles confirmation dialogs for elements with `data-confirm` attributes.
9
10
```javascript { .api }
11
/**
12
* Handle confirmation dialogs for elements with data-confirm
13
* @param event - Event that triggered the confirmation (usually click)
14
*/
15
function handleConfirm(event: Event): void;
16
17
/**
18
* Show confirmation dialog (overridable)
19
* @param message - Confirmation message to display
20
* @param element - Element that triggered the confirmation
21
* @returns True if user confirmed, false if cancelled
22
*/
23
function confirm(message: string, element: Element): boolean;
24
```
25
26
**Usage Examples:**
27
28
```html
29
<!-- Basic confirmation -->
30
<a href="/posts/1" data-method="delete" data-confirm="Are you sure?">Delete</a>
31
32
<!-- Form confirmation -->
33
<form action="/posts" method="post" data-confirm="Submit this post?">
34
<button type="submit">Create</button>
35
</form>
36
37
<!-- Button confirmation -->
38
<button data-remote="true" data-url="/refresh" data-confirm="Refresh data?">Refresh</button>
39
```
40
41
```javascript
42
// Custom confirmation implementation
43
Rails.confirm = function(message, element) {
44
return customConfirmDialog(message, {
45
title: "Please Confirm",
46
element: element
47
});
48
};
49
50
// Listen for confirmation events
51
document.addEventListener("confirm", function(event) {
52
console.log("Confirming action on:", event.target);
53
// event.preventDefault() to skip confirmation
54
});
55
56
document.addEventListener("confirm:complete", function(event) {
57
const [confirmed] = event.detail;
58
console.log("User decision:", confirmed);
59
});
60
```
61
62
### Method Override Handler
63
64
Handles HTTP method override for links with `data-method` attributes.
65
66
```javascript { .api }
67
/**
68
* Handle method override for links with data-method attribute
69
* @param event - Click event from link with data-method
70
*/
71
function handleMethod(event: Event): void;
72
```
73
74
**Usage Examples:**
75
76
```html
77
<!-- DELETE request via link -->
78
<a href="/posts/1" data-method="delete">Delete Post</a>
79
80
<!-- PUT request with confirmation -->
81
<a href="/posts/1/publish" data-method="put" data-confirm="Publish this post?">Publish</a>
82
83
<!-- PATCH request -->
84
<a href="/users/1" data-method="patch">Update User</a>
85
```
86
87
```javascript
88
// Rails UJS automatically:
89
// 1. Prevents normal link navigation
90
// 2. Creates hidden form with correct method
91
// 3. Includes CSRF token for same-origin requests
92
// 4. Submits form to trigger server request
93
94
// Generated form structure:
95
// <form method="post" action="/posts/1" style="display: none">
96
// <input name="_method" value="delete" type="hidden" />
97
// <input name="authenticity_token" value="..." type="hidden" />
98
// <input type="submit" />
99
// </form>
100
```
101
102
### Remote Request Handler
103
104
Handles AJAX requests for elements with `data-remote` attributes.
105
106
```javascript { .api }
107
/**
108
* Handle remote AJAX requests for elements with data-remote
109
* @param event - Event that triggered the remote request
110
*/
111
function handleRemote(event: Event): void;
112
```
113
114
**Usage Examples:**
115
116
```html
117
<!-- Remote link -->
118
<a href="/posts/1" data-remote="true">View Post</a>
119
120
<!-- Remote form -->
121
<form action="/posts" method="post" data-remote="true">
122
<input type="text" name="title">
123
<button type="submit">Create</button>
124
</form>
125
126
<!-- Remote button -->
127
<button data-remote="true" data-url="/refresh" data-method="post">Refresh</button>
128
129
<!-- Remote with custom data type -->
130
<a href="/posts.json" data-remote="true" data-type="json">Get JSON</a>
131
```
132
133
```javascript
134
// Listen for remote request events
135
document.addEventListener("ajax:beforeSend", function(event) {
136
const [xhr, options] = event.detail;
137
console.log("Making request to:", options.url);
138
});
139
140
document.addEventListener("ajax:success", function(event) {
141
const [data] = event.detail;
142
console.log("Remote request succeeded:", data);
143
});
144
145
// Custom handling based on element type
146
document.addEventListener("ajax:success", function(event) {
147
if (event.target.matches("form")) {
148
// Handle form success
149
showNotification("Form submitted successfully");
150
} else if (event.target.matches("a")) {
151
// Handle link success
152
updatePageContent(event.detail[0]);
153
}
154
});
155
```
156
157
### Click Prevention Handler
158
159
Prevents insignificant clicks on links (like meta+click on GET links without data).
160
161
```javascript { .api }
162
/**
163
* Prevent insignificant clicks from triggering Rails UJS behaviors
164
* @param event - Click event to potentially prevent
165
*/
166
function preventInsignificantClick(event: Event): void;
167
```
168
169
**Usage Examples:**
170
171
```javascript
172
// Automatically prevents Rails UJS processing for:
173
// - Meta+click or Ctrl+click on GET links without data-params
174
// - Right-click or middle-click on any link
175
// - This allows normal browser behavior (new tab, context menu, etc.)
176
177
// Example scenarios:
178
// <a href="/posts" data-method="get">Posts</a>
179
// - Normal click: processed by Rails UJS
180
// - Meta+click: opens in new tab (Rails UJS skipped)
181
182
// <a href="/posts/1" data-method="delete">Delete</a>
183
// - Meta+click: still processed by Rails UJS (DELETE is significant)
184
```
185
186
## Selector Constants
187
188
CSS selectors used by feature handlers for event delegation.
189
190
```javascript { .api }
191
const linkClickSelector = "a[data-confirm], a[data-method], a[data-remote]:not([disabled]), a[data-disable-with], a[data-disable]";
192
const buttonClickSelector = {
193
selector: "button[data-remote], button[data-confirm]:not([form]), button[data-disable-with], button[data-disable]",
194
exclude: "form button"
195
};
196
const inputChangeSelector = "select[data-remote], input[data-remote], textarea[data-remote]";
197
```
198
199
## Data Attribute Integration
200
201
### Confirmation Attributes
202
203
```html
204
<!-- data-confirm: Message to show in confirmation dialog -->
205
<a href="/delete" data-confirm="Are you sure you want to delete this?">Delete</a>
206
207
<!-- Works with any interactive element -->
208
<button data-confirm="Clear all data?" onclick="clearData()">Clear</button>
209
<form data-confirm="Submit form?" action="/submit">...</form>
210
```
211
212
### Method Override Attributes
213
214
```html
215
<!-- data-method: HTTP method to use instead of GET -->
216
<a href="/posts/1" data-method="delete">Delete</a>
217
<a href="/posts/1" data-method="put">Update</a>
218
<a href="/posts/1" data-method="patch">Patch</a>
219
220
<!-- Combines with other attributes -->
221
<a href="/posts/1"
222
data-method="delete"
223
data-confirm="Delete this post?"
224
data-remote="true">Delete</a>
225
```
226
227
### Remote Request Attributes
228
229
```html
230
<!-- data-remote: Make AJAX request instead of page navigation -->
231
<a href="/posts/1" data-remote="true">View</a>
232
<form action="/posts" data-remote="true">...</form>
233
234
<!-- data-type: Expected response content type -->
235
<a href="/data.json" data-remote="true" data-type="json">Get Data</a>
236
237
<!-- data-url: URL for button/input requests -->
238
<button data-remote="true" data-url="/refresh" data-method="post">Refresh</button>
239
240
<!-- data-params: Additional parameters -->
241
<a href="/posts" data-remote="true" data-params="filter=recent">Recent Posts</a>
242
243
<!-- data-with-credentials: Include credentials in cross-origin requests -->
244
<a href="https://api.example.com/data"
245
data-remote="true"
246
data-with-credentials="true">External API</a>
247
```
248
249
## Handler Execution Order
250
251
Rails UJS handlers execute in this order for click events:
252
253
1. **preventInsignificantClick**: Skip processing for insignificant clicks
254
2. **handleDisabledElement**: Stop if element is disabled
255
3. **handleConfirm**: Show confirmation dialog if needed
256
4. **disableElement**: Disable element during processing
257
5. **handleRemote**: Make AJAX request if data-remote
258
6. **handleMethod**: Create method override form if data-method
259
260
## Event Integration
261
262
All handlers integrate with the Rails UJS event system:
263
264
```javascript
265
// Before any handler runs
266
document.addEventListener("ajax:before", function(event) {
267
// Return false to cancel the action
268
});
269
270
// Confirmation events
271
document.addEventListener("confirm", function(event) {
272
// Custom confirmation logic
273
});
274
275
// AJAX events for remote handlers
276
document.addEventListener("ajax:success", function(event) {
277
// Handle successful remote requests
278
});
279
280
// Method override events
281
document.addEventListener("ajax:complete", function(event) {
282
if (event.target.matches("a[data-method]")) {
283
// Method override form was submitted
284
}
285
});
286
```
287
288
## Custom Handler Integration
289
290
You can add custom handlers using the same delegation pattern:
291
292
```javascript
293
// Custom handler for special elements
294
Rails.delegate(document, "[data-custom]", "click", function(event) {
295
const customValue = this.dataset.custom;
296
console.log("Custom handler:", customValue);
297
298
// Follow Rails UJS patterns
299
if (!Rails.fire(this, "custom:before", [customValue])) {
300
return; // Event was cancelled
301
}
302
303
// Your custom logic here
304
processCustomAction(customValue);
305
306
Rails.fire(this, "custom:complete", [customValue]);
307
});
308
```
309
310
## Handler Context
311
312
In all handlers, `this` refers to the element that matched the selector:
313
314
```javascript
315
Rails.delegate(document, "a[data-method]", "click", function(event) {
316
// 'this' is the <a> element with data-method
317
// event.target might be a child element like <span>
318
const method = this.dataset.method;
319
const url = Rails.href(this);
320
});
321
```