Getting and Setting scroll Position of content_panel

Continuing the discussion from How to prevent scrolling when updating a repeating panel:

What I’m trying to do:
I am trying to get the scroll position of the scroll bar for the content_panel loaded into Legacy Material Template. The idea being that when we navigate away from the form loaded into the content_panel and then go back (via anvil_extras.routing) we can get the window to scroll to the correct position that we left in.

What I’ve tried and what’s not working:
return $(window).scrollTop(); always returns 0

I think I need to replace window with the content_panel element but have no idea how to go about that? Have tried using the domNode but … JS + HTML = :face_vomiting:

Code Sample:

<Native Libraries>
<script>
function getScrollPosition() {
  return $(window).scrollTop();
}
function restoreScrollPosition(scrollPosition) {
  $(window).scrollTop(scrollPosition);
}
</script>

The scroll position is saved to the form and when form show is called it calls the `restoreScrollPostion()’

The issue I have is that the return $(window).scrollTop(); always returns 0so I" am assuming I am getting the scroll value of the wrong element?

Clone link:
Can build if required.

After a bit of trial and error and looking at chrome dev tools, the content panel has a selector of ‘.nav-holder’ so changing window to '.nav-holder' made this work

Not really. I was wrong.

I first made the app and tested and it was working with the selector '.content'.
Then users started to complain, I changed the selector to '.nav-holder' and it was finally working.
Then other complaints, so back to '.content'.
Then I decided to stop being lazy and take some time to test on my computer, and noticed that in debug was one selector and in production the other, so I kept the production one.
Then users started to complain, so I switched selector again.
Then other users started to complain… and finally noticed an obvious pattern: Edge vs Chrome!
So I used one selector for Edge and one for Chrome, and everybody was finally happy.
Until someone wasn’t. It wasn’t Edge vs Chrome.

Then I spent more time, much more, and finally understood that production/development or split/no split or Edge/Chrome had nothing to do with it.

The problem was this in theme.css:

.nav-holder {
  display: flex;
}

@media(max-width:998px) {
  html:not(.designer) .nav-holder {
    display: block;
  }

This causes the scrollbar to switch between '.content' and '.nav-holder' when the window width crosses the 998px threshold.

It turns out that when I debug in split mode I usually have a smaller window and when I do the final test in production I usually have a larger window.
And it turns out that most of our users in the shop use Edge in small tablets, while in the office use Chrome in large monitors.
And it turns out that all of those patterns were just hiding the real cause of the problem: css media queries.

Here is the final solution:

def maintain_scroll_position():
    def wrap(func):
        def wrapped_func(*args, **kwargs):
            selector = '.nav-holder' if _S('.nav-holder').css('display') == 'block' else '.content'
            anvil.get_open_form().call_js('getScrollPosition', selector)
            res = func(*args, **kwargs)
            anvil.get_open_form().call_js('restoreScrollPosition', selector)
            return res
        return wrapped_func
    return wrap

I used this little function to compare all the style attributes of each selector while the window is large and while the window is small. I would print it to the app console, then double click on it to select the whole string, paste it on a text editor, change the window size, repeat, then diff the two text editors to quickly highlight the changes.

def all_css(selector):
    from anvil.js.window import jQuery as _S
    return '\t'.join([f'{a}: {_S(selector).css(a)}' for a in dir(_S(selector)[0].style)  if _S(selector).css(a)])

# make the window wide
print(all_css('.content'))
# make the window narrow
print(all_css('.content'))
# check the diff

# make the window wide
print(all_css('.nav-holder'))
# make the window narrow
print(all_css('.nav-holder'))
# check the diff

I’m posting about this little thing just because it costed me and my users way more headaches than it should have, and this could help someone else’s headache go away.

2 Likes