diff --git a/doc/API.md b/doc/API.md
index 3a059a010..7e8e40271 100644
--- a/doc/API.md
+++ b/doc/API.md
@@ -4316,7 +4316,7 @@ Note: Use enable()/disable() methods to change state as other operations need to
new DDDroppable(el, option): DDDroppable;
```
-Defined in: [dd-droppable.ts:27](https://github.com/adumesny/gridstack.js/blob/master/src/dd-droppable.ts#L27)
+Defined in: [dd-droppable.ts:29](https://github.com/adumesny/gridstack.js/blob/master/src/dd-droppable.ts#L29)
###### Parameters
@@ -4341,7 +4341,7 @@ Defined in: [dd-droppable.ts:27](https://github.com/adumesny/gridstack.js/blob/m
destroy(): void;
```
-Defined in: [dd-droppable.ts:70](https://github.com/adumesny/gridstack.js/blob/master/src/dd-droppable.ts#L70)
+Defined in: [dd-droppable.ts:76](https://github.com/adumesny/gridstack.js/blob/master/src/dd-droppable.ts#L76)
Destroy this drag & drop implementation and clean up resources.
Removes all event handlers and clears internal state.
@@ -4360,7 +4360,7 @@ Removes all event handlers and clears internal state.
disable(forDestroy): void;
```
-Defined in: [dd-droppable.ts:57](https://github.com/adumesny/gridstack.js/blob/master/src/dd-droppable.ts#L57)
+Defined in: [dd-droppable.ts:63](https://github.com/adumesny/gridstack.js/blob/master/src/dd-droppable.ts#L63)
Disable this drag & drop implementation.
Subclasses should override to perform additional cleanup.
@@ -4385,7 +4385,7 @@ Subclasses should override to perform additional cleanup.
drop(e): void;
```
-Defined in: [dd-droppable.ts:146](https://github.com/adumesny/gridstack.js/blob/master/src/dd-droppable.ts#L146)
+Defined in: [dd-droppable.ts:152](https://github.com/adumesny/gridstack.js/blob/master/src/dd-droppable.ts#L152)
item is being dropped on us - called by the drag mouseup handler - this calls the client drop event
@@ -4405,7 +4405,7 @@ item is being dropped on us - called by the drag mouseup handler - this calls th
enable(): void;
```
-Defined in: [dd-droppable.ts:44](https://github.com/adumesny/gridstack.js/blob/master/src/dd-droppable.ts#L44)
+Defined in: [dd-droppable.ts:50](https://github.com/adumesny/gridstack.js/blob/master/src/dd-droppable.ts#L50)
Enable this drag & drop implementation.
Subclasses should override to perform additional setup.
@@ -4424,7 +4424,7 @@ Subclasses should override to perform additional setup.
off(event): void;
```
-Defined in: [dd-droppable.ts:40](https://github.com/adumesny/gridstack.js/blob/master/src/dd-droppable.ts#L40)
+Defined in: [dd-droppable.ts:46](https://github.com/adumesny/gridstack.js/blob/master/src/dd-droppable.ts#L46)
Unregister an event callback for the specified event.
@@ -4448,7 +4448,7 @@ Unregister an event callback for the specified event.
on(event, callback): void;
```
-Defined in: [dd-droppable.ts:36](https://github.com/adumesny/gridstack.js/blob/master/src/dd-droppable.ts#L36)
+Defined in: [dd-droppable.ts:42](https://github.com/adumesny/gridstack.js/blob/master/src/dd-droppable.ts#L42)
Register an event callback for the specified event.
@@ -4500,7 +4500,7 @@ Result from the callback function, if any
updateOption(opts): DDDroppable;
```
-Defined in: [dd-droppable.ts:77](https://github.com/adumesny/gridstack.js/blob/master/src/dd-droppable.ts#L77)
+Defined in: [dd-droppable.ts:83](https://github.com/adumesny/gridstack.js/blob/master/src/dd-droppable.ts#L83)
Method to update the options and return the DD implementation
@@ -4523,8 +4523,9 @@ Method to update the options and return the DD implementation
| Property | Modifier | Type | Default value | Description | Defined in |
| ------ | ------ | ------ | ------ | ------ | ------ |
| `accept` | `public` | (`el`) => `boolean` | `undefined` | - | [dd-droppable.ts:25](https://github.com/adumesny/gridstack.js/blob/master/src/dd-droppable.ts#L25) |
-| `el` | `public` | `HTMLElement` | `undefined` | The HTML element being extended | [dd-droppable.ts:27](https://github.com/adumesny/gridstack.js/blob/master/src/dd-droppable.ts#L27) |
-| `option` | `public` | [`DDDroppableOpt`](#dddroppableopt) | `{}` | The drag & drop options/configuration | [dd-droppable.ts:27](https://github.com/adumesny/gridstack.js/blob/master/src/dd-droppable.ts#L27) |
+| `el` | `public` | `HTMLElement` | `undefined` | The HTML element being extended | [dd-droppable.ts:29](https://github.com/adumesny/gridstack.js/blob/master/src/dd-droppable.ts#L29) |
+| `eventEl` | `protected` | `HTMLElement` | `undefined` | - | [dd-droppable.ts:27](https://github.com/adumesny/gridstack.js/blob/master/src/dd-droppable.ts#L27) |
+| `option` | `public` | [`DDDroppableOpt`](#dddroppableopt) | `{}` | The drag & drop options/configuration | [dd-droppable.ts:29](https://github.com/adumesny/gridstack.js/blob/master/src/dd-droppable.ts#L29) |
***
diff --git a/doc/CHANGES.md b/doc/CHANGES.md
index 33c37927f..29b267334 100644
--- a/doc/CHANGES.md
+++ b/doc/CHANGES.md
@@ -144,6 +144,7 @@ Change log
* NEW: vue wrapper: brand new wrapper follow same pattern as well
* fix: [#3154](https://github.com/gridstack/gridstack.js/issues/3154) Esc key doesn't restore subgrid
* fix: [#3231](https://github.com/gridstack/gridstack.js/issues/3231) drag broken in Firefox 147.0.4+ and Chrome 144+ due to `navigator.maxTouchPoints > 0` now returning true on desktop (macOS trackpad)
+* fix: [#3291](https://github.com/gridstack/gridstack.js/pull/3291) flicker nested grid demo when dragging between sub-grids
## 12.6.0 (2026-04-08)
* feat: [#3250](https://github.com/gridstack/gridstack.js/pull/3250) full RTL support - thank you [Daniel Cohen Gindi](https://github.com/danielgindi)
diff --git a/src/dd-droppable.ts b/src/dd-droppable.ts
index 4ea7d4f95..a79369031 100644
--- a/src/dd-droppable.ts
+++ b/src/dd-droppable.ts
@@ -24,11 +24,17 @@ export class DDDroppable extends DDBaseImplement implements HTMLElementExtendOpt
public accept: (el: HTMLElement) => boolean;
+ protected eventEl: HTMLElement;
+
constructor(public el: HTMLElement, public option: DDDroppableOpt = {}) {
super();
// create var event binding so we can easily remove and still look like TS methods (unlike anonymous functions)
this._mouseEnter = this._mouseEnter.bind(this);
this._mouseLeave = this._mouseLeave.bind(this);
+ // For sub-grids the .grid-stack element is inset from the .grid-stack-item cell by the item
+ // margins (--gs-item-margin-*). Register on the outer .grid-stack-item instead so adjacent
+ // cells are gapless — moving from one sub-grid to a sibling never passes through dead space.
+ this.eventEl = (this.el.closest('.grid-stack-item') as HTMLElement) || this.el;
this.enable();
this._setupAccept();
}
@@ -46,11 +52,11 @@ export class DDDroppable extends DDBaseImplement implements HTMLElementExtendOpt
super.enable();
this.el.classList.add('ui-droppable');
this.el.classList.remove('ui-droppable-disabled');
- this.el.addEventListener('mouseenter', this._mouseEnter);
- this.el.addEventListener('mouseleave', this._mouseLeave);
+ this.eventEl.addEventListener('mouseenter', this._mouseEnter);
+ this.eventEl.addEventListener('mouseleave', this._mouseLeave);
if (isTouch) {
- this.el.addEventListener('pointerenter', pointerenter);
- this.el.addEventListener('pointerleave', pointerleave);
+ this.eventEl.addEventListener('pointerenter', pointerenter);
+ this.eventEl.addEventListener('pointerleave', pointerleave);
}
}
@@ -59,11 +65,11 @@ export class DDDroppable extends DDBaseImplement implements HTMLElementExtendOpt
super.disable();
this.el.classList.remove('ui-droppable');
if (!forDestroy) this.el.classList.add('ui-droppable-disabled');
- this.el.removeEventListener('mouseenter', this._mouseEnter);
- this.el.removeEventListener('mouseleave', this._mouseLeave);
+ this.eventEl.removeEventListener('mouseenter', this._mouseEnter);
+ this.eventEl.removeEventListener('mouseleave', this._mouseLeave);
if (isTouch) {
- this.el.removeEventListener('pointerenter', pointerenter);
- this.el.removeEventListener('pointerleave', pointerleave);
+ this.eventEl.removeEventListener('pointerenter', pointerenter);
+ this.eventEl.removeEventListener('pointerleave', pointerleave);
}
}
@@ -176,4 +182,3 @@ export class DDDroppable extends DDBaseImplement implements HTMLElementExtendOpt
};
}
}
-