Performance and Multisite help — book a call with me via my Expert page on Clarity, I use this service for calls.

Replacing jQuery with Vanilla JavaScript in my WordPress plugin

Inspired by Gijo’s Varghese post I devoted last weekend to replace jQuery with vanilla JavaScript in my own plugin, SpeedGuard. (And I’m very happy I did. Share this post to inspire other developers to do so too!)

* SpeedGuard is a WordPress plugin to run Google PageSpeed Insights tests for your entire site, on schedule.

Why bother?

Long story short:

Replacing jQuery with Vanilla JavaScript in my WordPress plugin

jQuery was a thing like 10 years ago. Time moved forward but jQuery didn’t do much. And today it’s like a suitcase without a handle — hard to carry and hard to throw away. Share this on Twitter

Lots of things that couldn’t have been done with plain JavaScript 10 years ago, are doable now. Browser support changed significantly, as well.

  • Vanilla JS is faster than jQuery (no need to load all that ↑)
  • Vanilla JS means no dependencies (no need to wait till it’s loaded to do your thing)
  • Vanilla JS means fewer third-parties and fewer conflicts, therefore

There are might still be cases when you would like to use jQuery, but whenever you can — it’s better to use vanilla JS.

My plugin, SpeedGuard, in its previous version (1.8.3) used jQuery for these things:

  1. autocomplete for a search field
  2. make AJAX calls to run PageSpeed Insights tests in the background and update results once the test is finished
  3. mark test with color depending on speed result: green, yellow or red
  4. toggle metaboxes visibility

So I went ahead and replaced them all with plain JavaScript (version 1.8.4).

Here are the results, no jQuery used here, just plain JavaScript

There is no jQuery used here, just plain JavaScript

Search field with autocomplete

When you want to add a page from your website to be monitored by SpeedGuard you can do so by typing its title in the search field, and you’ll get suggestions to choose from.

jQuery

I was using jQuery with autocomplete-ui before:

jQuery(function($){
    var getData = function (request, response) {
      jQuery.ajax({
            url: speedguardsearch.search_url + request.term,
            type: "GET",
            contentType: 'application/json; charset=utf-8',
         beforeSend: function ( xhr ) {
            xhr.setRequestHeader( 'X-WP-Nonce', speedguardsearch.nonce );
         },
            success: function(data) {
            if ( data !== null ) {
               var results = [];
               console.log(results);
               for(var key in data) {
                  var valueToPush = { };
                  valueToPush["label"] = data[key].label;
                  valueToPush["value"] = data[key].ID;
                  valueToPush["permalink"] = data[key].permalink;
                  valueToPush["type"] = data[key].type;
                  results.push(valueToPush); 
               }              
             var result_to_show = response(results.slice(0, 6));
            }      
            },
            error : function(xhr, textStatus, errorThrown) {
             //console.log('error message here');
            },
            timeout: 5000,
        });    
    }; 
    var selectItem = function (event, ui) {
      event.preventDefault();
      $("#speedguard_new_url").val(ui.item.label);
      $("#speedguard_new_url_permalink").val(ui.item.permalink);
      $("#speedguard_item_type").val(ui.item.type);
      $("#speedguard_new_url_id").val(ui.item.value);
   } 
    $('input[name="speedguard_new_url"]').autocomplete({
        source: getData,
        select: selectItem,
        minLength: 2,
    });
});

Vanilla JS

Now I use Awesomplete instead. It’s pure JS, simple to use, it weigths only 2KB, it’s cool!

document.addEventListener('DOMContentLoaded', function() {
     const min_letters = 2; 
     var autocomplete_field = document.getElementById('speedguard_new_url');
     var awesomplete_field = new Awesomplete(autocomplete_field);    
     autocomplete_field.addEventListener('keyup', function() {
         var user_input = this.value;  
         if ( user_input.length >= min_letters ) {            
             fetch(   speedguardsearch.search_url + user_input, {
                 method: 'GET',
                 headers: {
                         'Content-Type': 'application/json',
                         'X-WP-Nonce': speedguardsearch.nonce
                       }
             })
             .then( response => {
                   if (response.status !== 200) {
                     console.log('Problem! Status Code: ' +
                       response.status);
                     return;
                   } 
                 response.json().then( posts => {          
                 var results = [];
                     for(var key in posts) {
                         var valueToPush = {}; 
                         valueToPush["label"] = posts[key].label;    
                         valueToPush["value"] = { id: posts[key].ID, permalink: posts[key].permalink, type: posts[key].type};
                         results.push(valueToPush);
                     }
                 awesomplete_field.list = results;  
                 awesomplete_field.evaluate();  
                 })
                 .catch(function(err) {
                 //  console.log('No results');
                 })
                 })
                 .catch(function(err) {
                     console.log('Error: ', err);
                 });
         }
     });
     awesomplete_field.replace = function(suggestion) {
         this.input.value = suggestion.value.permalink; //input field 
         document.getElementById("speedguard_new_url_permalink").value = suggestion.value.permalink;
         document.getElementById("speedguard_item_type").value = suggestion.value.type;
         document.getElementById("speedguard_new_url_id").value = suggestion.value.id;   
     };
 });

