sveltesveltekitglidejs

Glidejs on:click not triggering on every slide


In my glidejs carousel, the user can click on an image slide to open it in a preview mode. It works great except for the first and the last elements, they just don't trigger an on:click event if they are at a certain position. Check out the three examples below:

enter image description here

This is the configuration:

   onMount(async () => {
        bilder = await getData();

        setTimeout(() => {
            new Glide('.glide', {
                type: 'carousel',
                startAt: 0,
                perView: 3,
                focusAt: 'center',
                autoplay: 3000,
                hoverpause: true,
                keyboard: true,
                dragThreshold: false
            }).mount();
        }, 1);
    });

And the HTML:

{#await images then items}
    <div class="glide">
        <div class="glide__track" data-glide-el="track">
            <ul class="glide__slides">
                {#each items as image, i}
                    {#if image.public}
                        <li class="glide__slide">
                            <img
                                on:click={() => {
                                    <!-- sometimes not triggered -->
                                    openImage(image.filepath);
                                }}
                                src={image.filepath}
                                alt={image.name}
                            />
                        </li>
                    {/if}
                {/each}
            </ul>
        </div>

        <div class="glide__arrows" data-glide-el="controls">
            <button class="glide__arrow glide__arrow--left" data-glide-dir="<">
                <img src="/google-icons/arrow-back.svg" alt="prev" />
            </button>

            <button class="glide__arrow glide__arrow--right" data-glide-dir=">">
                <img src="/google-icons/arrow-forward.svg" alt="next" />
            </button>
        </div>
    </div>
{/await}

How can I fix that?


Solution

  • Glide duplicates elements for the slides which does not retain event listeners on the elements. If you want to reliably handle clicks, add the listener on an ancestor element like the root and inspect the event.target to ensure that an element of interest was clicked.

    Also, one should use actions for something like this, which does not require onMount or selectors and also allows you to easily clean up instances of third-party widgets like this properly.

    E.g.

    <script>
        ...
    
        function glide(node) {
            const instance = new Glide(node, { /* ... */ }).mount();
    
            return { destroy: () => instance.destroy() };
        }
    
        function onClick(e) {
            if (e.target.closest('[data-slide-image]') == null)
                return;
    
            // [click logic]
        }
    </script>
    
    <div class="glide" use:glide on:click={onClick}>
        ...
        <button type=button data-slide-image>
            <img ... />
        </button>
    

    click handlers should in general not be added to arbitrary elements as that is not accessible. To make this in particular more accessible, the image should be wrapped in a button which automatically enables keyboard interactions. The warnings on the div with the handler then can be ignored; I would recommend adding a justification so it is more clear what is going on.

    <!-- Events of interest come from interactive elements
         svelte-ignore a11y-click-events-have-key-events
                       a11y-no-static-element-interactions -->
    

    Side note: If I had to use a page using this library, it would drive me mad. The rate at which slides can be changed is capped by the duration of the animation, which has to fully complete before the next navigation can happen. Carousels in general are questionable and this one apparently does not even allow swiping multiple items at once.