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

feature-handlers.mddocs/

Feature Handlers

High-level handlers that implement the core Rails UJS behaviors for method overrides, remote requests, confirmations, and element disabling through HTML data attributes.

Capabilities

Confirmation Handler

Handles confirmation dialogs for elements with data-confirm attributes.

/**
 * Handle confirmation dialogs for elements with data-confirm
 * @param event - Event that triggered the confirmation (usually click)
 */
function handleConfirm(event: Event): void;

/**
 * Show confirmation dialog (overridable)
 * @param message - Confirmation message to display
 * @param element - Element that triggered the confirmation
 * @returns True if user confirmed, false if cancelled
 */
function confirm(message: string, element: Element): boolean;

Usage Examples:

<!-- Basic confirmation -->
<a href="/posts/1" data-method="delete" data-confirm="Are you sure?">Delete</a>

<!-- Form confirmation -->
<form action="/posts" method="post" data-confirm="Submit this post?">
  <button type="submit">Create</button>
</form>

<!-- Button confirmation -->
<button data-remote="true" data-url="/refresh" data-confirm="Refresh data?">Refresh</button>
// Custom confirmation implementation
Rails.confirm = function(message, element) {
  return customConfirmDialog(message, {
    title: "Please Confirm",
    element: element
  });
};

// Listen for confirmation events
document.addEventListener("confirm", function(event) {
  console.log("Confirming action on:", event.target);
  // event.preventDefault() to skip confirmation
});

document.addEventListener("confirm:complete", function(event) {
  const [confirmed] = event.detail;
  console.log("User decision:", confirmed);
});

Method Override Handler

Handles HTTP method override for links with data-method attributes.

/**
 * Handle method override for links with data-method attribute
 * @param event - Click event from link with data-method
 */
function handleMethod(event: Event): void;

Usage Examples:

<!-- DELETE request via link -->
<a href="/posts/1" data-method="delete">Delete Post</a>

<!-- PUT request with confirmation -->
<a href="/posts/1/publish" data-method="put" data-confirm="Publish this post?">Publish</a>

<!-- PATCH request -->
<a href="/users/1" data-method="patch">Update User</a>
// Rails UJS automatically:
// 1. Prevents normal link navigation
// 2. Creates hidden form with correct method
// 3. Includes CSRF token for same-origin requests
// 4. Submits form to trigger server request

// Generated form structure:
// <form method="post" action="/posts/1" style="display: none">
//   <input name="_method" value="delete" type="hidden" />
//   <input name="authenticity_token" value="..." type="hidden" />
//   <input type="submit" />
// </form>

Remote Request Handler

Handles AJAX requests for elements with data-remote attributes.

/**
 * Handle remote AJAX requests for elements with data-remote
 * @param event - Event that triggered the remote request
 */
function handleRemote(event: Event): void;

Usage Examples:

<!-- Remote link -->
<a href="/posts/1" data-remote="true">View Post</a>

<!-- Remote form -->
<form action="/posts" method="post" data-remote="true">
  <input type="text" name="title">
  <button type="submit">Create</button>
</form>

<!-- Remote button -->
<button data-remote="true" data-url="/refresh" data-method="post">Refresh</button>

<!-- Remote with custom data type -->
<a href="/posts.json" data-remote="true" data-type="json">Get JSON</a>
// Listen for remote request events
document.addEventListener("ajax:beforeSend", function(event) {
  const [xhr, options] = event.detail;
  console.log("Making request to:", options.url);
});

document.addEventListener("ajax:success", function(event) {
  const [data] = event.detail;
  console.log("Remote request succeeded:", data);
});

// Custom handling based on element type
document.addEventListener("ajax:success", function(event) {
  if (event.target.matches("form")) {
    // Handle form success
    showNotification("Form submitted successfully");
  } else if (event.target.matches("a")) {
    // Handle link success  
    updatePageContent(event.detail[0]);
  }
});

Click Prevention Handler

Prevents insignificant clicks on links (like meta+click on GET links without data).

/**
 * Prevent insignificant clicks from triggering Rails UJS behaviors
 * @param event - Click event to potentially prevent
 */
function preventInsignificantClick(event: Event): void;

Usage Examples:

