0

I have the following two functions...

function sizeSearch(size) {
    const container = document.querySelector('ul.size-ranks');
    const rankElems = container.querySelectorAll('li');
    rankElems.forEach(e => {
        e.style.display = 'none';
        e.classList.remove('active');
    });

    const sizeID = size.replace(/\D/g,'') + '-rank';
    const sizeElem = document.getElementById(sizeID);
    if (sizeElem) {
        sizeElem.classList.add('active');
        sizeElem.style.display = '';
    } else {
        container.insertAdjacentHTML('beforeend', '<li class="empty-search">Size not found</li>');
    }
}

function clearSizeSearch() {
    document.querySelectorAll('li.empty-search').forEach(e => e.remove());
    document.querySelectorAll('ul.size-ranks li').forEach(e => e.style.display = '');
    document.querySelector('ul.size-ranks li.active').scrollIntoView();
}

The sizeSearch() function works fine. When the clearSizeSearch() function is called, all of the <li> elements reappear as expected, and you can very briefly see the desired .active element scroll into view as expected, but then it immediately scrolls back to the top of the container element (or maybe it re-renders that way after the DOM is finished settling?).

If I manually run document.querySelector('ul.size-ranks li.active').scrollIntoView(); in the console after running clearSizeSearch(), then it works fine. That, along with the fact that I can briefly see it scroll into view when running clearSizeSearch(), makes it seem like maybe it's running before the prior commands have finished executing, but as far as I've been able to gather, forEach() is not async by default, and any commands that come after should not get run until it's done.

GlenVaughan
  • 143
  • 1
  • 3
  • 11
  • 1
    Can't repro: https://jsfiddle.net/8bnp7gsq/ If in that fiddle I scroll inside the
      (e.g to show "7"), then click the clear button, it scrolls back to the currently active value as expected. If then I type "10" (so that the "not found" message is shown and click again the button, then I have an error in the console because there is no `.active` element in such a case. But in no way I get what you're experiencing. What browser are you using? I first thought it could be [yet another reflow issue](https://stackoverflow.com/questions/47342730), but scrollIntoView should take care of everything.
    – Kaiido Aug 01 '23 at 23:26
  • I'm dumb. Project is using a 3rd party jQuery library on an ancestor element to manipulate the scrollbar. 99.9% sure the issue is related to that. Setting a small timeout before calling `scrollIntoView()` works well enough for this particular use case. – GlenVaughan Aug 02 '23 at 03:03

1 Answers1

0

Here's my solution.

function clearSizeSearch() {
    document.querySelectorAll('li.empty-search').forEach(e => e.remove());
    document.querySelectorAll('ul.size-ranks li').forEach(e => e.style.display = '');
    setTimeout(() => {
        document.querySelector('ul.size-ranks li.active').scrollIntoView();
    }, 0);
}
Diego Ammann
  • 463
  • 7
  • Were you able to reproduce the original issue on your end? Could you share a repro case? – Kaiido Aug 01 '23 at 23:26
  • Pretty sure now that the issue is related to a 3rd party jQuery library that's being used to fancy-pants the scrollbar. Setting a 0 timeout didn't work, but it looks like setting it at 100 is going to work well enough for the requirements of this particular use case. – GlenVaughan Aug 02 '23 at 03:08