Using the new InfiniteScroll hook in LiveView main

After watching @chrismccord’s excellent “The Road To LiveView 1.0” keynote, the upcoming infinite scroll features with :limit on streamed collections looked like exactly what I needed to simplify the implementation of something I’m working on. I introduced LiveView to this project a while before it was even available on Hex, so I’m happy enough to depend on main to get the new features before 0.19 gets released.

However, my phx-viewport-top and phx-viewport-bottom events don’t seem to be triggering. Apart from switching my phoenix_live_view dependency to use github and adding those attributes to an element that has phx-update="stream", is there another step that I’m missing?

This is what’s in my template, with some irrelevant things removed or renamed:

  <div>
    <ul
      id="items"
      phx-update="stream"
      phx-viewport-top="prev-page"
      phx-viewport-bottom="next-page"
    >
      <li :for={{id, item} <- @streams.items} id={id}>
        [ render item ]
      </li>
    </ul>
  </div>
1 Like

Are you sure you’re on latest main? I didn’t merge the viewport branch stuff until recently.

1 Like

Are you sure you’re on latest main? I didn’t merge the viewport branch stuff until recently.

Pretty sure – I only switched to tracking main and ran mix deps.get yesterday (I noticed that the branch was merged a few days ago). I can see Hooks.InfiniteScroll in app.js, so it looks like it’s compiling the right assets.

I’ll try creating a clean app and see if that works, and work from there. There’s almost certainly something obvious I’m missing!

OK, it works in a new app, so I’ll just have to figure out what the key difference is between that and the real one. Thanks!

1 Like

Maybe you need to clean some JavaScript cache or similar so it picks up the latest JS bundle from LiveView?

1 Like

Maybe you need to clean some JavaScript cache or similar so it picks up the latest JS bundle from LiveView?

Thanks – honoured to have two such esteemed people reply to my post :slightly_smiling_face:

I think I cleared all the caches (removed and rebuilt priv/static, and did an “empty cache and hard reload” in Chrome), but not 100% sure I got everything. I’ll keep digging!

1 Like

Update – the test liveview that worked in a new app didn’t when I dropped it into the old one, so I suspected it was something to do with the asset-building process. The old app is still using Webpack, along with an ancient version of node.js and what seems like far too many npm packages, and when I tried installing esbuild instead the events suddenly started getting through OK. Time to bite the bullet and finally switch, I guess (it had been left as-is up to now to avoid figuring out how to get sass, bootstrap etc working with esbuild).

FWIW, there is a dart_sass package that works like esbuild: GitHub - CargoSense/dart_sass: An installer for sass powered by Elixir Mix

So you keep two distinct pipelines, one for JS, and another for CSS.

1 Like

I believe the issue is with some assumptions on the InfiniteScroll hook.

Look at this two lines of code (taken from InfiniteScroll):

window.addEventListener("scroll", this.onScroll);
var scrollTop = () => document.documentElement.scrollTop || document.body.scrollTop;

In my case, where the stream is inside an absolutely positioned element, the scroll event is not triggered at the window level, and document.body.scrollTop returns always zero.

Now, if I switch both those statements to:

targetEl.addEventListener("scroll", this.onScroll);

And

var scrollTop = (targetEl) => targetEl.scrollTop;  // or something like this

Everything works.

What I believe is missing is a way of telling the hook, which container does the scrolling. Maybe something like phx-viewport-container.

5 Likes

Thanks @fceruti, that definitely looks like it’s related to the issue I was having. My liveview was inside a grid layout, which ended up with the immediate parent appearing to have a height big enough to fit its content, with the scrolling happening further up the dom. I think the culprit was actually a footer which is a LiveComponent, with the generated phx-root div somehow confusing things. I ended up reworking the layout, and rendering the <footer> element in the layout instead of the component, and eventually managed to end up with the phx-update="stream" element having 100% height and the scroll events reaching the hook correctly.

My assumption that Webpack was somehow to blame was a complete red herring, but on the plus side I now have a nice clean asset build with esbuild, dart-sass and phoenix-copy, and a much cleaner node_modules directory! Somewhat reminiscent of when I migrated the same app from brunch to webpack, and from bootstrap 4 to 5 a few years ago, only to find that the thing that was actually causing brunch to fail had been a missing semicolon in a config file somewhere :man_facepalming:t2:.

1 Like