The only feasible option appears to be to just wait until there aren’t any more scroll
events:
let timer;
window.addEventListener( 'scroll', () => {
clearTimeout( timer );
timer = setTimeout( () => {
callback();
}, 300 );
}, { passive: true } );
Update: Firefox 109 and Chrome 110 now have a scrollend event: https://caniuse.com/mdn-api_element_scrollend_event