With compose-html, how do I observe scroll offset?...
# compose-web
u
With compose-html, how do I observe scroll offset? (i'm accepting kobweb extensions as well šŸ˜€)
u
how do you suggest I expose that? wrap it into a Flow and then collectAsState?
c
Yes. Create the callback in a
DisposableEffect
so you can unregistered when the composable leaves the composition.
šŸ‘ 1
u
thanks
c
something like this
Copy code
var distance by remember { mutableDoubleStateOf(0.0) }
DisposableEffect(window) {
        val scrollListener =
            EventListener {
                distance = window.scrollY
            }
        window.addEventListener("scroll", scrollListener)

        onDispose {
            window.removeEventListener("scroll", scrollListener)
        }
}
u
wont wrapping into a Flow be more idiomatic?
c
not in ā€œpureā€ compose world. there you work with states.
u
say you wanted to share this bit, how'd you encapsulate it?
c
depends on what you want to do with the scroll event.
u
print it im just wondering what the abstraction should be if not fllw one doesnt usually need to wrap a 1st party api with DisposableEffect in order to observe some property
c
one doesnt usually need to wrap a 1st party api with DisposableEffect in order to observe some property
well. thats the purpose of side effects to make ā€œnon compose worldā€ work with compose.
u
in android scorllable column it is I believe
scrollState.scrollY
, so just a property being read
so therefore I'd expect api shape of
window.scrollY
c
thats not how compose html works. there are no API’s for anything outside compose. It’s only a modified compose runtime that creates dom elements instead if drawing to a canvas.
u
I know, i'm just thinking about the idiomatic api shape for your snippet, if it were provided by the library
since you told me to skip the Flow middleman, which youre right about
c
I see, no clue - never thought of how to wrap this in a library.
s
realistically you would only wrap this in a nicer api for bigger and specific components where internally you'd still use the code above. there is quite a big number of different event types which can be applied to all kinds of html elements, with different options and uses
u
Copy code
@Composable
fun rememberWindowScrollY(): State<Double> {
    val scrollY = remember { mutableStateOf(window.scrollY) }

    DisposableEffect(Unit) {
        val listener: (org.w3c.dom.events.Event) -> Unit = {
            scrollY.value = window.scrollY
        }
        window.addEventListener("scroll", listener)
        onDispose { window.removeEventListener("scroll", listener) }
    }

    return scrollY
}
like this? i.e. just to hide the mutable state inside and return it?
s
if you need to reuse this rather often then I guess that's fine. but what's your actual use case for listening to page scrolling? I find that I rarely need it and it's also one of the more expensive events
u
to apply some transformations to elements based on scroll (translate header away)
s
often what you actually want is the intersection observer https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API rather than calculating something based on scroll offsets
ā˜šŸ» 1
u
no I just want to slide away top bar as user scrolls down
okay ill check that out, im complete noob i dont knlw any web apis, thanks
s
well for your case the scroll event is indeed the best option
u
if I might whats the scrope of a plain remember here? does it survive refreshes?
s
nope. for that you need to use session or local storage
u
hmm but I guess scroll offset would be reset as well, right?
s
your function would reset but
window.scrollY
itself does not. it's always the exact viewport offset
so you can initially call
window.scrollY
to check the current position. it's just not possible to observe it's value outside of loops or event listeners
u
stupid question, but whats is a refresh technically, when one hits the refresh button right? not when i close and then reopen the tab
s
good question. when you close and reopen a tab, your browser might scroll by itself to your last position while a refresh usually is instant
but both will call your main function, I think you can regard both as a refresh. it's basically also the same as visiting for the first time, except that the browser remembers your scroll position (also not always though)