Skip to content

Commit 8e46264

Browse files
committed
Remove jQuery from the JavaScript runtime
Drop the remaining jQuery dependency from the published JavaScript runtime and move the validation flow to native DOM APIs. ClientSideValidations.enable(), validate(), isValid(), disable(), and reset() now work with DOM elements and DOM collections instead of jQuery objects. Validation callbacks and local validators also receive native nodes, which keeps custom integrations aligned with the new runtime surface. Replace jQuery event wiring with native CustomEvent dispatch and addEventListener hooks. Update visibility checks, dataset state, uniqueness handling, and helper utilities so the runtime can traverse forms and inputs without jQuery while preserving the existing validation behavior. Refresh the QUnit suite to build fixtures with plain DOM methods and assert against native events and callback arguments. Regenerate the bundled assets, switch the test harness to a neutral QUnit CDN, and remove jquery from the package metadata. Document the 24.0.0 breaking changes in the README and changelog, and bump the gem and npm package versions for the release.
1 parent b80c4ee commit 8e46264

45 files changed

Lines changed: 2599 additions & 1885 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/ISSUE_TEMPLATE/bug_report.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ _Tell us what happens instead_
3232
- [ ] I confirm that my browser's development console output does not contain errors
3333

3434
### Additional JavaScript Libraries*
35-
_If your issue depends on other JavaScript libraries, please list them here. E.g: *Bootstrap Modal v3.3.7, jQuery UI Datepicker 1.12.4*._
35+
_If your issue depends on other JavaScript libraries, please list them here. E.g: *Bootstrap Modal v3.3.7, Stimulus 3.2.2*._
3636

3737
### Repository demostrating the issue
3838
Debugging CSV issues is a time consuming task. If you want to speed up things, please

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## 24.0.0 / 2026-04-19
4+
* [FEATURE] Breaking change: Remove the jQuery runtime dependency and the old jQuery plugin aliases from the published JavaScript assets
5+
* [FEATURE] Breaking change: Public JavaScript APIs now work with native DOM elements and DOM collections instead of jQuery-wrapped objects
6+
* [ENHANCEMENT] Use native browser events and event listeners throughout the runtime and test harness
7+
38
## 23.1.0 / 2026-01-27
49

510
* [FEATURE] Add jQuery 4.0.0 compatibility

README.md

Lines changed: 103 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,11 @@ config/initializers/client_side_validations.rb
5656

5757
Instructions depend on your technology stack.
5858

59-
Please note that CSV depends on jQuery >= 3.7.1 (jQuery slim is fine).
59+
ClientSideValidations no longer depends on jQuery.
60+
If you previously installed `jquery-rails`, `jquery_ujs`, or custom jQuery startup code only for ClientSideValidations, you can remove that integration when upgrading to 24.x.
6061

6162
#### When using Webpacker ####
6263

63-
Make sure that you are requiring jQuery.
64-
6564
Add the following package:
6665

