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

dom-utilities.mddocs/

0

# DOM Utilities

1

2

Core DOM manipulation and querying utilities used throughout Rails UJS for element selection, data storage, and cross-browser compatibility.

3

4

## Capabilities

5

6

### Element Selection

7

8

Simple wrapper around `document.querySelectorAll` that returns a proper Array.

9

10

```javascript { .api }

11

/**

12

* Query selector wrapper that returns an Array

13

* @param selector - CSS selector string

14

* @returns Array of matching elements

15

*/

16

function $(selector: string): Element[];

17

```

18

19

**Usage Examples:**

20

21

```javascript

22

// Select all forms with data-remote attribute

23

const remoteForms = Rails.$("form[data-remote]");

24

remoteForms.forEach(form => {

25

console.log("Remote form:", form.action);

26

});

27

28

// Select disabled buttons

29

const disabledButtons = Rails.$("button:disabled");

30

31

// Works with complex selectors

32

const complexElements = Rails.$("form:not([data-turbo=true]) input[type=submit]");

33

```

34

35

### Element Matching

36

37

Cross-browser element matching utility that handles complex selector objects.

38

39

```javascript { .api }

40

/**

41

* Check if element matches selector (cross-browser)

42

* @param element - DOM element to check

43

* @param selector - CSS selector string or selector object with exclude

44

* @returns True if element matches selector

45

*/

46

function matches(element: Element, selector: string | SelectorObject): boolean;

47

48

interface SelectorObject {

49

/** Main CSS selector to match */

50

selector: string;

51

/** CSS selector for elements to exclude from match */

52

exclude: string;

53

}

54

```

55

56

**Usage Examples:**

57

58

```javascript

59

const button = document.querySelector("button");

60

61

// Simple selector matching

62

if (Rails.matches(button, "button[type=submit]")) {

63

console.log("This is a submit button");

64

}

65

66

// Complex selector with exclusions

67

const selectorObj = {

68

selector: "button[data-remote]",

69

exclude: "form button"

70

};

71

72

if (Rails.matches(button, selectorObj)) {

73

console.log("Remote button that's not inside a form");

74

}

75

76

// Used internally for event delegation

77

document.addEventListener("click", function(event) {

78

if (Rails.matches(event.target, "a[data-method]")) {

79

// Handle method links

80

}

81

});

82

```

83

84

### Element Data Storage

85

86

Utilities for storing and retrieving data on DOM elements using expando properties.

87

88

```javascript { .api }

89

/**

90

* Get data from element's expando properties

91

* @param element - DOM element to get data from

92

* @param key - Data key to retrieve

93

* @returns Stored value or undefined if not found

94

*/

95

function getData(element: Element, key: string): any;

96

97

/**

98

* Set data on element's expando properties

99

* @param element - DOM element to store data on

100

* @param key - Data key to store under

101

* @param value - Value to store

102

* @returns The stored value

103

*/

104

function setData(element: Element, key: string, value: any): any;

105

106

/**

107

* Check if element or any parent is content editable

108

* @param element - DOM element to check

109

* @returns True if element is content editable

110

*/

111

function isContentEditable(element: Element): boolean;

112

```

113

114

**Usage Examples:**

115

116

```javascript

117

const form = document.querySelector("form");

118

119

// Store data on element

120

Rails.setData(form, "ujs:submit-button", {

121

name: "commit",

122

value: "Create"

123

});

124

125

// Retrieve data from element

126

const buttonData = Rails.getData(form, "ujs:submit-button");

127

console.log("Submit button:", buttonData);

128

129

// Check if data exists

130

if (Rails.getData(element, "ujs:disabled")) {

131

console.log("Element is disabled by UJS");

132

}

133

134

// Common UJS data keys:

135

// "ujs:disabled" - Element is disabled

136

// "ujs:enable-with" - Original element content

137

// "ujs:submit-button" - Form submit button info

138

// "ujs:submit-button-formmethod" - Submit button method override

139

// "ujs:submit-button-formaction" - Submit button action override

140

141

// Check if element is content editable

142

const editor = document.querySelector("#rich-editor");

143

if (Rails.isContentEditable(editor)) {

144

console.log("Element is content editable");

145

// Skip certain UJS behaviors for content editable elements

146

}

147

148

// Used internally by Rails UJS to avoid disabling rich text editors

149

const textInput = document.querySelector("textarea");

150

if (!Rails.isContentEditable(textInput)) {

151

Rails.disableElement(textInput); // Safe to disable

152

}

153

```

154

155

## Cross-Browser Compatibility

156

157

The DOM utilities handle cross-browser differences:

158

159

### Element Matching

160

161

```javascript

162

// Handles different browser implementations:

163

// Element.prototype.matches

164

// Element.prototype.matchesSelector

165

// Element.prototype.mozMatchesSelector

166

// Element.prototype.msMatchesSelector

167

// Element.prototype.oMatchesSelector

168

// Element.prototype.webkitMatchesSelector

169

```

170

171

### Query Selection

172

173

```javascript

174

// Converts NodeList to Array for consistent behavior

175

// Array methods work reliably across browsers

176

const elements = Rails.$("input[type=submit]");

177

elements.forEach(el => console.log(el)); // Always works

178

```

179

180

## Data Structure

181

182

Element data is stored using expando properties with a consistent structure:

183

184

```javascript

185

// Internal data structure on elements

186

element._ujsData = {

187

"ujs:disabled": true,

188

"ujs:enable-with": "Original Text",

189

"ujs:submit-button": { name: "commit", value: "Create" }

190

};

191

```

192

193

### Internal Storage Constants

194

195

Rails UJS uses a consistent naming scheme for internal data storage:

196

197

```javascript { .api }

198

// Internal expando property name used for data storage

199

const EXPANDO = "_ujsData";

200

201

// Common data keys used throughout Rails UJS

202

const DATA_KEYS = {

203

DISABLED: "ujs:disabled",

204

ENABLE_WITH: "ujs:enable-with",

205

SUBMIT_BUTTON: "ujs:submit-button",

206

SUBMIT_BUTTON_FORMMETHOD: "ujs:submit-button-formmethod",

207

SUBMIT_BUTTON_FORMACTION: "ujs:submit-button-formaction",

208

FORMNOVALIDATE_BUTTON: "ujs:formnovalidate-button"

209

};

210

```

211

212

## Selector Examples

213

214

Common selectors used throughout Rails UJS:

215

216

```javascript

217

// Form-related selectors

218

"form:not([data-turbo=true])" // Forms not using Turbo

219

"form input[type=submit]" // Submit buttons in forms

220

"input[data-disable-with]:enabled" // Enabled elements with disable attribute

221

222

// Link selectors

223

"a[data-confirm]" // Links with confirmation

224

"a[data-method]" // Links with method override

225

"a[data-remote]:not([disabled])" // Remote links that aren't disabled

226

227

// Complex selectors with exclusions

228

{

229

selector: "button[data-remote]:not([form])", // Remote buttons

230

exclude: "form button" // But not buttons inside forms

231

}

232

```

233

234

## Performance Considerations

235

236

- Uses native `querySelectorAll` for optimal performance

237

- Caches cross-browser matching function on first use

238

- Expando properties avoid memory leaks compared to WeakMap polyfills

239

- Selector matching is optimized for Rails UJS's specific use cases