Skip to content

Site list scrolls to top on every delete — fixing the missing keepScroll argument across 6 call sites

Site list scrolls to top on every delete — fixing the missing keepScroll argument across 6 call sites

The symptom

Every time a site was deleted from the WordPress Maintenance Manager site list, the page jumped back to the very top. In environments managing many sites, removing an item in the middle of the list forced users to scroll back down before they could act on the next one.

The same behavior occurred in five other operations: drag-and-drop reordering, thumbnail fetching (running behind an open modal), category deletion, tag deletion, and after maintenance completion.

Root cause: omitting the keepScroll argument

The fetchSites() function accepts two arguments: keepPage and keepScroll. When keepScroll=true is passed, the scrollTo({top: 0}) call inside renderPage is suppressed. This mechanism was implemented in v1.6.6 and worked correctly.

async function fetchSites(keepPage = false, keepScroll = false) {
  // ...
  renderPage(keepPage, keepScroll);
}

function renderPage(keepPage, keepScroll) {
  if (!keepScroll) {
    window.scrollTo({ top: 0, behavior: 'smooth' });
  }
  // ...
}

The scroll-preservation logic itself was fine. The problem was that none of the six call sites that refresh the list after an operation were passing keepScroll=true. They all omitted the second argument, so it defaulted to false, and the scroll-to-top fired every time.

// Before: keepScroll omitted, defaults to false — scrolls to top
fetchSites(true);

// After: keepScroll=true explicitly passed
fetchSites(true, true);

Six call sites, one fix each

The same omission existed in six places:

Location Before After
deleteSiteByIndex (site delete) fetchSites(true) fetchSites(true, true)
D&D reorder completion fetchSites(true) fetchSites(true, true)
Thumbnail fetch (behind modal) fetchSites(true) fetchSites(true, true)
Category deletion fetchSites(true) fetchSites(true, true)
Tag deletion fetchSites(true) fetchSites(true, true)
Maintenance completion fetchSites(true) fetchSites(true, true)

The thumbnail case was particularly subtle: the refresh fires asynchronously in the background while a modal is open, so the list behind it would quietly scroll to the top with no visible cause. The maintenance completion case had a separate mechanism that suppressed scroll-to-top during streaming, but the final fetchSites call on completion was inconsistent with that intent. This fix aligns all six sites.

Initial load is intentionally excluded

The very first fetchSites() call on app startup is left unchanged. Resetting to page 1 and scroll position 0 on initial load is the correct behavior — applying keepScroll=true there would be wrong.

The filterSites function uses Math.min clamping so that if the current page falls out of range after a deletion, the list renders safely without a crash.

What to take away

This was not a broken feature — the scroll-preservation mechanism worked exactly as designed. The issue was that a safety behavior added in v1.6.6 was not carried forward into the call sites added afterward.

When new operations were implemented (thumbnail fetch, category and tag deletion, maintenance completion), each one wrote its own fetchSites call without checking how existing call sites were passing arguments. The result was valid-looking code that silently ignored an established convention.

Checking how existing callers pass arguments to a function before adding a new call site is a practical way to catch this category of omission before it ships.


Related: Site list UX: three improvements in one release