Files
rspade_system/app/RSpade/man/jquery.txt
root f6ac36c632 Enhance refactor commands with controller-aware Route() updates and fix code quality violations
Add semantic token highlighting for 'that' variable and comment file references in VS Code extension
Add Phone_Text_Input and Currency_Input components with formatting utilities
Implement client widgets, form standardization, and soft delete functionality
Add modal scroll lock and update documentation
Implement comprehensive modal system with form integration and validation
Fix modal component instantiation using jQuery plugin API
Implement modal system with responsive sizing, queuing, and validation support
Implement form submission with validation, error handling, and loading states
Implement country/state selectors with dynamic data loading and Bootstrap styling
Revert Rsx::Route() highlighting in Blade/PHP files
Target specific PHP scopes for Rsx::Route() highlighting in Blade
Expand injection selector for Rsx::Route() highlighting
Add custom syntax highlighting for Rsx::Route() and Rsx.Route() calls
Update jqhtml packages to v2.2.165
Add bundle path validation for common mistakes (development mode only)
Create Ajax_Select_Input widget and Rsx_Reference_Data controller
Create Country_Select_Input widget with default country support
Initialize Tom Select on Select_Input widgets
Add Tom Select bundle for enhanced select dropdowns
Implement ISO 3166 geographic data system for country/region selection
Implement widget-based form system with disabled state support

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 06:21:56 +00:00

414 lines
13 KiB
Plaintext
Executable File

