I'm trying to use the event handler for onscrollend (https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollend_event). However, I'm getting "Warning: Unknown event handler property onscrollend. It will be ignored." Has anyone come up against this before? onscroll works as expected e.g [:div {:onscroll #(function)}]. The issue that I'm trying to solve is that I have very large clickable buttons inside a scrollable element. Every so often on touchscreens the on-click function is being triggered on these when scrolling. I can disable them using the onscroll and want to re-enable them using onscrollend. Seems like the simplest solution to the issue.
Somewhat off topic: how come react does not simply accept events by their name? Why do new events need to be defined in react to work?
I'm attempting the debounce method like in the first answer here https://stackoverflow.com/questions/66139456/click-event-is-triggered-by-scrolling. Trying to hold a variable high while the event is happening. However, it's not working too well, i.e it still fires off the on-click fn-a while scrolling. I think I don't understand the interop around making interval functions work within reagent's atoms - here's the basics of my attempt-
(defonce is-scrolling? (atom false))
(defn main-menu []
(with-let [data (:data @content)
albums (all-leaves data)
timeout-fn (atom nil)
]
[:div {:style {:height "50px"}} (str @is-scrolling?)]
[:div.row {:id "main menu"
:ref (fn [el] (reset! side el)
)
:onScroll (fn [e]
(reset! is-scrolling? true)
(if @timeout-fn (do (js/clearTimeout timeout-fn) (reset! timeout-fn nil))
(reset! timeout-fn (js/setInterval #(reset! is-scrolling? false) 500))))
}
[:div
{:onclick (fn []
(if (false? @is-scrolling?)
(fn-a)
(fn-b)
))}
"CONTENT"]]
(finally (js/clearInterval @timeout-fn))
)
)> Why do new events need to be defined in react to work? @brjann I don't know for certain, but maybe because React wraps events in its own machinery (e.g. each event handled by React is wrapped in a synthetic event).
@jcb A few comments:
• Use global atoms only for truly global things, like app-wide state. The internal state of a component should be defined in that component. is-scrolling? sounds like a component's internal state
• Don't use :refer, use :as, so e.g. with [reagent.core :as r] you can use r/with-let. Helps immensely with seeing what's what at a glance, especially those atom calls (I have no idea whether those are from Reagent or CLJS)
• js/clearTimeout receives a timeout ID, but you're feeding it an atom
• js/setInterval should be used in tandem with js/clearInterval, not js/clearTimeout
• But it seems that you actually need to use js/setTimeout here
• No need for that finally unless you want is-scrolling? to remain true if the component is unmounted for some reason
Some minor things:
• You can use 50 instead of "50px"
• No need for that false? - you can use if-not or regular if if you swap the branches
• You can use :on-scroll and :on-click for event names to make them more CLJ-ey
• The code might be a tiny bit simpler if you use https://google.github.io/closure-library/api/goog.async.Debouncer.html. But I myself would also use js/setTimeout here, probably
Sorry, yes some of these mistakes crept in while trying to create a basic example without all the layout context.
"`js/clearTimeout` receives a timeout ID, but you're feeding it an atom" I thought that would just point to what was inside the atom?
"Don't use :refer, use :as, so e.g. with [reagent.core :as r] you can use r/with-let. Helps immensely with seeing what's what at a glance, especially those atom calls (I have no idea whether those are from Reagent or CLJS)"
They are reagent atoms at the moment
> I thought that would just point to what was inside the atom?
If you deref the atom, yes. But in that code above, there's no deref or @: (js/clearTimeout timeout-fn).
scrollend got added to the spec very recently.
React added support for it in its code just four months ago: https://github.com/facebook/react/commit/537228f9fd703d18bea1f6d20fa0e5006b795c42
To use that event with React (assuming your target browsers don't include Safari that still doesn't support it), you'd have to use an unreleased version of React.
Thanks, is this the case even using a longhand event listener?
If you mean onScrollEnd then yes, it's the same thing.
If only partial support from modern browsers doesn't bother you, you can add an event handler for the scrollend event imperatively via a ref. A caveat - you'll have to handle any changes to the handler yourself.
I'm not sure I follow, but thanks for your help
Do you mean adding it inside the ref function? when it is mounted?
Is it possible in reagent to track an event so that it changes an atom when it is happening and after it has fired? Or do I need to manually debounce the scroll event with a timeout?
> Do you mean adding it inside the ref function? when it is mounted?
Yes. But if you ever decide to change the event handler without re-mounting the component, you need to do it manually. IMO the best way is to use an event handler object instead of a function and just replace its handleEvent attribute with the new handler.
> Is it possible in reagent to track an event so that it changes an atom when it is happening and after it has fired?
Sorry, no idea what you mean.
That’s interesting, I can’t find any examples of this in the reagent or react docs, do you have a link to an example please?
No clue. It's just a combination of two things - React refs and the fact that an event handler can be an object with a handleEvent property.
Probably something like this (haven't tested):
(defn my-component [{:keys [on-scroll-end]}]
(let [handler #js {:handleEvent on-scroll-end}]
(r/create-class
{:display-name "my-component"
:component-did-update (fn [this & _] (set! handler -handleEvent (:on-scroll-end (r/props this))))
:reagent-render (fn [_] ...)})))Thank you, I was really just referring to the eventhandler object as everything I see suggests a function . I’ll take a look at this cheers
Yeah, it's documented here: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#listener