// Automatically prevents Rails UJS processing for:
// - Meta+click or Ctrl+click on GET links without data-params
// - Right-click or middle-click on any link
// - This allows normal browser behavior (new tab, context menu, etc.)

// Example scenarios:
// <a href="/posts" data-method="get">Posts</a>
// - Normal click: processed by Rails UJS
// - Meta+click: opens in new tab (Rails UJS skipped)

// <a href="/posts/1" data-method="delete">Delete</a>  
// - Meta+click: still processed by Rails UJS (DELETE is significant)

Selector Constants

CSS selectors used by feature handlers for event delegation.

const linkClickSelector = "a[data-confirm], a[data-method], a[data-remote]:not([disabled]), a[data-disable-with], a[data-disable]";
const buttonClickSelector = {
  selector: "button[data-remote], button[data-confirm]:not([form]), button[data-disable-with], button[data-disable]",
  exclude: "form button"
};
const inputChangeSelector = "select[data-remote], input[data-remote], textarea[data-remote]";

Data Attribute Integration

Confirmation Attributes

<!-- data-confirm: Message to show in confirmation dialog -->
<a href="/delete" data-confirm="Are you sure you want to delete this?">Delete</a>

<!-- Works with any interactive element -->
<button data-confirm="Clear all data?" onclick="clearData()">Clear</button>
<form data-confirm="Submit form?" action="/submit">...</form>

Method Override Attributes

<!-- data-method: HTTP method to use instead of GET -->
<a href="/posts/1" data-method="delete">Delete</a>
<a href="/posts/1" data-method="put">Update</a>
<a href="/posts/1" data-method="patch">Patch</a>

<!-- Combines with other attributes -->
<a href="/posts/1" 
   data-method="delete" 
   data-confirm="Delete this post?"
   data-remote="true">Delete</a>

Remote Request Attributes

<!-- data-remote: Make AJAX request instead of page navigation -->
<a href="/posts/1" data-remote="true">View</a>
<form action="/posts" data-remote="true">...</form>

<!-- data-type: Expected response content type -->
<a href="/data.json" data-remote="true" data-type="json">Get Data</a>

<!-- data-url: URL for button/input requests -->
<button data-remote="true" data-url="/refresh" data-method="post">Refresh</button>

<!-- data-params: Additional parameters -->
<a href="/posts" data-remote="true" data-params="filter=recent">Recent Posts</a>

<!-- data-with-credentials: Include credentials in cross-origin requests -->
<a href="https://api.example.com/data" 
   data-remote="true" 
   data-with-credentials="true">External API</a>

Handler Execution Order

Rails UJS handlers execute in this order for click events:

  1. preventInsignificantClick: Skip processing for insignificant clicks
  2. handleDisabledElement: Stop if element is disabled
  3. handleConfirm: Show confirmation dialog if needed
  4. disableElement: Disable element during processing
  5. handleRemote: Make AJAX request if data-remote
  6. handleMethod: Create method override form if data-method

Event Integration

All handlers integrate with the Rails UJS event system:

// Before any handler runs
document.addEventListener("ajax:before", function(event) {
  // Return false to cancel the action
});

// Confirmation events
document.addEventListener("confirm", function(event) {
  // Custom confirmation logic
});

// AJAX events for remote handlers
document.addEventListener("ajax:success", function(event) {
  // Handle successful remote requests
});

// Method override events
document.addEventListener("ajax:complete", function(event) {
  if (event.target.matches("a[data-method]")) {
    // Method override form was submitted
  }
});

Custom Handler Integration

You can add custom handlers using the same delegation pattern:

// Custom handler for special elements
Rails.delegate(document, "[data-custom]", "click", function(event) {
  const customValue = this.dataset.custom;
  console.log("Custom handler:", customValue);
  
  // Follow Rails UJS patterns
  if (!Rails.fire(this, "custom:before", [customValue])) {
    return; // Event was cancelled
  }
  
  // Your custom logic here
  processCustomAction(customValue);
  
  Rails.fire(this, "custom:complete", [customValue]);
});

Handler Context

In all handlers, this refers to the element that matched the selector:

Rails.delegate(document, "a[data-method]", "click", function(event) {
  // 'this' is the <a> element with data-method
  // event.target might be a child element like <span>
  const method = this.dataset.method;
  const url = Rails.href(this);
});

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