6766
```sh
@@ -92,42 +91,77 @@ detect `window.Turbolinks` and attach its event handlers.
9291

9392
#### When using Sprockets ####
9493

95-
Since ClientSideValidations can also be used via webpacker, it does not require
96-
by default `jquery-rails` gem.
94+
Add the following to your `app/assets/javascripts/application.js` file:
9795

98-
Make sure that `jquery-rails` is part of your bundled gems and `application.js`,
99-
otherwise add:
96+
```js
97+
//= require rails.validations
98+
```
10099

101-
```ruby
102-
gem 'jquery-rails'
100+
If you are using [Turbolinks](https://github.com/turbolinks/turbolinks),
101+
make sure that `rails.validations` is required **after** `turbolinks`, so
102+
ClientSideValidations can properly attach its event handlers.
103+
104+
If you need to copy the asset files from the gem into your project, run:
105+
106+
```
107+
rails g client_side_validations:copy_assets
103108
```
104109

105-
to your `Gemfile`, run `bundle`, and add
110+
Note: If you run `copy_assets`, you will need to run it again each time you update this project.
111+
112+
## Migration Guide ##
113+
114+
### 24.x Breaking Changes ###
115+
116+
If you are upgrading to 24.x, update your integration code to use the `ClientSideValidations` object directly.
117+
118+
The old jQuery plugin methods are removed. Use the DOM-first public API instead:
106119

107120
```js
108-
//= require jquery
121+
ClientSideValidations.enable(form)
122+
ClientSideValidations.validate(form)
123+
ClientSideValidations.isValid(form, validators)
124+
ClientSideValidations.disable(form)
125+
ClientSideValidations.reset(form)
109126
```
110127

111-
to your `app/assets/javascripts/application.js` file.
128+
These methods accept native DOM elements and DOM collections. They do not accept jQuery objects or CSS selector strings.
112129

113-
Then, add the following to your `app/assets/javascripts/application.js` file
114-
after `//= require jquery`.
130+
Custom validators, form builders, and callbacks now receive native DOM nodes instead of jQuery wrappers. Update any custom code to use DOM APIs such as `.value`, `.form`, `.closest()`, and `querySelector()`.
131+
Local validators are called as `(element, options)`. Form callbacks receive `(form, eventData)`, and element callbacks receive either `(element, message, callback)` or `(element, callback)` depending on the event.
115132

116-
```js
117-
//= require rails.validations
133+
All runtime-owned validation state attributes are now namespaced under `csv`. If you read or write these attributes in custom selectors, callbacks, or validators, update them to the scoped names:
134+
135+
```text
136+
data-changed => data-csv-changed
137+
data-valid => data-csv-valid
138+
data-validate => data-csv-validate
139+
data-not-locally-unique => data-csv-not-locally-unique
118140
```
119141

120-
If you are using [Turbolinks](https://github.com/turbolinks/turbolinks),
121-
make sure that `rails.validations` is required **after** `turbolinks`, so
122-
ClientSideValidations can properly attach its event handlers.
142+
The matching dataset properties are `element.dataset.csvChanged`, `element.dataset.csvValid`, `element.dataset.csvValidate`, and `element.dataset.csvNotLocallyUnique`. `csvChanged` is stored as the string values `'true'` and `'false'`.
123143

124-
If you need to copy the asset files from the gem into your project, run:
144+
**jQuery namespaced events are removed.** Events are now plain native DOM custom events. If your application listens to or unbinds events using jQuery-style namespacing, you must update those calls.
145+
146+
Before:
125147

148+
```js
149+
$(form).on('form:validate:before.ClientSideValidations', handler)
150+
$(input).off('.ClientSideValidations')
126151
```
127-
rails g client_side_validations:copy_assets
152+
153+
After:
154+
155+
```js
156+
form.addEventListener('form:validate:before', handler)
157+
// store and pass the handler reference to removeEventListener when unbinding
128158
```
129159

130-
Note: If you run `copy_assets`, you will need to run it again each time you update this project.
160+
The full list of native events dispatched by ClientSideValidations: `form:validate:before`, `form:validate:after`, `form:validate:pass`, `form:validate:fail`, `element:validate:before`, `element:validate:after`, `element:validate:pass`, `element:validate:fail`.
161+
162+
If you are upgrading from a version older than 23.0.0, the `data-csv-*` renaming above is required for any custom code that still reads or writes the old attribute names. If you are already on 23.x, the new 24.x upgrade step is to update any local uniqueness integrations that still reference `data-not-locally-unique` or `element.dataset.notLocallyUnique`.
163+
164+
If your application vendors the compiled asset with `rails g client_side_validations:copy_assets`, run that generator again after upgrading so your copied asset matches the current jQuery-free bundle.
131165

132166
## Initializer ##
133167

@@ -324,11 +358,11 @@ If you need to change the markup of how the errors are rendered you can modify t
324358

325359
```js
326360
window.ClientSideValidations.formBuilders['ActionView::Helpers::FormBuilder'] = {
327-
add: function($element, settings, message) {
361+
add: function(element, settings, message) {
328362
// custom add code here
329363
},
330364

331-
remove: function($element, settings) {
365+
remove: function(element, settings) {
332366
// custom remove code here
333367
}
334368
}
@@ -376,14 +410,14 @@ en:
376410
Finally we need to add a client side validator. This can be done by hooking into the `ClientSideValidations.validator` object. Create a new file `app/assets/javascripts/rails.validations.customValidators.js`
377411

378412
```js
379-
// The validator variable is a JSON Object
380-
// The selector variable is a jQuery Object
381-
window.ClientSideValidations.validators.local['email'] = function($element, options) {
413+
// The options variable is a JSON Object
414+
// The element variable is a DOM element
415+
window.ClientSideValidations.validators.local.email = function (element, options) {
382416
// Your validator code goes in here
383-
if (!/^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i.test($element.val())) {
417+
if (!/^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i.test(element.value)) {
384418
// When the value fails to pass validation you need to return the error message.
385419
// It can be derived from validator.message
386-
return options.message;
420+
return options.message
387421
}
388422
}
389423
```
@@ -408,68 +442,78 @@ There are many reasons why you might want to enable, disable, or even completely
408442
If you have rendered a new form via AJAX into your page you will need to enable that form for validation:
409443

410444
```js
411-
$(new_form).enableClientSideValidations();
445+
ClientSideValidations.enable(newForm)
412446
```
413447

414448
You should attach this to an event that is fired when the new HTML renders.
415449

416450
You can use the same function if you introduce new inputs to an existing form:
417451

418452
```js
419-
$(new_input).enableClientSideValidations();
453+
ClientSideValidations.enable(newInput)
420454
```
421455

422456
### Disabling ###
423457

424458
If you wish to turn off validations entirely on a form:
425459

426460
```js
427-
$(form).disableClientSideValidations();
461+
ClientSideValidations.disable(form)
428462
```
429463

430464
### Resetting ###
431465

432466
You can reset the current state of the validations, clear all error messages, and reattach clean event handlers:
433467

434468
```js
435-
$(form).resetClientSideValidations();
469+
ClientSideValidations.reset(form)
436470
```
437471

438472
## Callbacks ##
439473

440474
`ClientSideValidations` will run callbacks based upon the state of the element or form. The following callbacks are supported:
441475

442-
* `ClientSideValidations.callbacks.element.after($element, eventData)`
443-
* `ClientSideValidations.callbacks.element.before($element, eventData)`
444-
* `ClientSideValidations.callbacks.element.fail($element, message, callback, eventData)`
445-
* `ClientSideValidations.callbacks.element.pass($element, callback, eventData)`
446-
* `ClientSideValidations.callbacks.form.after($form, eventData)`
447-
* `ClientSideValidations.callbacks.form.before($form, eventData)`
448-
* `ClientSideValidations.callbacks.form.fail($form, eventData)`
449-
* `ClientSideValidations.callbacks.form.pass($form, eventData)`
476+
* `ClientSideValidations.callbacks.element.after(element, eventData)`
477+
* `ClientSideValidations.callbacks.element.before(element, eventData)`
478+
* `ClientSideValidations.callbacks.element.fail(element, message, callback, eventData)`
479+
* `ClientSideValidations.callbacks.element.pass(element, callback, eventData)`
480+
* `ClientSideValidations.callbacks.form.after(form, eventData)`
481+
* `ClientSideValidations.callbacks.form.before(form, eventData)`
482+
* `ClientSideValidations.callbacks.form.fail(form, eventData)`
483+
* `ClientSideValidations.callbacks.form.pass(form, eventData)`
450484

451485
The names of the callbacks should be pretty straight forward. For example, `ClientSideValidations.callbacks.form.fail` will be called if a form failed to validate. And `ClientSideValidations.callbacks.element.before` will be called before that particular element's validations are run.
452486

453-
All element callbacks will receive the element in a jQuery object as the first parameter and the eventData object as the second parameter. `ClientSideValidations.callbacks.element.fail()` will receive the message of the failed validation as the second parameter, the callback for adding the error fields as the third and the eventData object as the third. `ClientSideValidations.elementValidatePass()` will receive the callback for removing the error fields. The error field callbacks must be run in your custom callback in some fashion. (either after a blocking event or as a callback for another event, such as an animation)
487+
All element callbacks receive the DOM element as the first parameter and the native event object as the second parameter. `ClientSideValidations.callbacks.element.fail()` receives the failed message as the second parameter, the callback for adding error fields as the third parameter, and the eventData object as the fourth parameter. `ClientSideValidations.callbacks.element.pass()` receives the callback for removing the error fields as the second parameter. The error field callbacks must still be invoked by your custom callback.
454488

455-
All form callbacks will receive the form in a jQuery object as the first parameter and the eventData object as the second parameter.
489+
All form callbacks receive the DOM form element as the first parameter and the native event object as the second parameter.
456490

457-
Here is an example callback for sliding out the error message when the validation fails then sliding it back in when the validation passes:
491+
Here is an example callback that animates the error message when validation fails:
458492

459493
``` javascript
460-
// You will need to require 'jquery-ui' for this to work
461-
window.ClientSideValidations.callbacks.element.fail = function($element, message, callback) {
494+
window.ClientSideValidations.callbacks.element.fail = function (element, message, callback) {
462495
callback()
463496
464-
if ($element.data('csvValid') !== false) {
465-
$element.parent().find('.message').hide().show('slide', { direction: 'left', easing: 'easeOutBounce' }, 500)
497+
var messageElement = element.parentElement.querySelector('.message')
498+
499+
if (messageElement) {
500+
if (typeof messageElement.animate === 'function') {
501+
messageElement.animate(
502+
[
503+
{ opacity: 0, transform: 'translateX(-8px)' },
504+
{ opacity: 1, transform: 'translateX(0)' }
505+
],
506+
{ duration: 250, easing: 'ease-out', fill: 'both' }
507+
)
508+
} else {
509+
messageElement.style.opacity = '1'
510+
messageElement.style.transform = 'translateX(0)'
511+
}
466512
}
467513
}
468514
469-
window.ClientSideValidations.callbacks.element.pass = function($element, callback) {
470-
// Take note how we're passing the callback to the hide()
471-
// method so it is run after the animation is complete.
472-
$element.parent().find('.message').hide('slide', { direction: 'left' }, 500, callback)
515+
window.ClientSideValidations.callbacks.element.pass = function (element, callback) {
516+
callback()
473517
}
474518
```
475519

@@ -478,12 +522,9 @@ window.ClientSideValidations.callbacks.element.pass = function($element, callbac
478522
background-color: red;
479523
border-bottom-right-radius: 5px 5px;
480524
border-top-right-radius: 5px 5px;
525+
display: inline-block;
481526
padding: 2px 5px;
482527
}
483-
484-
div.field_with_errors div.ui-effects-wrapper {
485-
display: inline-block !important;
486-
}
487528
```
488529

489530
Finally uncomment the `ActionView::Base.field_error_proc` override in `config/initializers/client_side_validations.rb`
@@ -506,22 +547,21 @@ By default, ClientSideValidations will automatically validate the form.
506547
If for some reason you would like to manually validate the form (for example you're working with a multi-step form), you can use the following approach:
507548

508549
```js
509-
$input = $('#myInputField');
510-
$form = $($input[0].form);
511-
validators = $form[0].ClientSideValidations.settings.validators;
550+
const input = document.getElementById('myInputField')
551+
const form = input.form
552+
const validators = form.ClientSideValidations.settings.validators
512553
513554
// Validate a single field
514-
// It might not work for multiple inputs selected at once by `$input`
515-
$input.isValid(validators);
555+
ClientSideValidations.isValid(input, validators)
516556
517557
// Validate the whole form
518-
$form.isValid(validators);
558+
ClientSideValidations.isValid(form, validators)
519559
```
520560

521561
To manually validate a single field, you may also trigger a focusout event:
522562

523563
```js
524-
$('#myInputField').trigger('focusout');
564+
input.dispatchEvent(new Event('focusout', { bubbles: true }))
525565
```
526566

527567
## Authors ##

0 commit comments

Comments
 (0)