JetSmartFilters frontend events
Subscribe to the JSF event bus and scope every callback. For a listing, the
three essential lifecycle channels are:
| Channel |
Callback arguments |
Meaning |
ajaxFilters/start-loading |
provider, queryId |
A real AJAX request started; preloader is active |
ajaxFilters/updated |
provider, queryId, response, requestOptions |
Response was processed and provider DOM/fragments were updated |
ajaxFilters/end-loading |
provider, queryId |
Loading teardown ran and the preloader was hidden |
Use updated to read or initialize the new DOM. Use end-loading for a
global “cycle finished” UI state. They are not aliases.
Load a companion script after JSF
JSF enqueues its public script in wp_footer only on pages where filters were
rendered. Queue the bridge after JSF has enqueued itself and before footer
scripts print:
add_action( 'wp_footer', static function (): void {
if ( ! wp_script_is( 'jet-smart-filters', 'enqueued' ) ) {
return;
}
wp_enqueue_script(
'acme-jsf-bridge',
plugins_url( 'assets/js/jsf-bridge.js', __FILE__ ),
array( 'jet-smart-filters' ),
'1.0.0',
true
);
}, 16 );
Subscribe with provider/query scoping
(() => {
const targetProvider = 'jsf-listing';
const targetQueryId = 'catalog-listing';
const isTarget = (provider, queryId) =>
provider === targetProvider && queryId === targetQueryId;
const bind = () => {
const bus = window.JetSmartFilters?.events;
if (!bus?.subscribe) {
return;
}
bus.subscribe('ajaxFilters/start-loading', (provider, queryId) => {
if (!isTarget(provider, queryId)) return;
document.querySelector('.catalog-shell')?.setAttribute('aria-busy', 'true');
});
bus.subscribe(
'ajaxFilters/updated',
(provider, queryId, response, requestOptions) => {
if (!isTarget(provider, queryId)) return;
initCatalogWidgets(document.querySelector('#catalog-listing'));
}
);
bus.subscribe('ajaxFilters/end-loading', (provider, queryId) => {
if (!isTarget(provider, queryId)) return;
document.querySelector('.catalog-shell')?.setAttribute('aria-busy', 'false');
});
};
if (window.JetSmartFilters?.events) {
bind();
} else {
document.addEventListener('jet-smart-filters/before-init', bind, { once: true });
}
})();
Make initCatalogWidgets() idempotent: destroy or detect an existing widget
before recreating it.
Other useful events
- DOM
jet-smart-filters/before-init: JSF global exists; filter discovery has
not run.
- DOM
jet-smart-filters/inited: initial filter groups are available.
- Bus
provider/content-rendered: callback receives
provider, $provider after provider HTML and builder integrations render.
It has no query ID, so it is insufficient alone when several instances share
a provider.
- Legacy jQuery
jet-filter-content-rendered: document event with
event, $provider, filterGroup, provider, queryId. Prefer the event bus in
new code; keep this only for a legacy integration contract.
- A provider element may receive
jet-filter-data-updated for providers that
return data rather than HTML.
Failure and concurrency rules
- A “start” event is not proof that content changed.
- In 3.8.3.1 a newer request aborts the previous XHR, and the abort path can
skip
end-loading. Do not maintain an unbounded global counter that assumes
perfect start/end pairs.
- Key UI state by provider/query ID and make repeated cleanup harmless.
updated fires on successful processing. Use application-level monitoring
if failed AJAX requests must also be reported.
- Prefer delegated click/input handlers for elements inside the replaced
wrapper.
References
Verified against JetSmartFilters 3.8.3.1 source:
includes/filters/manager.php:22-112
assets/js/public.js channels ajaxFilters/start-loading,
ajaxFilters/updated, ajaxFilters/end-loading,
provider/content-rendered
assets/lib/jet-plugins/jet-plugins.js
1---2name: jsf-frontend-events3description: Handle the JetSmartFilters frontend AJAX lifecycle and reinitialize behavior after provider DOM replacement. Use when JavaScript must react when filtering starts, rendered content updates, loading ends, or JSF initializes; when integrating sliders, galleries, analytics, accessibility, or other widgets; or when code references JetSmartFilters.events, ajaxFilters/start-loading, ajaxFilters/updated, ajaxFilters/end-loading, provider/content-rendered, or jet-filter-content-rendered.4---56# JetSmartFilters frontend events78Subscribe to the JSF event bus and scope every callback. For a listing, the9three essential lifecycle channels are:1011| Channel | Callback arguments | Meaning |12|---|---|---|13| `ajaxFilters/start-loading` | `provider, queryId` | A real AJAX request started; preloader is active |14| `ajaxFilters/updated` | `provider, queryId, response, requestOptions` | Response was processed and provider DOM/fragments were updated |15| `ajaxFilters/end-loading` | `provider, queryId` | Loading teardown ran and the preloader was hidden |1617Use `updated` to read or initialize the new DOM. Use `end-loading` for a18global “cycle finished” UI state. They are not aliases.1920## Load a companion script after JSF2122JSF enqueues its public script in `wp_footer` only on pages where filters were23rendered. Queue the bridge after JSF has enqueued itself and before footer24scripts print:2526```php27add_action( 'wp_footer', static function (): void {28 if ( ! wp_script_is( 'jet-smart-filters', 'enqueued' ) ) {29 return;30 }3132 wp_enqueue_script(33 'acme-jsf-bridge',34 plugins_url( 'assets/js/jsf-bridge.js', __FILE__ ),35 array( 'jet-smart-filters' ),36 '1.0.0',37 true38 );39}, 16 );40```4142## Subscribe with provider/query scoping4344```js45(() => {46 const targetProvider = 'jsf-listing';47 const targetQueryId = 'catalog-listing';4849 const isTarget = (provider, queryId) =>50 provider === targetProvider && queryId === targetQueryId;5152 const bind = () => {53 const bus = window.JetSmartFilters?.events;5455 if (!bus?.subscribe) {56 return;57 }5859 bus.subscribe('ajaxFilters/start-loading', (provider, queryId) => {60 if (!isTarget(provider, queryId)) return;61 document.querySelector('.catalog-shell')?.setAttribute('aria-busy', 'true');62 });6364 bus.subscribe(65 'ajaxFilters/updated',66 (provider, queryId, response, requestOptions) => {67 if (!isTarget(provider, queryId)) return;68 initCatalogWidgets(document.querySelector('#catalog-listing'));69 }70 );7172 bus.subscribe('ajaxFilters/end-loading', (provider, queryId) => {73 if (!isTarget(provider, queryId)) return;74 document.querySelector('.catalog-shell')?.setAttribute('aria-busy', 'false');75 });76 };7778 if (window.JetSmartFilters?.events) {79 bind();80 } else {81 document.addEventListener('jet-smart-filters/before-init', bind, { once: true });82 }83})();84```8586Make `initCatalogWidgets()` idempotent: destroy or detect an existing widget87before recreating it.8889## Other useful events9091- DOM `jet-smart-filters/before-init`: JSF global exists; filter discovery has92 not run.93- DOM `jet-smart-filters/inited`: initial filter groups are available.94- Bus `provider/content-rendered`: callback receives95 `provider, $provider` after provider HTML and builder integrations render.96 It has no query ID, so it is insufficient alone when several instances share97 a provider.98- Legacy jQuery `jet-filter-content-rendered`: document event with99 `event, $provider, filterGroup, provider, queryId`. Prefer the event bus in100 new code; keep this only for a legacy integration contract.101- A provider element may receive `jet-filter-data-updated` for providers that102 return data rather than HTML.103104## Failure and concurrency rules105106- A “start” event is not proof that content changed.107- In 3.8.3.1 a newer request aborts the previous XHR, and the abort path can108 skip `end-loading`. Do not maintain an unbounded global counter that assumes109 perfect start/end pairs.110- Key UI state by provider/query ID and make repeated cleanup harmless.111- `updated` fires on successful processing. Use application-level monitoring112 if failed AJAX requests must also be reported.113- Prefer delegated click/input handlers for elements inside the replaced114 wrapper.115116## References117118Verified against JetSmartFilters 3.8.3.1 source:119120- `includes/filters/manager.php:22-112`121- `assets/js/public.js` channels `ajaxFilters/start-loading`,122 `ajaxFilters/updated`, `ajaxFilters/end-loading`,123 `provider/content-rendered`124- `assets/lib/jet-plugins/jet-plugins.js`