4

I have a Component which fetches posts from a graphql server. It shall fetch more posts if the user is at the bottom of the page. How can I check if he has reached it? I looked on clientHeight, innerHeight and outerHeight but only clientHeight made any sense to work with from those. Probalby I have to substract somethin from it but I don't know how what exactly and what I am missing?

<script>
    import {getClient, query} from 'svelte-apollo';
    import ContentWrapper from '../layout/ContentWrapper.svelte';
    import {GET_POSTS} from "../services/posts/postsApi";

    let pageInfo;
    let first = 2;
    let after;

    const client = getClient();
    let posts = query(client, {query: GET_POSTS, variables: {first}});

    const fetchMore = () => {
        posts.fetchMore({
            variables: {first, after},
            updateQuery: (previousResult, {fetchMoreResult}) => {
                const newEdges = fetchMoreResult.getPosts.edges;
                const pageInfo = fetchMoreResult.getPosts.pageInfo;

                return newEdges.length
                        ? {
                            getPosts: {
                                __typename: previousResult.getPosts.__typename,
                                edges: [...previousResult.getPosts.edges, ...newEdges],
                                pageInfo
                            }
                        }
                        : previousResult;
            }
        })
    };

    const setState = queryData => {
        pageInfo = queryData.getPosts.pageInfo;
        after = pageInfo.endCursor;
        return '';
    };

    let y;
    let clientHeight;

    const checkScrollPosition = () => {
        if(clientHeight <= y)
            fetchMore();
    }
</script>

<svelte:window on:scroll={checkScrollPosition} bind:scrollY={y} />

<ContentWrapper>
    <div slot="header">
        <h4>Page of Pagination</h4>
    </div>
    <div slot="body" bind:clientHeight={clientHeight}>
        {#await $posts}
            <h3>Loading...</h3>
        {:then result}
            {setState(result.data)}
            {#each result.data.getPosts.edges as post}
                <div class="card">
                    <div class="card-content">
                        <p class="card-text">{post.node.body}</p>
                    </div>
                </div>
            {/each}
            <div id="paginator">
                {#if pageInfo}
                    {#if pageInfo.hasNextPage}
                        <div class="btn" on:click={fetchMore}>Next</div>
                    {/if}
                {/if}
            </div>
        {:catch error}
            {console.log(error)}
        {/await}
    </div>
</ContentWrapper>
poku17
  • 41
  • 3
  • Does this answer your question? [Javascript: How to detect if browser window is scrolled to bottom?](https://stackoverflow.com/questions/9439725/javascript-how-to-detect-if-browser-window-is-scrolled-to-bottom) – Shoejep Mar 03 '20 at 15:30

2 Answers2

2

The Intersection Observer is what you are looking for in the question. The technology is supported by all modern browsers. With it, you can find whether some element is visible on a screen and, after that, fetch more items.

You can take a look at the Svelte IntersectionObserver component implementation right here: it uses both the technology the old-fashioned way with window.innerHeight

VanishMax
  • 71
  • 5
0

Well, here's a simple workaround that I used, in case the height of your scrollable div is fixed for all screen sizes.

  • So, First of all, enclose it in a div and in CSS, set the overflow: scroll and the height: 600px (any fixed height)
  • Then on localhost, run this function in console of your browser's dev tools, getX() and note down the value that is returned (We'll call it 'x'):
const getX = () => {
  let myDiv = document.querySelector('my-div-id')
  myDiv.scrollTop = myDiv.scrollHeight;
  return myDiv.scrollHeight - myDiv.scrollTop;
}
  • Then in your Svelte component:
<sript>
    let x = 585 // the value returned by getX() function
    let contentLength = 10; // number of items that load on the page initially
    
    const loadOnScroll = (elem) => {
       if (elem.scrollTop >= (elem.scrollHeight - x) ) {
         contentLength += 10; // load more as user scrolls to bottom
       }
    }
</script>

  <div id="my-div-id"
  on:scroll="{ () => loadOnScroll(document.querySelector('my-div-id')) }" >

    {#each items as item, i}
        {#if i < contentLength}
          <ul>
            <li>        
                <!-- Do something with item -->
            </li>
        </ul>
        {/if}
    {/each}
  </div>

This will load 10 more fetched items from your API each time the user scrolls all the way to the bottom of the div. I know it looks kinda scattered, but hey, it works ;)

RAZ0229
  • 193
  • 4
  • 14