IdoLeshkowitz

IdoLeshkowitz

Hey everyone :waving_hand:
Im using sticky live view to render a navbar across my app. The issue is the navbar scroll position resets whenever a live redirect happens within the app. How would you recommend approaching this?

Showing Posts 1 to 5

codeanpeace

codeanpeace

Welcome!

Hmm, that’s odd – LiveView’s definitely gotten better at restoring scroll position out of the box in more recent versions, but it sounds like sticky/nested LiveViews might still be a problem.

Anyways, I’d probably stash scroll position on the client and restore it via a phx-hook on mounted rather than tracking it from the server since it’s a UI concern.

# pseudocode
Hooks.StickyNav = {
  mounted(){
    // stash scroll top
    this.el.addEventListener("scroll", e => {
      // consider debouncing
      sessionStorage.setItem("stickyNavScrollTop", this.el.scrollTop)
    })

   // restore scroll top
   scrollTop = sessionStorage.getItem("stickyNavScrollTop")
   if (scrollTop) { this.el.scrollTop = scrollTop }
  }
}

note: session storage is tied to an individual tab so local storage may be the way to go if you want to handle new tabs as well

And just a heads up, live_redirect has been deprecated for a good while now so it may be worth updating soon – especially with 1.0 around the corner!

bu6n

bu6n

I have the same problem. While the code above looks ok, it does not work because the sticky liveview is not remounted on live navigation. The updated hook is also not fired.
There is the “phx:navigate” javascript event that you can listen to with window.addEventListener("phx:navigate", <read sessionStorage>...), but this causes a flickering of the UI since liveview will have reset the scroll position before that.
Does someone have a better solution?
There’s this issue here in live_view (scroll other element than body on history navigation · Issue #2107 · phoenixframework/phoenix_live_view · GitHub) but it would be nice to have a workaround in the meantime.

dstockdale

dstockdale

I use this snippet in a few places and it works ok

Hooks.ScrollPosition = {
  mounted() {
    let top = localStorage.getItem(this.el.id);

    if (top !== null) {
      this.el.scrollTop = parseInt(top, 10);
    }

    window.addEventListener("phx:page-loading-start", _info => localStorage.setItem(this.el.id, this.el.scrollTop))
  }
}
bu6n

bu6n

Thanks for the snippet. This seems to be used when navigating between different pages (and not live navigation), right? I’m looking for something specifically for live navigation and sticky liveview. I have tried to listen to phx:navigate and phx:page-loading-start/phx:page-loading-stop, but I always get some flickering. That’s my code:

Hooks.KeepScrollPosition = {
  mounted() 
    this.el.addEventListener("scroll", (_e) => {
      sessionStorage.setItem(`panel-primary-view-scroll-position`, this.el.scrollTop);
    });
    window.addEventListener("phx:page-loading-stop", (e) => {
      const id = "panel-primary-view";
      scrollTop = sessionStorage.getItem(`${id}-scroll-position`);
      if (scrollTop) {
        const div = document.getElementById(id);
        setTimeout(() => {
          div.scrollTop = scrollTop;
        }, 0);
      }
    });
  },
};

I should obviously remove the listener at some point.I can’t get it working without setTimeout.

bu6n

bu6n

Seems like this is working for my use-case:

function debounce(func, timeout = 100) {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => {
      func.apply(this, args);
    }, timeout);
  };
}

/**
 * A hook used to keep the scroll position of elements
 * inside a sticky liveview after a live navigation.
 */

/**
 * Session storage key for restoring panel size.
 */
const SCROLL_POSITION_STORAGE_KEY = "scroll-position";

const KeepScrollPosition = {
  restoreScrollPosition(_info) {
    scrollTop = sessionStorage.getItem(`${this.el.id}-${SCROLL_POSITION_STORAGE_KEY}`);
    if (scrollTop) {
      this.el.scrollTop = scrollTop;
    }
  },

  mounted() {
    const save = debounce(() => {
      sessionStorage.setItem(`${this.el.id}-${SCROLL_POSITION_STORAGE_KEY}`, this.el.scrollTop);
    });

    this._restoreScrollPosition = this.restoreScrollPosition.bind(this);
    this.el.addEventListener("scroll", save);
    window.addEventListener("phx:page-loading-stop", this._restoreScrollPosition);
  },

  destroyed() {
    window.removeEventListener("phx:page-loading-stop", this._restoreScrollPosition);
  },
};

export { KeepScrollPosition };
— All posts loaded —

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews