How jQuery events and vanilla JavaScript events are usually defined on WordPress projects #
Since WordPress includes and recommends the use of jQuery for the development of custom scripts and plugins, most plugins use jQuery in their code to handle all kinds of events and functions.
Many plugins and custom scripts use jQuery and JavaScript events directly attached to the HTML elements where the events are originated.
Although this is the way recommeded in the WordPress plugin handbook, it does not always work with Fluid Checkout. When jQuery and JavaScript events are attached directly to the elements, the reference to the event handler functions for these events is lost when these elements are replaced on the page.
In this article, you will learn how to fix this issue in your scripts and a more robust way to use jQuery and JavaScript events that works every time, with and without Fluid Checkout.
Code examples with directly attached events — which does not always work #
Take the script examples below which change the value of checkout fields to UPPERCASE
or lowercase
while the customer is typing in the field.
Event handlers are attached directly to each element that matches a selector at page initialization. These event handlers will not work properly when Fluid Checkout replaces those elements on the page after checkout page fragments are replaced.
Using jQuery
<script type="text/javascript">
jQuery( document ).ready( function() {
var fieldsSelector = '#account_first_name, #account_last_name, #shipping_first_name, #shipping_last_name, #shipping_company, #billing_first_name, #billing_last_name, #billing_company, #form-field-first_name, #form-field-last_name';
jQuery( fieldsSelector ).keyup(function() {
jQuery( this ).val( function( i, val ) {
return val.toUpperCase();
} );
} );
} );
jQuery( document ).ready( function() {
var fieldsSelector = '#account_email, #billing_email, #form-field-email';
jQuery( fieldsSelector ).keyup( function() {
jQuery( this ).val( function( i, val ) {
return val.toLowerCase();
} );
});
});
</script>
Using Vanilla JavaScript
<script type="text/javascript">
var changeToUppercase = function( e ) {
var field = e.target;
field.value = field.value.toUpperCase();
}
var changeToLowercase = function( e ) {
var field = e.target;
field.value = field.value.toLowerCase();
}
var initEvents = function() {
// Uppercase fields
var uppercaseFieldsSelector = '#account_first_name, #account_last_name, #shipping_first_name, #shipping_last_name, #shipping_company, #billing_first_name, #billing_last_name, #billing_company, #form-field-first_name, #form-field-last_name';
var uppercaseFields = document.querySelectorAll( uppercaseFieldsSelector );
for ( var i = 0; i < uppercaseFields.length; i++ ) {
var field = uppercaseFields[ i ];
field.addEventListener( 'keyup', changeToUppercase );
}
// Lowercase fields
var lowercaseFieldsSelector = '#account_email, #billing_email, #form-field-email';
var lowercaseFields = document.querySelectorAll( lowercaseFieldsSelector );
for ( var i = 0; i < lowercaseFields.length; i++ ) {
var field = lowercaseFields[ i ];
field.addEventListener( 'keyup', changeToLowercase );
}
};
// Trigger initialization when page is loaded
document.addEventListener( 'load', initEvents );
</script>
Why jQuery and JavaScript events does not always work with Fluid Checkout #
To optimize the WooCommerce checkout page with better layout and functionality, Fluid Checkout needs to do things a little bit differently.
One of the things Fluid Checkout does differently is to replace more parts of the checkout page while updating the checkout page fragments, which are not originally replaced with the native WooCommerce checkout.
Some of these checkout fragments replaced by Fluid Checkout are:
- Account fields (when using the Account matching feature from Fluid Checkout PRO)
- Shipping address form fields
- Billing address form fields
- The place order button and surrounding elements
- … and more.
When these fragments are replaced on the checkout page, the reference to the jQuery and JavaScript event functions attached directly to those elements is lost. In that case, the events will fire but the functions which were supposed to be attached will not run.
Available solutions #
There are two ways to fix this problem:
- Solution 1: Use robust captured events with jQuery or Vanilla JavaScript (recommended)
- Solution 2: Re-attach the events to the elements after every checkout update
Solution 1: Use robust captured events with jQuery or Vanilla JavaScript (recommended) #
This solution consists in using the desired events captured at a higher level on the hierarchy of elements on the page, and triggering the event handler function only for elements that match a certain criteria or selector.
With this approach, it does not matter if the element was replaced with the checkout fragments updates or another method. The event will always run, and we check in real time whether the event handler function should be run for that particular element.
Because we recommend using Vanilla JavaScript for the reasons explained below we first show below the solution using Vanilla JavaScript. The solution using jQuery is available further down in this section.
Using Vanilla JavaScript (recommended)
Instead of attaching an keyup
event handler directly on each element, we list for any event keyup
at the element document.body
and run function handleKeyUp
which checks if the element matches the respective selectors for uppercase or lowercase fields and run the respective functions changeToUppercase
and changeToLowercase
.
<script type="text/javascript">
var uppercaseFieldsSelector = '#account_first_name, #account_last_name, #shipping_first_name, #shipping_last_name, #shipping_company, #billing_first_name, #billing_last_name, #billing_company, #form-field-first_name, #form-field-last_name';
var lowercaseFieldsSelector = '#account_email, #billing_email, #form-field-email';
var changeToUppercase = function( field ) {
// Bail if field is not available
if ( ! field ) { return; }
field.value = field.value.toUpperCase();
}
var changeToLowercase = function( field ) {
// Bail if field is not available
if ( ! field ) { return; }
field.value = field.value.toLowerCase();
}
var handleKeyUp = function( e ) {
// UPPERCASE
if ( e.target.matches( uppercaseFieldsSelector ) ) {
changeToUppercase( e.target );
}
// LOWERCASE
else if ( e.target.matches( lowercaseFieldsSelector ) ) {
changeToLowercase( e.target );
}
}
document.body.addEventListener( 'keyup', handleKeyUp );
</script>
Using jQuery
Instead of using the event function keyup
directly on each element that matches the selectors, we use the function on
passing which event to capture, in this case keyup
, and telling jQuery only run the event functions changeToUppercase
and changeToLowercase
if the element matches the respective selector.
<script type="text/javascript">
var changeToUppercase = function() {
jQuery( this ).val( function( i, val ) {
return val.toUpperCase();
}jQuery );
}
var changeToLowercase = function() {
jQuery( this ).val( function( i, val ) {
return val.toLowerCase();
} );
}
var initEvents = function() {
var uppercaseFieldsSelector = '#account_first_name, #account_last_name, #shipping_first_name, #shipping_last_name, #shipping_company, #billing_first_name, #billing_last_name, #billing_company, #form-field-first_name, #form-field-last_name';
var lowercaseFieldsSelector = '#account_email, #billing_email, #form-field-email';
jQuery( document.body ).on( 'keyup', uppercaseFieldsSelector, changeToUppercase );
jQuery( document.body ).on( 'keyup', lowercaseFieldsSelector, changeToLowercase );
}
jQuery( document ).ready( initEvents ); // This will attach events on page initialization
</script>
Solution 2: Re-attach the events to the elements after every checkout update #
Another solution would be to re-attach the jQuery or Vanilla JavaScript events to the elements after every checkout page fragments update.
While this should work for most cases, it sometimes can cause events to run more than once for the same element if that particular element was not replaced on the page for some reason, hence why we recommend using captured events instead which is a more robust solution that works every time.
This solution is also not possible without using jQuery because the event updated_checkout
triggered by WooCommerce only works with jQuery.
<script type="text/javascript">
var changeToUppercase = function() {
jQuery( this ).val( function( i, val ) {
return val.toUpperCase();
} );
}
var changeToLowercase = function() {
jQuery( this ).val( function( i, val ) {
return val.toLowerCase();
} );
}
var initEvents = function() {
var uppercaseFieldsSelector = '#account_first_name, #account_last_name, #shipping_first_name, #shipping_last_name, #shipping_company, #billing_first_name, #billing_last_name, #billing_company, #form-field-first_name, #form-field-last_name';
var lowercaseFieldsSelector = '#account_email, #billing_email, #form-field-email';
jQuery( uppercaseFieldsSelector ).keyup( changeToUppercase );
jQuery( lowercaseFieldsSelector ).keyup( changeToLowercase );
}
jQuery( document ).ready( initEvents ); // This will attach events on page initialization
jQuery( document.body ).on( 'updated_checkout', initEvents ); // This will re-attach events after checkout page fragments are replaced
</script>
The case to ditch jQuery in favor of Vanilla JavaScript (when possible) #
It is common to use jQuery when developing a plugin for WordPress or some custom code, and almost impossible to not to use it when developing for WooCommerce as it relies heavily on the use of jQuery. Also most 3rd-party plugins made for WooCommerce use jQuery for that reason.
Although, here at Fluid Checkout we prefer to use pure/vanilla JavaScript when possible. Vanilla JavaScript is just pure JavaScript using only the functions and resources available natively with modern browsers, without the use of any custom code library like jQuery and others.
As modern browsers natively provide all the resources provided by jQuery which solved many compatibility issues between browsers that made jQuery so popular back in the days, today jQuery is not really necessary anymore and is seen as an extra load that can be avoided for better performance.