Requested by: @Crabcyborg
What: `buildInit()` calls `setupSortable( 'ul.frm_sorting' )` once at page load (`js/src/admin/admin.js:11349`), which walks every sortable list in the builder and, for every field currently in it, calls `makeDroppable()` on the list and `makeDraggable()` on every child (`js/src/admin/admin.js:1043-1055`). Each call instantiates a live jQuery UI draggable or droppable widget. On a form with 1,100+ fields that's 1,100+ widgets stood up before any drag ever happens, all at initial render, and jQuery UI's ddmanager iterates every live droppable during a drag (see the `refreshDroppableOffsets` comment at `admin.js:1267-1291`, which already works around the per-mousemove version of this cost but not the initial widget count itself).
Ask: a field should not become draggable/droppable until its row is actually in the viewport. Attach `makeDraggable`/`makeDroppable` lazily via IntersectionObserver as each field's row scrolls into view, and detach (destroy the widget) when it scrolls back out — so the live widget count tracks what's on screen, not total field count. This is the same shape as #3432/#3433/#3435/#3450's defer-to-first-open pattern, but keyed on viewport visibility instead of settings-panel-open, since drag/drop applies to every field row regardless of whether its panel has ever been opened.
Once merged: the number of live draggable/droppable widgets scales with what's currently visible, not with total field count — dragging on a 1,100-field form costs roughly what dragging on a 50-field form costs today.
Requested by: @Crabcyborg
What: `buildInit()` calls `setupSortable( 'ul.frm_sorting' )` once at page load (`js/src/admin/admin.js:11349`), which walks every sortable list in the builder and, for every field currently in it, calls `makeDroppable()` on the list and `makeDraggable()` on every child (`js/src/admin/admin.js:1043-1055`). Each call instantiates a live jQuery UI draggable or droppable widget. On a form with 1,100+ fields that's 1,100+ widgets stood up before any drag ever happens, all at initial render, and jQuery UI's ddmanager iterates every live droppable during a drag (see the `refreshDroppableOffsets` comment at `admin.js:1267-1291`, which already works around the per-mousemove version of this cost but not the initial widget count itself).
Ask: a field should not become draggable/droppable until its row is actually in the viewport. Attach `makeDraggable`/`makeDroppable` lazily via IntersectionObserver as each field's row scrolls into view, and detach (destroy the widget) when it scrolls back out — so the live widget count tracks what's on screen, not total field count. This is the same shape as #3432/#3433/#3435/#3450's defer-to-first-open pattern, but keyed on viewport visibility instead of settings-panel-open, since drag/drop applies to every field row regardless of whether its panel has ever been opened.
Once merged: the number of live draggable/droppable widgets scales with what's currently visible, not with total field count — dragging on a 1,100-field form costs roughly what dragging on a 50-field form costs today.