Elm-Scroll - An Effect Manager for Scroll Events

I got inquisitive on a plane ride this morning, and hacked together a prototype of an effect manager to listen to “scroll” events, without using ports or native code.

Find it here.

Documentation can be with this

This is the roughest of prototypes, you subscript to the scroll function and it returns a record with the x and y coordinates.

Under the hood it uses Dom.LowLevel’s onWindow function to create the event listener.

It’s basically hacked together from Window.resize and Mouse.downs. But this was just my “will it work?” attempt.

So far it works great for me in Safari and in Firefox, but is jumpy in Chrome. Haven’t tested on windows or mobile.

Obvious additions would be:

  1. Either direction of the scroll, or a diff from the last event

  2. Get it to work better on Chrome.

  3. See if it works on every modern, major browser.

  4. Try to break it.

  5. Clean up and simplify the code.

  6. Write tests

Let me know what you think. Thanks.


The good news is that you quite likely won’t need an effect manager, native or ports to do this sort of thing in the next release. The bad news is that I wouldn’t advise using an effect manager to deal with this in the meantime.

So the way I’d recommend doing this sort of thing right now would be using a port. Here’s an example for dealing with wheel events: https://ellie-app.com/qqp6pVJRQa1/0

The thing is that the next release will basically have a pair of functions onWindow and onDocument, to subscribe to events on the window and the document. So in that world, I could very easily adapt the code in the above Ellie:

subscriptions : model -> Sub Msg
subscriptions _ =
  onDocument "wheel" (Decode.map Wheel wheelDecoder)

Overall, this seems like it provides the nicer path:

  • :heavy_check_mark:️ possible right now
  • :heavy_check_mark:️ easy to adapt in the future
  • :heavy_check_mark:️ no need for native/kernel/effect managers/…

In fact, in a world where onDocument and onWindow are available, a package like the one described here could be released, and the entire implementation (adapted from your code :wink: ) would be this:

scroll : (Position -> msg) -> Sub msg
scroll tagger =
    onDocument "scroll" (Json.map tagger position)

position : Json.Decoder Position
position =
    Json.map2 Position
        (Json.at [ "target", "defaultView", "scrollX" ] Json.int)
        (Json.at [ "target", "defaultView", "scrollY" ] Json.int)

Is there somewhere where we can easily read what is and isn’t in the next release?

1 Like

I may have put too much focus on the upgrade path in my reply, sorry for that.

The main point I wanted to make is that it is trivial to write a subscription for scroll events using a port right now. Using only the documented and recommended tools will always result in a smoother upgrade path in the long run. Since this can be (quite trivially) implemented with those tools, that is my strong recommendation.

1 Like

The point for me was largely learning exercise. It was a notion I had and wasnted to see if my idea would work and it seems like it did. I also wanted to understand effect managers more, which have been frustrating to understand for me.

But if we’re approaching 0.19 closely enough that it’s not even worth working on API designs that may change, that maybe there’s somewhere we could find that info out. Apart from native, is there stuff we shouldn’t be working on?

1 Like

Dan, if you do write a ports version, I would be very interested in using it to fix a deficiency in http://www.knode.io

– Jim

I’m probably not going to write a ports version, as it’s almost harder to setup via npm than to do as a one off, but I’d be happy to see if I can help 1 on 1.


Moderation note:

Folks, let’s keep this thread about the original topic and not about opinions about ports.

Chrome seems to be slower (and possibly more sporadic) about sending scroll events. We have code that tries to only render what’s visible and Chrome is much more prone than other browsers to scrolling into unrendered territory. Usually it fills in eventually.


1 Like

That’s exactly what I’m finding.

Since this was a learning exercise, even though we now know that onWindow and onDocument will just fire events in 0.19, I’d still love some feedback on API design, if anyone is interested (not that this is the most crazy design in the world).

The biggest question I have, if this was to be implemented as an Effect Manager, should we provide the direction the scroll is heading, either as a union type (UpRight, DownNeutral, etc.), a tuple of union types ((Up, Right) or (Down, Neutral) etc.) or even as a tuple of diffs: (-12, 6).

Personally, I would love to see deltas as speeds — in other words, it’s scrolling at this many pixels per second or per frame or per whatever — since that gives one the direction and allows one to predict where the scroll position is going to be at the next frame update.



On chrome you will have better chances using a requestAnimationFrame then get the scroll height. The wheel event is kind of unreliable in terms of timings and sizes.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.