Skip to content

Group Containers

Group containers let a set of elements act as an independent drag scope: a node dragged within its group reorders only among that group’s own siblings, and a drop outside the group is rejected by default.

Configuration

const mosaic = new Mosaic({
root: document.getElementById("board"),
selectors: {
node: ".card",
group: ".lane"
}
});
mosaic.initialize();
<div id="board">
<div class="lane" id="todo">
<div class="card" data-mosaic-id="a">A</div>
</div>
<div class="lane" id="done">
<div class="card" data-mosaic-id="b">B</div>
</div>
</div>
  • selectors.group — elements that scope drag behavior for their descendants

When selectors.group is omitted entirely, groups have no effect at all — the whole root behaves as one flat drag scope, the pre-group baseline.

Scoping Rules

At pointerdown, MosaicJS resolves the dragged node’s own group container (its nearest ancestor matching selectors.group, bounded by root) and tracks it for the duration of the drag. At drop time, checkConstraints() compares that origin group against the resolved drop target’s own group: if they differ, the drop is rejected with reason "group-boundary"unless MosaicOptions.crossGroupDrag is enabled.

const mosaic = new Mosaic({
root,
selectors: { node: ".card", group: ".lane" },
crossGroupDrag: true // allow nodes to move between lanes
});

Two ungrouped nodes (no selectors.group configured at all, or a node with no group ancestor even when it is configured) are treated as belonging to the same (null) group — group-boundary rejection never fires for a Mosaic that never configured selectors.group.

Cross-Group Hover

With crossGroupDrag enabled, MosaicJS tracks which group container is currently under the pointer independently of the node/drop-target hover pair, and emits a dedicated pair of events as the pointer crosses group boundaries:

window.addEventListener("mosaic:group:enter", (e) => {
const { groupId } = e.detail; // the entered group's DOM id
});
window.addEventListener("mosaic:group:leave", (e) => {
const { groupId } = e.detail;
});

These fire only for groups other than the dragged node’s own origin group — re-entering the group you started in emits neither event.

Styling

Two CSS classes support group-aware styling (default names, both overridable via MosaicOptions.cssClasses):

ClassApplied toWhen
groupActive (mosaic--group-active)the dragged node’s own origin group containerfor the duration of the drag
groupHover (mosaic--group-hover)a non-origin group containerwhile the pointer is over it, cross-group mode only
.mosaic--group-active {
outline: 1px solid;
}
.mosaic--group-hover {
background: rgba(0, 0, 0, 0.05);
}
  • Drop Targets — a second, independent way to scope drag destinations; the two combine (a group can contain nested drop targets)
  • Drag Lifecycle — the overall pointer-event flow this scoping logic runs inside of
  • Constraints Design — the full set of built-in rejection reasons, including "group-boundary"
  • Constraint Scoping Across Groups and Containers — how to register user-defined constraints on top of this page’s built-in group-boundary check, and how cross-container constraints (a separate concept — linked Mosaic instances, not groups within one instance) differ