Debouncing event handlers in AlpineJS x-init directive

I’m trying to add scroll event listeners to x-refs (I don’t want to add them in the DOM with @scroll.debounce, because I want it to be as portable as possible).

Codepen here: https://codepen.io/skttl/pen/vYXowBY?editors=1111

<div x-data="xOverflow()" x-init="init()" style="width:50%;border:1px solid red;position:relative;overflow:hidden;">
  <div x-ref="wrapper" style="overflow-x:auto;">
  <div x-ref="content" style="width:1000px;border:1px solid blue; height:500px;">
    
    <div>wrapper.clientWidth: <span x-text="$refs.wrapper.clientWidth"></span></div>
    <div>content.clientWidth: <span x-text="$refs.content.clientWidth"></span></div>
    <div>overflow: <span x-text="overflow"></span>
  </div>
    </div>
  <div x-show="overflow" style="position:absolute;top:0;left:90%;right:0;bottom:0;background:rgba(0,0,0,.15);"></div>
</div>
function xOverflow() {
    return {
        overflow:false,

        // methods
        setFromResize() {
            this.overflow = this.$refs?.wrapper?.clientWidth < this.$refs?.content?.clientWidth
            console.log(`resize: overflow is ${this.overflow}`);
        },
        setFromScroll(e) {
            this.overflow = !(e.target.scrollLeft == this.$refs?.content?.clientWidth - this.$refs?.wrapper?.clientWidth);
            console.log(`scroll: overflow is ${this.overflow}`);
        },

        init() {
            window.addEventListener("resize", event => _.debounce(this.setFromResize(), 250));
            this.$refs?.wrapper.addEventListener("scroll", event => _.debounce(this.setFromScroll(event)));
        }
    }
}

What I am trying to detect if the clientWidth of $refs.content is larger than $refs.wrapper. When the window resizes, this should be detected again.

In addition to that, I want to show an overlay (if content is overflowing), that should be removed when scrolled to the end.

I could do it by just slapping @resize.window.debounce on the root element, and @scroll.debounce on the wrapper element. But I want this component to be portable, by just adding x-data, x-init and x-refs, without worrying about attaching event listeners.

I tried adding lodash debounce to the eventlisteners, but the functions gets called for each event, and not debounced. Check the console.log for proof.

Can anyone help me get this right?

61 thoughts on “Debouncing event handlers in AlpineJS x-init directive”

Leave a Comment