Svebor Sorić
Svebor Sorić

Reputation: 23

Removing an Event Listener if e.target is a link

I'm using a slideshow component from our library of components which gets swipe events from a utility function that's initialized on mount. The problem I'm facing is while testing on phone I found that once you tap on a link in a slide from that slideshow, a swipe event fires along with a click.

this is the util function: `

function initSwipeEvents(el, deltaMin = 80) {
    const swipeData = {
        startX: 0,
        startY: 0,
        endX: 0,
        endY: 0,
    }
    let directionEvents = []
    el.addEventListener("touchstart", (e) => {
        const t = e.touches[0]
        swipeData.startX = t.screenX
        swipeData.startY = t.screenY
    })
    el.addEventListener("touchmove", (e) => {
        const t = e.touches[0]
        swipeData.endX = t.screenX
        swipeData.endY = t.screenY
    })
    el.addEventListener("touchend", () => {
        const deltaX = swipeData.endX - swipeData.startX
        const deltaY = swipeData.endY - swipeData.startY

        if (Math.abs(deltaX) > deltaMin) {
            if (deltaX > 0) directionEvents.push("right")
            else directionEvents.push("left")
        }
        if (Math.abs(deltaY) > deltaMin) {
            if (deltaY > 0) directionEvents.push("down")
            else directionEvents.push("up")
        }

        directionEvents.forEach((direction) =>
            el.dispatchEvent(new Event(`swipe-${direction}`))
        )

        directionEvents = []
    })
}

export default initSwipeEvents

`

This is the slideshow component: `

  <div
        class="slideshow"
        tabindex="0"
        @swipe-down="goToPrev"
        @swipe-up="goToNext"
    >
            <div
                v-show="activeSlideLogic(i, internalIdx)"
                class="slide"
            >
                <slot
                    :slide="slide"
                    name="slide"
                />
            </div>
    </div>
    <script>
         mounted() {   
        if (this.swipeEvents) initSwipeEvents(this.$el) **// swipeEvents is just a boolean prop**
        },
        methods:{
          getLoopedIdx(idx) {
            if (this.wrap)
                return (idx + this.slides.length) % this.slides.length
            else return _clamp(idx, 0, this.slides.length - 1)
          },
          goToNext() {
            this.$nextTick(
                () =>
                    (this.internalIdx = this.getLoopedIdx(this.internalIdx + 1))
            )
          },
          goToPrev() {
            this.$nextTick(
                () =>
                    (this.internalIdx = this.getLoopedIdx(this.internalIdx - 1))
            )
          },
        }
    </script>

` This is the slide components that is used in the slideshow slot:

  <div class="slide-video">
        <wp-image **// this is a custom image component, shouldn't be causing the issue**
            class="image"
            :image="image"
        >
                <nuxt-link **// this is the link I'm referencing**
                    class="title-meta"
                    :to="to"
                >
                    <div class="title-wrapper">
                        <svg-play class="svg-play" />
                        <h2
                            class="title"
                            v-html="title"
                        />
                    </div>
                </nuxt-link>
        </wp-image>
    </div>

I tried to prevent the default action by doing the folowing: `

function initSwipeEvents(el, deltaMin = 80) {
    const swipeData = {
        startX: 0,
        startY: 0,
        endX: 0,
        endY: 0,
    }
    let directionEvents = []
    el.addEventListener("touchstart", (e) => {
        const t = e.touches[0]
        swipeData.startX = t.screenX
        swipeData.startY = t.screenY
        
        // added
        console.log(e.target);
        if(  e.target.matches('.title-meta') || 
            e.target.matches('.title-meta *')){
            console.log("this is a link");
            e.preventDefault();   
        }

    })

    el.addEventListener("touchmove", (e) => {
        const t = e.touches[0]
        swipeData.endX = t.screenX
        swipeData.endY = t.screenY
        
        // added
        console.log(e.target);
        if(  e.target.matches('.title-meta') || 
            e.target.matches('.title-meta *')){
            console.log("this is a link");
            e.preventDefault();
        }

    })

    el.addEventListener("touchend", () => {
        const deltaX = swipeData.endX - swipeData.startX
        const deltaY = swipeData.endY - swipeData.startY


        if (Math.abs(deltaX) > deltaMin) {
            if (deltaX > 0) directionEvents.push("right")
            else directionEvents.push("left")
        }
        if (Math.abs(deltaY) > deltaMin) {
            if (deltaY > 0) directionEvents.push("down")
            else directionEvents.push("up")
        }

        directionEvents.forEach((direction) =>
            el.dispatchEvent(new Event(`swipe-${direction}`))
        )

        directionEvents = []
        })
    }

    export default initSwipeEvents

` I also tried the css property, touch-action: none; on the link in the slide, to no avail

I expected to be able to click on the link without triggering the swipe event. I'm not sure where to implement the change, whether on the util or on the slideshow it's self or if the change I implemented is correct.

Edit: I have tried adding

 goToNext(e) {
        console.log(e)

        this.$nextTick(
            () =>
                (this.internalIdx = this.getLoopedIdx(this.internalIdx + 1))
        )
    },
    goToPrev(e) {
        console.log(e)
        this.$nextTick(
            () =>
                (this.internalIdx =       this.getLoopedIdx(this.internalIdx - 1))
        )
    },

...on the slideshow its self, the console.log is always logging that I'm targeting the div "target: div.slideshow", even when clicking on the link. While if I log the onWheel method, which listens to the wheel event (it isn't being imported form a utilty), I', getting the expected result. I think the util might not have the correct scope it needs, as it;s bound to the $el, which is slideshow.

Upvotes: 1

Views: 127

Answers (1)

Svebor Sorić
Svebor Sorić

Reputation: 23

I found out the solution on my own. Basically what was happening is that the utility function listens for the touchStart, touchMove and touchEnd events. But if only a tap of a finger happens, there is no touchMove event firing. TouchEnd event is subtracting touchStart and touchMove data (screenX & screenY). So result is always bigger then deltaMin and the custom swipe event is triggered. The solution is to take out the touchMove event and add its logic to touchEnd event.

Upvotes: 1

Related Questions