CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-rails--ujs

Ruby on Rails unobtrusive scripting adapter that enables modern JavaScript behaviors through HTML5 data attributes

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

event-system.mddocs/

Event System

Custom event system and delegation utilities for managing user interactions and library events throughout Rails UJS.

Capabilities

Custom Event Firing

Creates and dispatches custom events with data, providing a way to communicate between Rails UJS and application code.

/**
 * Dispatch custom event on target element
 * @param obj - EventTarget to dispatch event on
 * @param name - Event name to dispatch
 * @param data - Optional data to include in event.detail
 * @returns True if event was not cancelled (defaultPrevented is false)
 */
function fire(obj: EventTarget, name: string, data?: any): boolean;

Usage Examples:

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

// Fire simple event
const shouldContinue = Rails.fire(form, "custom:before-submit");
if (!shouldContinue) {
  console.log("Event was cancelled");
  return;
}

// Fire event with data
Rails.fire(document, "rails:navigation", {
  url: "/posts/1",
  method: "GET"
});

// Listen for Rails UJS events
document.addEventListener("ajax:success", function(event) {
  console.log("AJAX success:", event.detail);
});

// Cancel events by preventing default
document.addEventListener("confirm", function(event) {
  if (!userIsAdmin) {
    event.preventDefault(); // Cancels the action
  }
});

Event Delegation

Efficient event delegation system that handles events for dynamically created elements.

/**
 * Set up delegated event listener on container element
 * @param element - Container element to listen on (usually document)
 * @param selector - CSS selector for target elements
 * @param eventType - Event type to listen for (click, submit, etc.)
 * @param handler - Handler function to call when event matches
 */
function delegate(element: Element, selector: string, eventType: string, handler: Function): void;

Usage Examples:

// Delegate click events for all remote links
Rails.delegate(document, "a[data-remote]", "click", function(event) {
  console.log("Remote link clicked:", this.href);
  // 'this' refers to the clicked link
});

// Delegate form submissions
Rails.delegate(document, "form[data-remote]", "submit", function(event) {
  console.log("Remote form submitted:", this.action);
});

// Custom delegation for application logic
Rails.delegate(document, ".toggle-button", "click", function(event) {
  this.classList.toggle("active");
});

// Delegation works with complex selectors
Rails.delegate(document, "button[data-confirm]:not([disabled])", "click", confirmHandler);

Event Stopping

Comprehensive event stopping utility that prevents all forms of event propagation.

/**
 * Stop event propagation, prevent default, and fire stopping event
 * @param event - Event to stop completely
 */
function stopEverything(event: Event): void;

Usage Examples:

// Completely stop an event
document.addEventListener("click", function(event) {
  if (event.target.disabled) {
    Rails.stopEverything(event);
    return;
  }
});

// Used internally by Rails UJS
function handleDisabledElement(event) {
  const element = this;
  if (element.disabled) {
    Rails.stopEverything(event); // Prevents any further processing
  }
}

// Fires 'ujs:everythingStopped' event for debugging
document.addEventListener("ujs:everythingStopped", function(event) {
  console.log("Event was completely stopped on:", event.target);
});

Rails UJS Events

Rails UJS fires numerous custom events during its operation:

Confirmation Events

// Before showing confirmation dialog
document.addEventListener("confirm", function(event) {
  // event.target is the element being confirmed
  // Return false or preventDefault() to skip confirmation
});

// After confirmation dialog is handled
document.addEventListener("confirm:complete", function(event) {
  const [answer] = event.detail; // true if user confirmed, false if cancelled
  console.log("User response:", answer);
});

// Custom confirmation handling
document.addEventListener("confirm", function(event) {
  event.preventDefault();
  customConfirmDialog(event.target.dataset.confirm)
    .then(confirmed => {
      if (confirmed) {
        Rails.fire(event.target, "confirm:complete", [true]);
        // Continue with original action
      }
    });
});

AJAX Events

// Before AJAX request starts
document.addEventListener("ajax:before", function(event) {
  console.log("About to make AJAX request");
  // Return false to cancel request
});

// Before XHR is sent (after beforeSend callback)
document.addEventListener("ajax:beforeSend", function(event) {
  const [xhr, options] = event.detail;
  console.log("Sending request to:", options.url);
});

// When XHR is actually sent
document.addEventListener("ajax:send", function(event) {
  const [xhr] = event.detail;
  console.log("Request sent, readyState:", xhr.readyState);
});

// When AJAX request is stopped/cancelled
document.addEventListener("ajax:stopped", function(event) {
  console.log("AJAX request was stopped");
});

// On successful AJAX response (2xx status)
document.addEventListener("ajax:success", function(event) {
  const [data, statusText, xhr] = event.detail;
  console.log("AJAX success:", data);
});

// On AJAX error response (non-2xx status)
document.addEventListener("ajax:error", function(event) {
  const [response, statusText, xhr] = event.detail;
  console.error("AJAX error:", xhr.status, statusText);
});

// When AJAX request completes (success or error)
document.addEventListener("ajax:complete", function(event) {
  const [xhr, statusText] = event.detail;
  console.log("AJAX complete:", xhr.status);
});

Initialization Events

// Check if Rails UJS should attach bindings
document.addEventListener("rails:attachBindings", function(event) {
  // Return false to prevent Rails UJS from starting
  console.log("Rails UJS is initializing");
});

Event Delegation Implementation

Rails UJS uses a sophisticated delegation system:

// Example of how Rails UJS sets up delegation internally
Rails.delegate(document, "a[data-method]", "click", Rails.handleMethod);
Rails.delegate(document, "form[data-remote]", "submit", Rails.handleRemote);
Rails.delegate(document, "[data-confirm]", "click", Rails.handleConfirm);

// The delegation system:
// 1. Listens on document for efficiency
// 2. Checks event.target against selector
// 3. Bubbles up DOM tree to find matches
// 4. Calls handler with matched element as 'this'

CustomEvent Polyfill

Rails UJS includes a CustomEvent polyfill for older browsers:

// Creates CustomEvent constructor if not available
// Ensures consistent behavior across all browsers
// Handles preventDefault() properly on polyfilled events

Performance Benefits

  • Single listener per event type: Uses delegation instead of individual listeners
  • Dynamic content support: Automatically handles elements added after page load
  • Memory efficient: No need to add/remove listeners when elements change
  • Event bubbling: Leverages native browser event bubbling for efficiency

Event Handler Context

In delegated event handlers, this refers to the matched element:

Rails.delegate(document, "a[data-remote]", "click", function(event) {
  // 'this' is the clicked <a> element that matches "a[data-remote]"
  // event.target might be a child element (like <span> inside the <a>)
  console.log("Clicked link:", this.href);
  console.log("Actual target:", event.target.tagName);
});

Install with Tessl CLI

npx tessl i tessl/npm-rails--ujs

docs

ajax-remote.md

csrf-protection.md

dom-utilities.md

element-state.md

event-system.md

feature-handlers.md

form-handling.md

index.md

tile.json