svelte icon indicating copy to clipboard operation
svelte copied to clipboard

scrollX/Y for things other than window

Open weepy opened this issue 6 years ago • 15 comments

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!

weepy avatar Oct 23 '19 19:10 weepy

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

Conduitry avatar Oct 23 '19 19:10 Conduitry

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.

weepy avatar Oct 23 '19 20:10 weepy

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

Rich-Harris avatar Nov 11 '19 14:11 Rich-Harris

+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.

scottjmaddox avatar Jun 14 '20 16:06 scottjmaddox

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", ...)?

mizzao avatar Sep 29 '20 18:09 mizzao

Would love an interim solution if anyone worked one out!

coleholyoake avatar Dec 17 '20 20:12 coleholyoake

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

aradalvand avatar Mar 05 '21 07:03 aradalvand

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.

emonadeo avatar May 02 '21 15:05 emonadeo

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.

xylobol avatar May 19 '21 17:05 xylobol

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>

?

kwshi avatar Nov 06 '21 19:11 kwshi

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.

svenjacobs avatar Jan 28 '22 18:01 svenjacobs

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}> ...

weepy avatar Mar 10 '22 20:03 weepy

guys, any updates on this?

SoundAsleep192 avatar Jun 17 '22 09:06 SoundAsleep192

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

acarl005 avatar Jun 30 '22 19:06 acarl005

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 }}>

Karakatiza666 avatar Aug 04 '24 08:08 Karakatiza666