WordPress AJAX calls ( for updating PageSpeed Insights tests)

After I created a PageSpeed Insights test I go check every 10 seconds if it’s already finished. And as soon as it is — I save results to a database.

jQuery:

jQuery(document).ready(function($) {
 var data = {
 'action': 'run_waiting_tests',
 'post_ids': <?php echo json_encode( array_values( $waiting_pages ) ); ?>,
 };

 jQuery.post(ajaxurl, data, function(response) {
 setTimeout(function () {
 window.location.replace(window.location.href + "&speedguard=load_time_updated");
 }, 10000) 
 });
 });

Vanilla JS:

 var waiting_posts = <?php echo json_encode( array_values( $waiting_pages ) ); ?>;
 const params = new URLSearchParams();
 params.append('action', 'run_waiting_tests');
 for (var i = 0; i < waiting_posts.length; i++) {
 params.append('post_ids[]', waiting_posts[i]);
 }

 fetch(ajaxurl, {
 method: 'POST',
 credentials: 'same-origin',
 headers: {
 'Content-Type': 'application/x-www-form-urlencoded',
 'Cache-Control': 'no-cache',
 },
 body: params,
 })

 .then(response => {
 response.json()
 setTimeout(function () {
 window.location.replace(window.location.href + "&speedguard=load_time_updated");
 }, 10000) 
 })
 .catch(err => console.log(err));

Add class depending on AJAX results

Depending on the page loading time PageSpeed Insights assigns it a score, and I want to color a dot before the loading time accordingly — green, yellow or red

jQuery

jQuery(document).ready(function($){
 $("span.speedguard-score").each(function() {
     $(this).data('score') > 0.7 ? $(this).children('span').addClass('score-green') : ($(this).data('score') > 0.4 ? $(this).children('span').addClass('score-yellow') : $(this).children('span').addClass('score-red'));
         }); 
}); 

Vanilla JS:

document.addEventListener('DOMContentLoaded', function() {    
     var testscore = document.getElementsByClassName("speedguard-score");
     var i;
     for (i = 0; i < testscore.length; i++) {         var datascore = testscore[i].getAttribute('data-score');         if ( datascore > 0.7 ) testscore[i].classList.add("score-green");
         else if ( datascore > 0.4 ) testscore[i].classList.add("score-yellow");
         else testscore[i].classList.add("score-red");    
     }
});

Metabox visibility toggles

WordPress lets us arrange content in our plugins pages with native metaboxes. But to make them show/hide content there is another jQuery script to be loaded and JS function to be added.

jQuery

wp_enqueue_script('postbox');  //script to load
jQuery(document).ready(function($){
postboxes.add_postbox_toggles( 'speedguard'); 
});

Vanilla JS

document.addEventListener('DOMContentLoaded', function() {    
var a = document.getElementsByClassName("postbox"); 
var i; 
for (let i = 0; i < a.length; i++) {     
var togglebutton = a[i].querySelector('.handlediv');     togglebutton.addEventListener('click', function() {         
a[i].classList.toggle("closed");     }); 
}
});

Notes:

  • To replace other things with vanilla JS: check out this cool Cheat sheet for moving from jQuery to vanilla JavaScript
  • Browser support: I used Fetch API and it’s supported by all major browsers, so as many other others vanilla JS widgets
  • What about IE? Well, WordPress seems to be dropping IE 11 support. But in case you absolutely need it (maybe for front-end, I dunno) there are polyfills that you can use for IE.
  • Most of this things are supported by major browsers. For example,, here

Wrap up

So, my plugin is completely jQuery free now.

It doesn’t mean that WordPress won’t be loading a bunch of jQuery-related stuff on my plugin’s pages, it still will.

But SpeedGuard won’t be using it anymore, won’t be waiting for it to load before doing its thing, and will be ready to embrace the day when WordPress would not be loading jQuery by default.

You can do that too! Let me know in the comments below if you already did!

Leave a Reply

Your email address will not be published. Required fields are marked *