or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

ajax-remote.mdcsrf-protection.mddom-utilities.mdelement-state.mdevent-system.mdfeature-handlers.mdform-handling.mdindex.md

feature-handlers.mddocs/

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

```