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