# Upgrading from AJAX Loading 1.x to 2.0

Version 2.0 is a complete rewrite. Your on/off setting carries over, existing custom code keeps working, and you can go back to 1.1 at any time. This page explains what happens during the upgrade, what behaves differently, and how to roll back.

Related documents: [user guide](user-guide.md), [troubleshooting](troubleshooting.md), [developer guide](developer-guide.md).

## Contents

1. [Before you upgrade](#before-you-upgrade)
2. [What is migrated automatically](#what-is-migrated-automatically)
3. [Legacy jQuery and emoji removal](#legacy-jquery-and-emoji-removal)
4. [What changes in behaviour](#what-changes-in-behaviour)
5. [Custom code that keeps working](#custom-code-that-keeps-working)
6. [What no longer exists](#what-no-longer-exists)
7. [Moving custom code to the new events](#moving-custom-code-to-the-new-events)
8. [After upgrading: checklist](#after-upgrading-checklist)
9. [Rolling back to 1.1](#rolling-back-to-11)
10. [What uninstalling removes](#what-uninstalling-removes)

## Before you upgrade

* **Requirements:** WordPress 6.0 or newer and PHP 7.4 or newer (1.x accepted much older versions).
* **Custom code:** note any code that uses `wp_ajax_load_complete`, `reload_helper` or `History` (History.js). See [Custom code that keeps working](#custom-code-that-keeps-working).
* **Page cache:** if you use a caching plugin, a host cache or a CDN that caches pages, plan to purge it right after the update.

## What is migrated automatically

The migration runs by itself on the first request after the update (front end or dashboard), and also when the plugin is activated. You do not need to deactivate and reactivate the plugin.

| 1.x | 2.0 after the upgrade |
|---|---|
| **Enable plugin** on or off (`enable_plugin` = `'on'` / `'off'`) | Kept exactly. As in 1.x, only the value `'on'` counts as on. |
| jQuery was always loaded on the front end | **General → Advanced → Load jQuery for custom callbacks** is switched **on**. |
| WordPress emoji scripts were removed whenever the plugin was on | **General → Advanced → Disable WordPress emoji scripts** is switched **on**. |
| No other settings existed | Every other setting gets its 2.0 default. |
| Version option `ajax-loading` (for example `1.1`) | Updated to `2.0.0`. |

Other details:

* The upgrade never resets settings that exist. 2.0 does not switch the plugin back on if you turned it off (1.x could do that when its version option went missing).
* If the 1.x settings were never created (the plugin was activated but no administrator opened the dashboard afterwards, so 1.x stayed off), 2.0 treats the site as a new install and switches AJAX navigation on. 1.x would have done the same on the next dashboard visit.
* Keys added to the settings option by other code are kept.

The 2.0 defaults change how your site looks and behaves compared with 1.x: only the content area is replaced (not the whole body), a thin progress bar appears when a page takes a moment, and content fades out and in. You can change all of this on the **General** and **Loading Experience** tabs; **General → Content detection → Whole page body** is the closest to 1.x.

## Legacy jQuery and emoji removal

Two settings are switched on for upgraded sites only, so nothing that worked with 1.x breaks on the day of the update. New installs have both off.

### Load jQuery for custom callbacks

**Why it is on:** version 1.x always loaded jQuery on every front-end page. Callbacks written for 1.x, such as `wp_ajax_load_complete = function () { jQuery( … ) … }`, very often use jQuery. Without it, they would fail with "jQuery is not defined".

**When to switch it off:** when none of your custom front-end code relies on the plugin to load jQuery, for example:

* you have no `wp_ajax_load_complete` code, or it does not use `jQuery` or `$`;
* your theme or other plugins load jQuery anyway (then this setting changes nothing for them).

The 2.0 engine itself does not need jQuery. After switching it off, browse your site with the browser console open and look for "jQuery is not defined" errors. The environment report on **Tools & Diagnostics** shows whether this dependency is active.

### Disable WordPress emoji scripts

**Why it is on:** version 1.x removed WordPress's emoji detection script and styles from the front end whenever the plugin was enabled, without saying so. Some sites may now rely on the lighter page, so 2.0 keeps doing it after an upgrade, as a visible setting.

**When to switch it off:** if you want WordPress's default behaviour back. WordPress then adds its small emoji detection script again, which shows emoji as images in browsers that cannot display them natively. Modern browsers display emoji either way.

## What changes in behaviour

| Area | 1.x | 2.0 |
|---|---|---|
| What is replaced | The whole `<body>`; in fact the entire fetched document, `<head>` included, was injected into the body. | Only the content area (automatic detection, custom selectors, or the whole body if you choose). The header and footer are kept by default. |
| Scripts on the new page | Never ran. | Scripts in the new content run; new scripts and stylesheets the page needs are loaded; WordPress script data is refreshed. |
| `<head>` | Never updated (only the title). | Title, meta tags, canonical, hreflang and structured data are kept in sync. `<html lang>`, body classes and menu highlighting are updated too. |
| Loading effect | The whole page dimmed to 40%, could not be clicked, and slid down 70 px; the body was forced to `position: relative`. | A configurable indicator (progress bar by default) and page transitions. The plugin never styles `<html>` or `<body>`. |
| Back and forward | Always jumped to the top. | Return to the previous scroll position. |
| `#section` links | Links to a section on another page changed the address but loaded nothing. | The page loads and scrolls to the section. |
| Redirects | The old address stayed in the address bar. | The address bar shows the final address. |
| Non-ASCII addresses | Garbled, leading to 404 pages. | Kept as they are. |
| Ctrl/Cmd-click, `target`, `download` | Taken over by the plugin. | Left to the browser. |
| Other scripts' click handlers | Blocked (`return false`): mobile menu toggles and WooCommerce AJAX add-to-cart stopped working. | Run first; the plugin leaves alone any click another script has handled. |
| Links to files and feeds | Froze the page. | Load normally. |
| Failed pages | Requested again with a normal page load. | Still loaded normally (the visitor always reaches the page), and a visitor whose navigations keep failing gets normal page loads for a while. |
| `wp_ajax_load_complete` | Called about 1 second after the swap, without arguments. | Called right after the navigation has completely finished, with the navigation details as its only argument. Still once per successful navigation, never on the first page load. |
| `reload_helper.rootUrl` | The WordPress address (`site_url`), which broke sites with WordPress in a subdirectory. | The site address (`home_url`). |
| jQuery | Always loaded. | Only when **Load jQuery for custom callbacks** is on (on after an upgrade). It is then loaded on the pages where AJAX navigation runs and on the pages the plugin excludes on the server (cart, login pages and the like). |
| Customizer preview, page builder editors, AMP pages | The plugin ran there and could break them. | The plugin stays off there. |
| Analytics | Only the long-retired `pageTracker` (ga.js) and Reinvigorate trackers were notified. | Those are still notified if present; GA4, Tag Manager and a custom function are supported (see the user guide). |
| Settings screen | One on/off switch under **Settings → AJAX Loading**. | Same place, now with tabs. The old links `&con=about` and `&con=donate` still open the About and Say Thanks tabs. |
| Front-end files | jQuery plus a 48 KB unminified script on every page while enabled. | One script of about 169 KB minified (about 58 KB gzipped) and 8 KB of CSS (2 KB gzipped), loaded deferred in the footer and only on pages where AJAX navigation runs. No jQuery needed. |
| Uninstall | Options were left in the database. | Deleting the plugin removes all its data. |

## Custom code that keeps working

| What | Status in 2.0 |
|---|---|
| `wp_ajax_load_complete = function () { … };` (also with `var`, `let`, `const` or `function wp_ajax_load_complete() {}` at the top level of a script) | Still called once after every successful AJAX navigation. Errors inside it are caught and written to the browser console. |
| `window.reload_helper` | Still defined in the page `<head>`, before your scripts, on every page where the plugin is active (also on pages it excludes, such as the cart). Values your code sets before the page has finished loading are kept. `ids` (a CSS selector) now really excludes the matching links, and a `container_id` other than `body` is tried first when detecting the content area. |
| Class `no-ajaxy` | Still opts a link, or every link inside an element, out of AJAX navigation. |
| Script handle `ajax-loading-WPajaxLoad` | Unchanged, so code that dequeues the plugin's script or depends on it keeps working. The stylesheet handle is `ajax-loading`. |
| `run_WP_Ajax_loading()`, the classes `WP_Ajax_loading_Core` and `WP_ajax_loading_Admin`, the `WPAJXL_*` constants | Kept (the classes as aliases of the new ones). |
| Options `_wp_ajax_loading` (with `enable_plugin` = `'on'`/`'off'`) and `ajax-loading` | Same names and the same on/off values. |
| Settings page `options-general.php?page=ajax-loading-admin` | Same address. |

## What no longer exists

* **History.js:** the global `History` object and its `statechange` and `anchorchange` events are gone. Use the `wp_ajax_load_*` events instead (for example `wp_ajax_load_popstate` for back and forward).
* **The one-second delay** before `wp_ajax_load_complete`. If old code depends on it, a developer can restore it with a filter (see below).
* **Unused `reload_helper` keys:** `mcdc`, `searchID`, `transition`, `scrollTop`, `loader` and `bp_status` are still defined for compatibility but have no effect (most of them never had one in 1.x).
* **The `.header` copy:** 1.x overwrote every element with the class `header` after each navigation; 2.0 does not.
* **Old unused files** from earlier versions have been removed.

## Moving custom code to the new events

Your 1.x callback keeps working, but the new events have advantages: several scripts can listen at the same time, and you get details about the navigation, including the element that was replaced.

Because 2.0 keeps the header and footer in place by default, re-initialise only what is inside the new content. Running set-up code for the whole page again can attach a second set of handlers to the header or footer.

**Before (1.x style):**

```js
wp_ajax_load_complete = function () {
	// Re-initialise everything on the page.
	initMySliders( document );
};
```

**After (2.0 event):**

```js
function initMySliders( root ) {
	root.querySelectorAll( '.my-slider' ).forEach( function ( el ) {
		// Start one slider on el.
	} );
}

// First page load.
document.addEventListener( 'DOMContentLoaded', function () {
	initMySliders( document );
} );

// After every successful AJAX navigation (never on the first page load).
document.addEventListener( 'wp_ajax_load_complete', function ( event ) {
	event.detail.containers.forEach( initMySliders );
} );
```

`event.detail.containers` lists the elements whose content was replaced (the content area first); `event.detail.container` is the first of them. `event.detail.url` and `event.detail.title` give the new address and title.

**With jQuery** (the event is mirrored to jQuery when jQuery is on the page; the details are the second argument):

```js
jQuery( document ).on( 'wp_ajax_load_complete', function ( event, detail ) {
	initMySliders( detail.container );
} );
```

**Restoring the 1.x delay** (PHP, for example in your theme's `functions.php` or a small plugin). The value is in milliseconds (0 to 10000) and only delays the old global `wp_ajax_load_complete` function, not the events:

```php
add_filter( 'ajax_loading_legacy_callback_delay', function () {
	return 1000;
} );
```

All events, their details and the `window.AjaxLoading` API are described in the [developer guide](developer-guide.md).

## After upgrading: checklist

1. Purge your page cache (plugin, host and CDN).
2. Open **Settings → AJAX Loading → Tools & Diagnostics** and run **Check content selectors** and **Test navigation**. Read the warnings in the environment report.
3. Browse your site: menus, search, forms, sliders, shop pages. Use **Test mode (administrators only)** on the Overview tab if you want to check things before visitors see them.
4. Adjust the look on **Loading Experience**.
5. Decide whether you still need **Load jQuery for custom callbacks** and **Disable WordPress emoji scripts** (see [above](#legacy-jquery-and-emoji-removal)).
6. Consider moving custom callbacks to the `wp_ajax_load_complete` event.

If something does not work, see [troubleshooting](troubleshooting.md).

## Rolling back to 1.1

You can go back to version 1.1 at any time.

### Steps

1. **Optional but recommended:** export your 2.0 settings (**Tools & Diagnostics → Export settings → Download export file**) so you can import them again later.
2. **Do not delete the plugin first.** Deleting it removes all of its settings (see [below](#what-uninstalling-removes)). Install 1.1 over 2.0 instead, in one of these ways:
   * **WP-CLI:**

     ```sh
     wp plugin install ajax-loading --version=1.1 --force
     ```

   * **By hand:** on the plugin's page on WordPress.org, open **Advanced View** (`https://wordpress.org/plugins/ajax-loading/advanced/`), choose **1.1** under **Previous Versions** and download it. Then go to **Plugins → Add New Plugin → Upload Plugin**, choose the zip file, click **Install Now** and confirm **Replace current with uploaded**.
3. If automatic updates are on for this plugin, turn them off on the Plugins screen, or WordPress will update it to 2.0 again.
4. Purge your page cache.

### Why rolling back is safe

* Version 1.1 only reads `enable_plugin` from the `_wp_ajax_loading` option, and 2.0 stores it as `'on'` or `'off'` exactly as 1.1 expects. Your on/off choice carries over.
* 1.1 ignores all settings and options that only 2.0 uses.
* 1.1 only resets its settings (and switches itself on) when its version option `ajax-loading` is empty. 2.0 set it to `2.0.0`, so 1.1 leaves your settings alone.

### Things to know

* **Saving the 1.1 settings page** replaces the whole settings option with just the on/off value, so your 2.0 settings are lost. If you later update to 2.0 again, 2.0 recognises these 1.x settings (even though its own version number is still stored) and migrates them again: on/off kept, legacy jQuery and emoji removal on, everything else at the defaults. Import your export file to get your settings back. If you never save the 1.1 settings page, all 2.0 settings are still there when you update again.
* The problems fixed in 2.0 come back with 1.1, for example scripts not running after navigation.
* The 2.0 error log and last compatibility check stay in the database after a rollback (1.1 has no uninstall routine). They are not loaded on normal page views.

## What uninstalling removes

Uninstalling happens only when you **Delete** the plugin on the Plugins screen (deactivating keeps everything). It removes:

| Data | Stored as |
|---|---|
| All settings | option `_wp_ajax_loading` |
| The installed version | option `ajax-loading` |
| The navigation error log | option `_wp_ajax_loading_log` |
| The last compatibility check result | option `_wp_ajax_loading_diagnostics` |
| The error log rate limit | transient `ajax_loading_log_rate` |
| Any other temporary data of the plugin | transients whose names start with `ajax_loading_` or `wpajxl_` |

On a multisite network, the data of every site is removed.

After deleting, a new installation starts with the default settings (AJAX navigation on for everyone). To keep your configuration, export your settings before deleting the plugin.
