How we took jQuery out of PokaTheme 5 without breaking existing sites

PokaTheme 5 loads no jQuery on the front end, which takes 42.7 KB off every page. The rewrite was the easy part. Making sure the sites already running the theme did not break quietly was the hard part.

By The PokaTheme team 5 min read PokaTheme 5.0.0

In this article
  1. What jQuery was costing
  2. First, find out where jQuery actually was
  3. Five steps, each with its own tests
  4. The bugs that had been hiding
  5. The search box was rebuilt, not translated
  6. Not breaking the sites already running PokaTheme
  7. Keeping it out
  8. What still uses jQuery

PokaTheme 5 does not load jQuery on the front end. On a WordPress site that is less common than it should be, and it takes 42.7 KB, gzipped, off every page.

The rewrite was the easy part. The hard part was that about 2,000 sites run PokaTheme 4, and some of them have jQuery code of their own in a child theme. If we removed the library and one of those scripts stopped working, nothing would tell the site owner. The error would sit in a browser console that nobody has open.

This is how we did it.

What jQuery was costing

These are the gzipped bytes that no longer load on a PokaTheme 5 page:

jquery.min.js             30,294
jquery-ui/core             6,536
jquery-ui/menu             3,055
jquery-ui/autocomplete     2,834
                          ------
                          42,719 bytes

What replaced them, a hand-written reviews search, adds 450 bytes to the theme’s own script.

First, find out where jQuery actually was

The assumption was that the theme’s JavaScript was “all jQuery”. The survey said otherwise. Almost all of it was in two files: 57 $() calls and 161 jQuery method calls in global.js, and six more calls in the geolocation AJAX mode. Every other source file had none.

The bundled libraries did not need it either. The mobile menu (mmenu-light) and the carousel (tiny-slider) are plain JavaScript. The theme declared jQuery as a dependency of its vendor bundle only for the sake of a separate script.

Five steps, each with its own tests

  1. Geolocation AJAX mode, rewritten with fetch.
  2. Eight small features: the header search toggle, the FAQ toggle, both carousels, the share pop-ups, the fixed review box, the sticky header, the preloader and the sponsored-content modal.
  3. The load-more buttons and the table sorting.
  4. The reviews search, which had to be rebuilt rather than translated.
  5. Keeping jQuery off without breaking anyone.

None of the eight features in step 2 had a single test before this. That matters more than it sounds: a toggle that stops toggling throws no error and fails no lint, and every check on the page markup still passes, because the markup did not change. So each feature got a browser test first, eleven in all.

They paid for themselves straight away. The first version of the rewrite ran as soon as the file loaded instead of waiting for the page, and all seven tests that inject their own markup failed at once. jQuery’s $(function () { … }) had been waiting for the document all along, and the new code has to do the same.

The bugs that had been hiding

Reading every line of old code closely turns things up. A few of them:

  • Geolocation AJAX mode had been failing on every request since PHP 8. jQuery sent a nested object that PHP received as an array, a sanitising function turned that array into an empty string, and the next line tried to read it as an array. Every placeholder got a server error and stayed empty. The mode is off by default, which is probably why nobody noticed.
  • The debounce function never worked. It was an arrow function reading arguments, which in an arrow function belongs to the surrounding code.
  • The fixed review box threw an error on every scroll on pages that had the box but not the content it measures against.
  • Reading localStorage could stop the whole script. It throws in browsers that block site data, and it was not guarded.
  • Sorting needed numbers for ratings and text for names. Sorted as text, a rating of 10 comes before 9. And jQuery’s .data() had been turning a review titled “777” into the number 777, which then crashed the name sort.

That last one has a test that only catches the bug because it adds rows rated 9 and 10. The ratings already on the test site sort the same way as text and as numbers, so seven of the eight sorting tests would pass against a broken sort.

The search box was rebuilt, not translated

The reviews search used jQuery UI’s autocomplete, customised to print a whole review card in each result: logo, title, bonus line and two buttons. An autocomplete option is not allowed to contain buttons, and the widget exposed no accessible roles at all. The one accessible thing it did produce, a spoken count of the results, was hidden by the theme’s own stylesheet with display: none, so screen readers never announced it.

The new search works as a disclosure. The input says whether the results are open, the number of results is announced, Escape closes the results and returns focus to the input, the down arrow moves into them, clicking elsewhere dismisses them, and Tab moves through the links normally.

Not breaking the sites already running PokaTheme

We could have left jQuery on by default and let site owners switch it off. Then almost nobody would get the 42.7 KB back, and changing the default later would be a second migration.

So jQuery is off by default, and the theme looks first. On the first admin page you open after updating, PokaTheme 5 scans the active child theme for .php and .js files that use jQuery. If it finds any, it turns jQuery back on for that site and shows a notice listing up to five of the files. If it finds none, the notice says what it checked.

A few rules keep the scan honest:

  • It runs once per version, on the first admin request. Updating a theme does not fire WordPress’s theme-activation hook, so waiting for activation would miss exactly the sites that update in place.
  • It only ever turns jQuery on. If you switch it off afterwards, the scan will not undo that.
  • It skips build folders (node_modules, vendor, .git, languages) and files over 2 MB.
  • It does not pretend to have looked everywhere. Code in a plugin, or pasted into a Customizer setting, is invisible to it, and the notice says so.

If you have jQuery code outside your child theme, turn jQuery back on under Customizer → Theme Settings → General → Load jQuery, or from code:

add_filter( 'poka_front_end_dependencies', function ( $deps ) {
	$deps[] = 'jquery';
	return $deps;
} );

Keeping it out

A test counts the jQuery usage left in every front-end source file against a budget. Each step lowered the budget, and it now reads zero everywhere. The same test checks that every script declares the dependencies it uses and no others: a script that has stopped using jQuery but still declares it saves nobody anything, and one that uses it without declaring it is a broken page.

On top of that, every release is checked against a page-weight budget, so a library that creeps back in fails the build.

What still uses jQuery

The WordPress admin. The metabox framework, the Customizer repeater control and the geolocation settings page still use it, and WordPress loads jQuery in the admin anyway, so there was nothing to save there.

If something on your site stopped working after updating to PokaTheme 5, tell us in the support forum and we will help you track it down.

Written by the PokaTheme team

PokaTheme is designed and built by Redshift web development, a small team in Thessaloniki, Greece, making WordPress themes for affiliate sites for more than ten years.