NAME
jQuery Extensions - RSpade framework extensions and overrides to jQuery
SYNOPSIS
// Standard click handler with automatic preventDefault
$('.btn').click(function(e) {
console.log('Button clicked');
});
// Escape hatch for native behavior (rare)
$('.nav-link').click_allow_default(function(e) {
analytics.track('click');
// Link navigation happens
});
// Other jQuery helpers
if ($('.element').exists()) { ... }
if ($('.element').is_in_viewport()) { ... }
$('.form').checkValidity();
DESCRIPTION
RSpade extends jQuery with utility methods and overrides default behaviors
to provide sensible defaults for modern web applications. The most significant
change is the .click() override that automatically calls e.preventDefault()
to prevent accidental page navigation and form submission.
Unlike vanilla jQuery where developers must remember to call preventDefault
in nearly every click handler, RSpade makes the correct behavior automatic.
This eliminates a common source of bugs where forgotten preventDefault calls
cause unwanted page reloads or navigation.
Philosophy:
- Prevent default is correct 95% of the time
- Escape hatch available for the 5% case
- Fail toward the safe behavior (no navigation)
- Make the common case trivial
CLICK OVERRIDE
.click(handler)
Attach click handler that automatically calls e.preventDefault()
This prevents:
- Link navigation (<a href>)
- Form submission (<button type="submit">)
- Default button actions
Usage:
$('.delete-btn').click(function(e) {
delete_record($(this).data('id'));
// No need for e.preventDefault() - automatic
});
$('a.modal-trigger').click(function(e) {
open_modal();
// Link won't navigate - preventDefault called
});
.click() (no handler)
Trigger click event - native behavior unchanged
Usage:
$('.hidden-button').click(); // Triggers click
.click_allow_default(handler)
Escape hatch for native click behavior without preventDefault
Use this ONLY when you explicitly need default browser behavior.
Examples: analytics tracking before navigation, conditional preventDefault
Usage:
$('.external-link').click_allow_default(function(e) {
analytics.track('external_click', this.href);
// Navigation happens after handler
});
$('button[type=submit]').click_allow_default(function(e) {
if (!validate_form()) {
e.preventDefault(); // Conditionally prevent
}
// Otherwise allows form submission
});
JQUERY HELPERS
Element Existence and State
.exists()
Returns true if jQuery selector matched any elements
if ($('.error-message').exists()) {
console.log('Error message present');
}
.is_visible()
Returns true if element is visible (not display:none or hidden)
if ($('.modal').is_visible()) {
$('.modal').fadeOut();
}
.is_in_dom()
Returns true if element exists in and is attached to the DOM
if ($('.dynamic-element').is_in_dom()) {
// Element is live in the page
}
.is_in_viewport()
Returns true if element is currently visible in the viewport
$('.lazy-image').each(function() {
if ($(this).is_in_viewport()) {
load_image($(this));
}
});
Scrolling
.scroll_up_to(speed = 0)
Scrolls page up to bring element into view if it's above viewport
$('.error-field').scroll_up_to(300);
Element Information
.tagname()
Returns lowercase tag name of element
if ($element.tagname() === 'a') {
// It's a link
}
.is_external()
Returns true if link href is external to current domain
Only works on <a> elements
if ($('a').is_external()) {
$(this).attr('target', '_blank');
}
Component-Aware DOM Traversal
.shallowFind(selector)
Finds child elements matching the selector that don't have another
element of the same class as a parent between them and the component.
Useful for finding direct widget children in nested component trees
without accidentally selecting widgets from nested child components.
Example:
Component_A
└── div
└── Widget (found)
└── span
└── Widget (not found - has Widget parent)
$('.Component_A').shallowFind('.Widget')
// Returns only the first Widget
Use case - Finding form widgets without selecting nested widgets:
this.$.shallowFind('.Form_Field').each(function() {
// Only processes fields directly in this form,
// not fields in nested sub-forms
});
.closest_sibling(selector)
Searches for elements within progressively higher ancestor containers.
Similar to .closest() but searches within ancestors instead of
matching the ancestors themselves. Stops searching at <body> tag.
Useful for component-to-component communication when components need
to find related sibling or cousin components without knowing the
exact DOM structure.
Algorithm:
1. Get current element's parent
2. Search within parent using parent.find(selector)
3. If found, return results
4. If not found, move to parent's parent and repeat
5. Stop when reaching <body> (searches body but not beyond)
6. Return empty jQuery object if nothing found
Example DOM structure:
<body>
<div class="form-section">
<div class="row">
<div class="Country_Select_Input"></div>
</div>
<div class="another-row">
<div class="State_Select_Input"></div>
</div>
</div>
</body>
$('.Country_Select_Input').closest_sibling('.State_Select_Input')
// Finds State_Select_Input by searching up to form-section
Use case - Country selector updating state selector:
on_ready() {
if (this.tom_select) {
this.tom_select.on('change', () => {
const state_input = this.$el.closest_sibling('.State_Select_Input');
if (state_input.exists()) {
const widget = state_input.component();
widget.set_country_code(this.val());
}
});
}
}
Form Validation
.checkValidity()
Returns true if form/field passes HTML5 validation
if ($('form').checkValidity()) {
submit_form();
}
.reportValidity()
Triggers browser's native validation UI, returns validity
if (!$('form').reportValidity()) {
return; // Browser shows validation errors
}
.requestSubmit()
Programmatically submit form (triggers validation)
$('form').requestSubmit();
Focus Selector
:focus pseudo-selector
Select element that currently has focus
if ($('input').is(':focus')) {
// Input is focused
}
$('input:focus').addClass('active');
RSPADE VS VANILLA JQUERY
Click Handlers
Vanilla jQuery:
$('.btn').click(function(e) {
e.preventDefault(); // Must remember this
do_something();
});
RSpade:
$('.btn').click(function(e) {
do_something(); // preventDefault automatic
});
Existence Checks
Vanilla jQuery:
if ($('.element').length > 0) { ... }
RSpade:
if ($('.element').exists()) { ... }
Form Validation
Vanilla jQuery:
if ($('form')[0].checkValidity()) { ... }
RSpade:
if ($('form').checkValidity()) { ... }
WHEN TO USE .click_allow_default()
Valid use cases (rare):
1. Analytics tracking before navigation
$('a.external').click_allow_default(function(e) {
analytics.track('external_link');
// Let navigation happen
});
2. Conditional preventDefault
$('button[type=submit]').click_allow_default(function(e) {
if (!validate_form()) {
e.preventDefault();
}
});
3. Progressive enhancement fallbacks
$('a[href="/fallback"]').click_allow_default(function(e) {
if (ajax_available()) {
e.preventDefault();
load_via_ajax();
}
// Otherwise let href work
});
Invalid use cases (use standard .click() instead):
- Opening modals (don't need navigation)
- Triggering actions (don't need navigation)
- Ajax requests (don't need navigation)
- Any case where you don't want the browser's default behavior
MIGRATION FROM VANILLA JQUERY
When porting jQuery code to RSpade:
1. Remove explicit preventDefault calls in click handlers
Before:
$('.btn').click(function(e) {
e.preventDefault();
do_action();
});
After:
$('.btn').click(function(e) {
do_action();
});
2. Identify legitimate native behavior needs
If you have click handlers that SHOULD allow navigation/submission,
switch to .click_allow_default()
3. Replace existence checks
Before: if ($('.el').length > 0)
After: if ($('.el').exists())
4. Replace form validation
Before: $('form')[0].checkValidity()
After: $('form').checkValidity()
EXAMPLES
Modal Trigger
// Opens modal without navigating
$('a.open-modal').click(function(e) {
const modal_id = $(this).data('modal');
$(`#${modal_id}`).fadeIn();
});
Delete Button
// Deletes record without form submission
$('.delete-btn').click(function(e) {
const id = $(this).data('id');
if (confirm('Delete this record?')) {
delete_record(id);
}
});
External Link with Tracking
// Tracks click then allows navigation
$('a.external').click_allow_default(function(e) {
analytics.track('external_link', {
url: this.href,
text: $(this).text()
});
});
Conditional Form Submit
// Only prevents submit if validation fails
$('form button[type=submit]').click_allow_default(function(e) {
if (!validate_custom_rules()) {
e.preventDefault();
show_errors();
}
});
Lazy Loading on Scroll
$(window).on('scroll', function() {
$('.lazy-image').each(function() {
if ($(this).is_in_viewport() && !$(this).data('loaded')) {
const src = $(this).data('src');
$(this).attr('src', src).data('loaded', true);
}
});
});
TROUBLESHOOTING
Problem: Links not navigating when they should
Solution: Use .click_allow_default() instead of .click()
Problem: Form submitting unexpectedly
Solution: This shouldn't happen - .click() prevents submission by default
If using .click_allow_default(), add explicit e.preventDefault()
Problem: .exists() not working
Solution: Ensure Rsx_Jq_Helpers is loaded - check browser console for errors
Problem: Want to use .on('click') to avoid preventDefault
Solution: Don't do this - it defeats the framework's safety. If you need
native behavior, use .click_allow_default() to make intent explicit
IMPLEMENTATION NOTES
The click override is implemented in:
/app/RSpade/Core/Js/Rsx_Jq_Helpers.js
Native jQuery .click() is preserved as:
$.fn._click_native()
The override applies to all elements, not just links and buttons. This is
intentional - preventDefault is harmless on elements without default actions
and ensures consistency across the codebase.
SEE ALSO
jqhtml.txt - JQHTML component system
controller.txt - Controller click handlers
error_handling.txt - JavaScript error patterns