Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Infinite scroll in Elm

I am building a simple application in Elm that show just a list of divs one under the other, and I would like to add infinite scroll functionality, to add new content every time the last div of the page appears in the viewport.

Is there a way in Elm to know when a div appears in the viewport? As an alternative, in there a way to track, as a signal, the mouse scroll event?

like image 967
marcosh Avatar asked Feb 05 '16 13:02

marcosh


1 Answers

There is currently no Elm support for scroll events, so you'll have to resort to using ports. Here's a simple example.

We need a javascript function to tell us whether the last element in the list is in the view port. We can take the isElementInViewport code from this StackOverflow answer (copied here for future reference):

function isElementInViewport (el) {
    //special bonus for those using jQuery
    if (typeof jQuery === "function" && el instanceof jQuery) {
        el = el[0];
    }

    var rect = el.getBoundingClientRect();

    return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
        rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
    );
}

Let's say your HTML looks something like this:

<div class="wrapper">
  <div class="item">...</div>
  <div class="item">...</div>
</div>

Your Elm code could have a port acting as a signal to tell us whether the last item is visible.

port lastItemVisible : Signal Bool

Now you need to wire up the port code on the Javascript side of things. This code will listen to the window.onscroll event, then check to see if the last item inside the .wrapper div is visible, and send the appropriate signal.

var app = Elm.fullscreen(Elm.Main, {
  lastItemVisible: false
});

window.onscroll = function () {
  var wrapper = document.getElementsByClassName("wrapper")[0];
  var lastItem = wrapper.childNodes[wrapper.childNodes.length - 1];

  if (isElementInViewport(lastItem)) {
    app.ports.lastItemVisible.send(true);
  } else {
    app.ports.lastItemVisible.send(false);
  }
};

If you instead just wanted a signal for tracking scroll events, there is a related StackOverflow answer here.

like image 69
Chad Gilbert Avatar answered Nov 05 '22 10:11

Chad Gilbert