Ruby on Rails unobtrusive scripting adapter that enables modern JavaScript behaviors through HTML5 data attributes
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Core DOM manipulation and querying utilities used throughout Rails UJS for element selection, data storage, and cross-browser compatibility.
Simple wrapper around document.querySelectorAll that returns a proper Array.
/**
* Query selector wrapper that returns an Array
* @param selector - CSS selector string
* @returns Array of matching elements
*/
function $(selector: string): Element[];Usage Examples:
// Select all forms with data-remote attribute
const remoteForms = Rails.$("form[data-remote]");
remoteForms.forEach(form => {
console.log("Remote form:", form.action);
});
// Select disabled buttons
const disabledButtons = Rails.$("button:disabled");
// Works with complex selectors
const complexElements = Rails.$("form:not([data-turbo=true]) input[type=submit]");Cross-browser element matching utility that handles complex selector objects.
/**
* Check if element matches selector (cross-browser)
* @param element - DOM element to check
* @param selector - CSS selector string or selector object with exclude
* @returns True if element matches selector
*/
function matches(element: Element, selector: string | SelectorObject): boolean;
interface SelectorObject {
/** Main CSS selector to match */
selector: string;
/** CSS selector for elements to exclude from match */
exclude: string;
}Usage Examples:
const button = document.querySelector("button");
// Simple selector matching
if (Rails.matches(button, "button[type=submit]")) {
console.log("This is a submit button");
}
// Complex selector with exclusions
const selectorObj = {
selector: "button[data-remote]",
exclude: "form button"
};
if (Rails.matches(button, selectorObj)) {
console.log("Remote button that's not inside a form");
}
// Used internally for event delegation
document.addEventListener("click", function(event) {
if (Rails.matches(event.target, "a[data-method]")) {
// Handle method links
}
});Utilities for storing and retrieving data on DOM elements using expando properties.
/**
* Get data from element's expando properties
* @param element - DOM element to get data from
* @param key - Data key to retrieve
* @returns Stored value or undefined if not found
*/
function getData(element: Element, key: string): any;
/**
* Set data on element's expando properties
* @param element - DOM element to store data on
* @param key - Data key to store under
* @param value - Value to store
* @returns The stored value
*/
function setData(element: Element, key: string, value: any): any;
/**
* Check if element or any parent is content editable
* @param element - DOM element to check
* @returns True if element is content editable
*/
function isContentEditable(element: Element): boolean;Usage Examples:
const form = document.querySelector("form");
// Store data on element
Rails.setData(form, "ujs:submit-button", {
name: "commit",
value: "Create"
});
// Retrieve data from element
const buttonData = Rails.getData(form, "ujs:submit-button");
console.log("Submit button:", buttonData);
// Check if data exists
if (Rails.getData(element, "ujs:disabled")) {
console.log("Element is disabled by UJS");
}
// Common UJS data keys:
// "ujs:disabled" - Element is disabled
// "ujs:enable-with" - Original element content
// "ujs:submit-button" - Form submit button info
// "ujs:submit-button-formmethod" - Submit button method override
// "ujs:submit-button-formaction" - Submit button action override
// Check if element is content editable
const editor = document.querySelector("#rich-editor");
if (Rails.isContentEditable(editor)) {
console.log("Element is content editable");
// Skip certain UJS behaviors for content editable elements
}
// Used internally by Rails UJS to avoid disabling rich text editors
const textInput = document.querySelector("textarea");
if (!Rails.isContentEditable(textInput)) {
Rails.disableElement(textInput); // Safe to disable
}The DOM utilities handle cross-browser differences:
// Handles different browser implementations:
// Element.prototype.matches
// Element.prototype.matchesSelector
// Element.prototype.mozMatchesSelector
// Element.prototype.msMatchesSelector
// Element.prototype.oMatchesSelector
// Element.prototype.webkitMatchesSelector// Converts NodeList to Array for consistent behavior
// Array methods work reliably across browsers
const elements = Rails.$("input[type=submit]");
elements.forEach(el => console.log(el)); // Always worksElement data is stored using expando properties with a consistent structure:
// Internal data structure on elements
element._ujsData = {
"ujs:disabled": true,
"ujs:enable-with": "Original Text",
"ujs:submit-button": { name: "commit", value: "Create" }
};Rails UJS uses a consistent naming scheme for internal data storage:
// Internal expando property name used for data storage
const EXPANDO = "_ujsData";
// Common data keys used throughout Rails UJS
const DATA_KEYS = {
DISABLED: "ujs:disabled",
ENABLE_WITH: "ujs:enable-with",
SUBMIT_BUTTON: "ujs:submit-button",
SUBMIT_BUTTON_FORMMETHOD: "ujs:submit-button-formmethod",
SUBMIT_BUTTON_FORMACTION: "ujs:submit-button-formaction",
FORMNOVALIDATE_BUTTON: "ujs:formnovalidate-button"
};Common selectors used throughout Rails UJS:
// Form-related selectors
"form:not([data-turbo=true])" // Forms not using Turbo
"form input[type=submit]" // Submit buttons in forms
"input[data-disable-with]:enabled" // Enabled elements with disable attribute
// Link selectors
"a[data-confirm]" // Links with confirmation
"a[data-method]" // Links with method override
"a[data-remote]:not([disabled])" // Remote links that aren't disabled
// Complex selectors with exclusions
{
selector: "button[data-remote]:not([form])", // Remote buttons
exclude: "form button" // But not buttons inside forms
}querySelectorAll for optimal performanceInstall with Tessl CLI
npx tessl i tessl/npm-rails--ujs