New V11 GridStackWidget.lazyLoad feature. open console and see widget content (or angular components) created as they become visible.
+
+
+
+
+
+
+
diff --git a/doc/CHANGES.md b/doc/CHANGES.md
index 2c81b5ce5..bb22e30f5 100644
--- a/doc/CHANGES.md
+++ b/doc/CHANGES.md
@@ -119,6 +119,7 @@ Change log
* fix: [#2231](https://github.com/gridstack/gridstack.js/bug/2231),[#1840](https://github.com/gridstack/gridstack.js/bug/1840),[#2354](https://github.com/gridstack/gridstack.js/bug/2354)
big overall to how we do sidepanel drag&drop helper. see release notes.
* feat: [#2818](https://github.com/gridstack/gridstack.js/pull/2818) support for Angular Component hosting true sub-grids (that size according to parent) without requring them to be only child of grid-item-content.
+* feat: Lazy loading of widget content until visible (`GridStackOptions.lazyLoad` and `GridStackWidget.lazyLoad`)
## 10.3.1 (2024-07-21)
* fix: [#2734](https://github.com/gridstack/gridstack.js/bug/2734) rotate() JS error
diff --git a/doc/README.md b/doc/README.md
index 478b7537c..a85747497 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -106,6 +106,7 @@ gridstack.js API
- `handle` - draggable handle selector (default: `'.grid-stack-item-content'`)
- `handleClass` - draggable handle class (e.g. `'grid-stack-item-content'`). If set `handle` is ignored (default: `null`)
- `itemClass` - widget class (default: `'grid-stack-item'`)
+- `lazyLoad?`: boolean - true when widgets are only created when they scroll into view (visible). also overridable per widget in `GridStackWidget`
- `margin` - gap size around grid item and content (default: `10`). Can be:
* an integer (px)
* a string (ex: '2em', '20px', '2rem')
@@ -155,7 +156,7 @@ most of the above options are also available as HTML attributes using the `gs-`
Extras:
- `gs-current-row` - (internal) current rows amount. Set by the library only. Can be used by the CSS rules.
-## Item Options
+## Item Options - GridStackWidget
options you can pass when calling `addWidget()`, `update()`, `load()` and many others
@@ -174,6 +175,7 @@ You need to add `noResize` and `noMove` attributes to completely lock the widget
Note: This also allow you to set a maximum h value (but user changeable during normal resizing) to prevent unlimited content from taking too much space (get scrollbar)
- `subGrid`?: GridStackOptions - optional nested grid options and list of children
- `subGridDynamic`?: boolean - enable/disable the creation of sub-grids on the fly by dragging items completely over others (nest) vs partially (push). Forces `DDDragOpt.pause=true` to accomplish that.
+- `lazyLoad?`: boolean - true when widgets are only created when they scroll into view (visible). also optin on entire grid.
## Item attributes
diff --git a/src/gridstack.ts b/src/gridstack.ts
index aa906763c..43c885edd 100644
--- a/src/gridstack.ts
+++ b/src/gridstack.ts
@@ -461,12 +461,13 @@ export class GridStack {
let el: GridItemHTMLElement;
let node: GridStackNode = w;
+ node.grid = this;
if (node?.el) {
el = node.el; // re-use element stored in the node
} else if (GridStack.addRemoveCB) {
el = GridStack.addRemoveCB(this.el, w, true, false);
} else {
- el = Utils.createWidgetDivs(this.opts.itemClass, w);
+ el = Utils.createWidgetDivs(this.opts.itemClass, node);
}
if (!el) return;
diff --git a/src/types.ts b/src/types.ts
index 2e1092ca2..c4d8079d4 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -191,6 +191,9 @@ export interface GridStackOptions {
/** additional widget class (default?: 'grid-stack-item') */
itemClass?: string;
+ /** true when widgets are only created when they scroll into view (visible) */
+ lazyLoad?: boolean;
+
/**
* gap between grid item and content (default?: 10). This will set all 4 sides and support the CSS formats below
* an integer (px)
@@ -331,6 +334,8 @@ export interface GridStackWidget extends GridStackPosition {
id?: string;
/** html to append inside as content */
content?: string;
+ /** true when widgets are only created when they scroll into view (visible) */
+ lazyLoad?: boolean;
/** local (vs grid) override - see GridStackOptions.
* Note: This also allow you to set a maximum h value (but user changeable during normal resizing) to prevent unlimited content from taking too much space (get scrollbar) */
sizeToContent?: boolean | number;
@@ -447,4 +452,6 @@ export interface GridStackNode extends GridStackWidget {
_removeDOM?: boolean;
/** @internal had drag&drop been initialized */
_initDD?: boolean;
+ /** @internal allow delay creation when visible */
+ _visibleObservable?: IntersectionObserver;
}
diff --git a/src/utils.ts b/src/utils.ts
index 095e4323a..2bf633324 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -110,19 +110,29 @@ export class Utils {
return els;
}
- /** create the default grid item divs */
- static createWidgetDivs(itemClass?: string, w?: GridStackWidget): HTMLElement {
+ /** create the default grid item divs, and content possibly lazy loaded calling GridStack.renderCB */
+ static createWidgetDivs(itemClass: string, n: GridStackNode): HTMLElement {
const el = Utils.createDiv(['grid-stack-item', itemClass]);
- Utils.createDiv(['grid-stack-item-content'], el, w);
+ const cont = Utils.createDiv(['grid-stack-item-content'], el);
+
+ const lazyLoad = n.lazyLoad || n.grid?.opts?.lazyLoad && n.lazyLoad !== false;
+ if (lazyLoad) {
+ n._visibleObservable = new IntersectionObserver(([entry]) => { if (entry.isIntersecting) {
+ n._visibleObservable.disconnect();
+ delete n._visibleObservable;
+ GridStack.renderCB(cont, n)
+ }});
+ window.setTimeout(() => n._visibleObservable.observe(el)); // wait until callee sets position attributes
+ } else GridStack.renderCB(cont, n);
+
return el;
}
- /** create a div (possibly 2) with the given classes */
- static createDiv(classes: string[], parent?: HTMLElement, w?: GridStackWidget): HTMLElement {
+ /** create a div with the given classes */
+ static createDiv(classes: string[], parent?: HTMLElement): HTMLElement {
const el = document.createElement('div');
classes.forEach(c => {if (c) el.classList.add(c)});
parent?.appendChild(el);
- if (w) GridStack.renderCB(el, w);
return el;
}