pikax/vue-composable

useOnScroll throwing errors

ux-engineer opened this issue · 17 comments

  • vue-composable 1.0.0-beta.12
  • vue 2.6.12
  • @vue/composition-api 1.0.0-beta.21
setup: () => {
    const tableElementRef: Ref<HTMLElement | null> = ref(null);

    const { scrollTop, scrollTopTo } = useOnScroll(tableElementRef);
    
    const onClick = async function onClick () {
      // Scroll To Table
      console.debug('tableElementRef', tableElementRef, tableElementRef.value);
      scrollTopTo(scrollTop.value);
      // Fetch via store
      await store.doAction();
    };

    return { tableElementRef, onClick };
}
[Vue warn]: Error in event handler for "hook:beforeUpdate": "TypeError: e.addEventListener is not a function"

TypeError: e.addEventListener is not a function
    addEventListener vue-composable.esm-bundler.js:179
    removeWatch vue-composable.esm-bundler.js:184
    node_modules vue-composition-api.esm.js:1039
    node_modules vue-composition-api.esm.js:982
    node_modules vue-composition-api.esm.js:893
    node_modules vue-composition-api.esm.js:849
    VueJS 7
pikax commented

Can you provide your template code?

Are you assigning tableElementRef to a HTML element?

@pikax ah, ok, I was applying the template ref into a library component. Adding it to a native element cleared the errors.

However, it's not working yet. No scrolling happens. The documentation is quite thin on how to actually use it.

I'm trying to programmatically scroll the viewport up to a component, and hopefully doing a smooth scroll?

pikax commented

However, it's not working yet. No scrolling happens. The documentation is quite thin on how to actually use it.

I need to do some work on the documentation.

The way it work is:
You pass a ref with template ref, internally the composable will basically do

<template>
  <div ref="tableElementRef">
   <!-- ... -->
  </div>
</template>
<script>

export default {
 setup(){
  const tableElementRef: Ref<HTMLElement | null> = ref(null);
    
    const onClick = async function onClick () {
      tableElementRef.value.scrollTo({ top: tableElementRef.value.scrollTop } );
    };

    return { tableElementRef, onClick };
 }
}
</script>

useOnScroll is intended to be a wrapper for the HTMLElement.

I'm trying to programmatically scroll the viewport up to a component, and hopefully doing a smooth scroll?

Currently calling scrollTopTo and scrollLeftTo do not use smooth scroll, that needs to be added, but you can always call scrollTo and pass the smooth behaviour.

I was applying the template ref into a library component

I haven't tried with library component, I think in v2 should be straight forward but in v3 with multiple root elements might be a bit tricky

@pikax sorry but I still could not figure out how to use this - could you post a full setup code example on how to scroll top to a specific element?

pikax commented

If you provide code somewhere where I can have a look, I can help you better.

@pikax here's basically my stripped-down version. scrollTop.value is giving 0 and scrollTo method is not doing anything?

<script lang="ts">
import { defineComponent, ref, Ref } from '@vue/composition-api';
import { useOnScroll } from 'vue-composable';

export default defineComponent({
  setup: () => {
    const titleRef: Ref<HTMLElement | null> = ref(null);

    const { scrollTo, scrollTop } = useOnScroll(titleRef);

    const scrollToElementTop = function scrollToElementTop() {
      console.debug('scrollToElementTop', titleRef.value);
      if (titleRef.value) {
        console.debug('scrollToElementTop - scrolling...', scrollTop.value);
        // scrollTop.value is giving 0 and scrollTo method is not doing anything
        scrollTo({
          top: scrollTop.value,
        });
      }
    };

    return {
      titleRef,
      scrollToElementTop,
    };
  },
});
</script>

<template>
    <div style="padding-top: 300px">
        <h2 ref="titleRef">Title</h2>
        <div style="height: 500px;"></div>
        
        <b-button @click="scrollToElementTop()">Scroll</b-button>
    </div>
</template>

Logging of titleRef.value shows that the element is found.

Using scrollTopTo(150); quirks up my layout. It seems plausible that as I'm using simplebar-vue scrollbar library and these contents are within such a scrollable container, that it somehow doesn't along with your composable.

pikax commented

Seems you're using it incorrectly, definitely something the docs are not very clear.

The element passed to the useOnScroll needs to be a scrollable element, that element is the one that shows the scrollbar.

At the least in your example, the element who scrolls is the window document, in your case you can change the code to:

<script lang="ts">
import { defineComponent, ref, Ref } from '@vue/composition-api';
import { useOnScroll } from 'vue-composable';

export default defineComponent({
  setup: () => {
    const titleRef: Ref<HTMLElement | null> = ref(null);

    const { scrollTo, scrollTop } = useOnScroll(); // use the document scrollable

    const scrollToElementTop = function scrollToElementTop() {
      console.debug('scrollToElementTop', titleRef.value);
      if (titleRef.value) {
        console.debug('scrollToElementTop - scrolling...', scrollTop.value);
        // scrollTop.value is giving 0 and scrollTo method is not doing anything
        scrollTo({
          top: titleRef.value.offsetTop, // passing the title offset to top
        });
      }
    };

    return {
      titleRef,
      scrollToElementTop,
    };
  },
});
</script>

<template>
  <div style="padding-top: 300px">
    <h2 ref="titleRef">Title</h2>
    <div style="height: 500px;"></div>

    <button @click="scrollToElementTop()">Scroll</button>
  </div>
</template>

@pikax got it working using vue-scrollTo library. Needed to use .simplebar-content-wrapper as the container, so should be able to get it work with your composable using that container.

you can always call scrollTo and pass the smooth behaviour.

How would this be done in practice?

pikax commented

How would this be done in practice?

   scrollTo({
          top: titleRef.value.offsetTop, // passing the title offset to top
          behavior: 'smooth'
        });

@pikax thanks, smooth scrolling works now.

However I'm still having problem scrolling to the ref'fed element, as offsetTop is giving me value like 20 which is relative to it's own parent and not the scroll container or document? Could I just pass the element href to the method for it to scroll there, and options as the second param?

pikax commented

Have you tried to use scrollIntoView?

  setup: () => {
    const titleRef: Ref<HTMLElement | null> = ref(null);

    const { scrollIntoView } = useOnScroll(titleRef); // use the document scrollable

    const scrollToElementTop = function scrollToElementTop() {
      console.debug('scrollToElementTop', titleRef.value);
      if (titleRef.value) {
        scrollIntoView();
      }
    };

    return {
      titleRef,
      scrollToElementTop,
    };
  },

@pikax how to define scroll container if titleRef is given as the param for useOnScroll() instead of scroll container?

pikax commented

if you want to scroll to that component, you probably can you scrollIntoView if you provide an example, I might to be able more, but so far it seems useOnScroll is working as intended.

@pikax I mean I got this working only by specifying the scroll container by passing it to useOnScroll. If scrollIntoView working by passing the element to which to scroll to useOnScroll, how can I specify the scroll container when doing so?

@pikax can scrollIntoView method do a smooth scrolling instead of jumping immediately to the position, or is it working wrong with my setup?