JetEngine Listing Grid — Skeleton Loading
This guide explains how to enable skeleton loading for JetEngine Listing Grids, which widgets Auto Apply recognizes, how to add manual classes, and how to extend compatibility without changing this plugin.
How To Start
- Prefer Auto Apply first: in the Listing Grid controls, enable “Skeleton Loading” and turn on “Auto Apply Skeleton”. The system detects supported widgets and assigns the proper skeleton classes for you.
- If Auto Apply is not effective for your layout, switch it off and add the classes manually to the specific widgets inside the Listing Item (Elementor editor → widget → Advanced → CSS Classes). Use the methods below.
Overview
- Works during JetEngine async updates (Load More, Infinite Scroll, JetSmartFilters, etc.).
- Targets inner parts (image/icon/title/description lines, list items) — no monolithic overlay on containers.
- Honors Reduced Motion (disables shimmer animation if requested by the OS).
Supported Widgets (Auto Apply recognition)
Elementor (Default):
- Heading
- Text Editor
- Icon List
- Button
- Video
- Image
- Icon
- Icon Box
- Image Box
- Shortcode
JetEngine:
- Dynamic Field (multiline text overlay)
- Dynamic Terms
- Dynamic Link
- Dynamic Image
-
Dynamic Field – Gallery Grid Slider (per–image skeleton) - Data Store Button
- Jet Video
JetElements:
- Button
- Headline
- Video Player
Dynamic Elementor Extension:
- Dynamic Opening Hours
Notes:
- Auto Apply recognizes the list above. The skeleton system itself can be used manually on any widget.
- Auto Apply does not apply full‑block skeletons on wrappers; it decorates meaningful inner parts.
Special Skeleton Classes
skeleton-loading— Universal block skeleton (type‑agnostic). Ideal for images, generic containers, or widgets without a dedicated adapter.skeleton-multitext-loading— Multiline text line overlay for Text Editor, Heading, Dynamic Field (text mode).skeleton-list-loading— Icon List: per‑<li>stripes.skeleton-icon-box-loading— Icon Box split (icon/title/description).skeleton-image-box-loading— Image Box split (image/title/description). Skips image shape if the image slot is empty (no media/background).skeleton-icon-loading— Icon: circle on.elementor-icon.skeleton-dynamice-g-loading— Dynamic Field Gallery Grid mode: enables per‑image skeletons on.jet-engine-gallery-grid__item-wrap.skeleton-dynamice-sl-loading— Dynamic Field Slider mode: enables per‑image skeletons on.jet-engine-gallery-slider__item-wrap.
Optional helpers
skeleton-silent-bg— Silences background (color/image) and box‑shadow during loading.skeleton-silent-border— Makes the border transparent during loading.skeleton-z-bg— Pushes the block shimmer layer behind child elements.skeleton-loading-justhide— Hides the element while loading (no skeleton). (will set opacity and height to 0, and visibility to hidden, )
Absolute positioning tip:
selector {
--hw-skeleton-original-position: absolute;
}
Keeps the element from snapping if it was absolutely positioned. Change the selector to your widget class name
Listing Grid Controls
After enabling “Skeleton Loading” you can set:
- Corners: straight or rounded (rounded can have a custom radius).
- Animation style: shimmer or fade; shimmer direction: ltr / rtl.
- Colors: base and highlight.
- Speed: animation duration (seconds).
- Auto Apply Skeleton: enable automatic detection for supported widgets.
Auto Apply — How it Works
- Scans only grids marked with
data-hw-skeleton-auto="yes". - Matches widget types via a small registry and assigns skeleton classes to inner parts.
- Dynamic Field: if
.jet-engine-gallery-gridis found, switches toskeleton-dynamice-g-loadingand appliesskeleton-loadingper.jet-engine-gallery-grid__item-wrap. - No container‑level skeletons are added; only inner elements are decorated.
- Manual overrides are preserved; classes aren’t duplicated.
- Per‑widget opt‑out: add
data-hw-skeleton-auto="no"on a widget to exclude it. - Performance: runs within
requestAnimationFrame, debounced on resize, watches grids via MutationObserver, honors Reduced Motion.
Manual Usage — Quick Examples
- Text (Text Editor / Heading / Dynamic Field text): add
skeleton-multitext-loadingon the widget. - Icon List: add
skeleton-list-loadingon the Icon List widget. - Icon Box: add
skeleton-icon-box-loadingon the Icon Box widget. - Image Box: add
skeleton-image-box-loadingon the Image Box widget. - Icon: add
skeleton-icon-loadingon the Icon widget. - Dynamic Field — Gallery Grid: add
skeleton-dynamice-g-loadingon the widget.
Important: For any other widget not listed above, you can always add the universal
skeleton-loadingclass. This applies the loading effect to the widget wrapper as a whole (a single block skeleton) and works universally — there is no widget that “cannot” receive a skeleton. Use this when no specialized class is needed.
Notes & Tips
- You can combine helpers (
skeleton-silent-bg,skeleton-silent-border,skeleton-z-bg). - For complex stacking,
skeleton-z-bgkeeps the shimmer behind child skeletons. - If the Listing Grid item’s top‑level container has visible background/border/box‑shadow, add
skeleton-silent-bgandskeleton-silent-borderto that wrapper so only inner skeletons are visible. - For multiline text overlays, use
skeleton-multitext-loading(no need to add the universalskeleton-loading).
Per‑Widget Auto Apply Opt‑out
If you need to exclude a specific widget from Auto Apply inside a Listing Grid, open the widget in Elementor → Advanced tab → Attributes and add:
Key: data-hw-skeleton-auto
Value: no
Copy paste: data-hw-skeleton-auto|no
This prevents the JS auto‑detection from assigning skeleton classes to that widget while the grid is loading. Manual classes still work as usual.
Extending Without Modifying This Plugin
You can add compatibility for your own widgets without changing this codebase.
- Manual (no code)
- In Elementor → widget → Advanced → CSS Classes, add the skeleton classes you need.
- Front‑end shim (theme/companion plugin)
- Enqueue a small script after this plugin’s JS. It recognizes your widget(s) and adds skeleton classes during grid loading. Scope it to the loading grid only:
(function ($) {
var MAP = {
"acme-card.default": {
classes: ["skeleton-loading"],
shapes: {
".acme-card__image": "hw-skel-block",
".acme-card__title": "hw-skel-block",
".acme-card__desc": "hw-skel-block",
},
},
};
function inLoadingState($grid) {
return (
$grid &&
$grid.length &&
($grid.hasClass("jet-listing-grid-loading") ||
$grid.hasClass("hw-js-skeleton-loading"))
);
}
function getProviderGrids(provider, queryId) {
var $result = $();
if (queryId) {
var selectors = [
'.jet-listing-grid[data-hw-query-id="' + queryId + '"]',
".jet-listing-grid-" + queryId,
'.jet-listing-grid[data-query-id="' + queryId + '"]',
];
$result = $(selectors.join(", ")).filter(".jet-listing-grid");
if (!$result.length) {
var $items = $(
'.jet-listing-grid__items[data-hw-query-id="' +
queryId +
'"], .jet-listing-grid__items[data-query-id="' +
queryId +
'"]'
);
if ($items.length) $result = $items.closest(".jet-listing-grid");
}
}
if (!$result.length && provider) {
var candidate =
provider.$provider ||
provider.container ||
provider.$container ||
provider;
var $c = candidate ? $(candidate) : $();
if ($c.length)
$result = $c.is(".jet-listing-grid")
? $c
: $c.closest(".jet-listing-grid");
}
return $result;
}
function applyCompat(root) {
var $roots = $(root || document);
var $grids = $roots.filter(".jet-listing-grid");
if (!$grids.length) $grids = $roots.find(".jet-listing-grid");
if (!$grids.length) return;
$grids.each(function () {
var $grid = $(this);
if (!inLoadingState($grid)) return;
Object.keys(MAP).forEach(function (type) {
var cfg = MAP[type];
$grid.find('[data-widget_type="' + type + '"]').each(function () {
var $w = $(this);
(cfg.classes || []).forEach(function (cls) {
if (cls && !$w.hasClass(cls)) $w.addClass(cls);
});
if (cfg.shapes) {
Object.keys(cfg.shapes).forEach(function (sel) {
$w.find(sel).addClass(cfg.shapes[sel]);
});
}
});
});
});
}
$(function () {
applyCompat($(".jet-listing-grid"));
});
$(document).on(
"jet-engine/listing/ajax-get-listing/done",
function (_e, $html) {
var $scope = $html && $html.length ? $html : $(document);
var $grid = $scope.closest(".jet-listing-grid");
applyCompat($grid.length ? $grid : $scope);
}
);
$(document).on(
"jet-engine/listing-grid/after-load-more",
function (_e, inst) {
var $scope = inst && inst.container ? $(inst.container) : $(document);
applyCompat($scope);
}
);
var bus =
(window.JetSmartFilters &&
(window.JetSmartFilters.events ||
window.JetSmartFilters.eventBus ||
window.JetSmartFilters.bus)) ||
null;
if (bus && typeof bus.subscribe === "function") {
try {
bus.subscribe("ajaxFilters/start-loading", function (provider, queryId) {
var $grids = getProviderGrids(provider, queryId);
applyCompat($grids.length ? $grids : $(".jet-listing-grid"));
});
bus.subscribe("ajaxFilters/end-loading", function (provider, queryId) {
var $grids = getProviderGrids(provider, queryId);
applyCompat($grids.length ? $grids : $(".jet-listing-grid"));
});
} catch (e) {}
}
})(jQuery);
Notes:
- The shim only adds classes; teardown is not required (grid loading toggle handled by JetEngine/JSF).
- Use the same inner selectors you target in your CSS for per‑element shapes.
- If the wrapper background or border bleeds, add
skeleton-silent-bg/skeleton-silent-borderon that wrapper.
- Server‑side (template)
- Output skeleton classes directly in your widget template if you prefer a no‑JS approach.