sveltejs / svelte

web development for the rest of us
https://svelte.dev
MIT License
79.82k stars 4.24k forks source link

scrollX/Y for things other than window #3780

Open weepy opened 5 years ago

weepy commented 5 years ago

Is there a reason why there's no bind:scrollX for dom nodes ? I see there are for window. I rolled my own solution, but it felt like it should be supported!

Conduitry commented 5 years ago

What would these measure? Which DOM properties are you referring to?

weepy commented 5 years ago

dom.scrollLeft and dom.scrollTop So e.g if I want to programmatically set scrollLeft of a dom element from a reactive property or tweenable store prop.

Rich-Harris commented 5 years ago

I like this, I've wanted it a couple of times myself. Opened #3895. It uses scrollLeft and scrollTop as the binding names, rather than making it consistent with <svelte:window> but inconsistent with the DOM

scottjmaddox commented 4 years ago

+1 for wanting bind:scrollLeft/bind:scrollTop.

@Rich-Harris I saw that you identified some issues with your initial implementation approach. I could take a crack at fixing it, if you can point me in the right direction.

mizzao commented 4 years ago

We've been wanting this feature as well, to implement a scroll carousel functionality using CSS scroll snapping on a PWA. But the app needs to know the current scroll position!

What would you folks recommend as an interim solution? Would it be something like element.addEventListener("scroll", ...)?

coleholyoake commented 3 years ago

Would love an interim solution if anyone worked one out!

aradalvand commented 3 years ago

This would've been nice. I thought it's already implemented since we have it for <svelte:window>.

emonadeo commented 3 years ago

Requires a few lines, but falling back to EventListeners this is the best I could come up with, using bind:

<script>
  import { onMount } from 'svelte';

  let elem;
  let elemScrollTop = 0; // This will be reactive

  onMount(() => {
    // Update elemScrollTop every time the user scrolls
    elem.addEventListener('scroll', ({ target }) => (elemScrollTop = target.scrollTop));
  });
</script>

<div bind:this={elem}></div>

I couldn't figure out a solution leveraging svelte's reactivity to update scrollTop/scrollLeft. If someone finds a better solution I'd love to know too.

xylobol commented 3 years ago

This would be a massive feature for my project. We have an in-house carousel that we've built, and it's extremely complex because we had to implement part of this ourselves.

kwshi commented 3 years ago

I couldn't figure out a solution leveraging svelte's reactivity to update scrollTop/scrollLeft. If someone finds a better solution I'd love to know too.

What about

<script>
  let elem;
  let elemScrollTop = 0;
</script>

<div bind:this={elem} on:scroll={() => (elemScrollTop = elem.scrollTop)}></div>

or

<script>
  let elemScrollTop = 0;
</script>

<div on:scroll={(ev) => (elemScrollTop = ev.target.scrollTop)}></div>

?

svenjacobs commented 2 years ago

In case you're trying to find out whether an element entered or left the viewport, I found the nice svelte-inview action. However support of scrollX / scrollY for any element would be greatly appreciated.

weepy commented 2 years ago

Here's my little action :

    const scrollLeft = writable(0)

    export function scrollX(node, store) {

        store.subscribe(val => node.scrollLeft = val)

        node.addEventListener('scroll', (e) => {
            store.set(e.target.scrollLeft)
        })
    }

use like:

<div use:scrollX={scrollLeft}> ...
SoundAsleep192 commented 2 years ago

guys, any updates on this?

acarl005 commented 2 years ago

This would be very useful for triggering an animation when a child element becomes visible!

Karakatiza666 commented 3 months ago

A workaround I came up with for Svelte 5 runes


let scrollY = $state(0)
...
  const bindScrollY = (node: HTMLElement, val: { scrollY: number }) => {
    $effect(() => {
      node.scrollTop = scrollY
    })
    const handle = (e: any) => {
      scrollY = e.target.scrollTop
    }
    node.addEventListener('scroll', handle)
    return {
      destroy: () => removeEventListener('scroll', handle)
    }
  }
...
<div ... use:bindScrollY={{ scrollY }}>