0

I'm trying to make an infinite scroll (without jQuery) to show more results in a page. I'm using an IntersectionObserver to detect a div called #paginate and everytime it enters the screen, the #result div will be refreshed.

var result = document.querySelector('#result');
var paginate = document.querySelector('#paginate');

var observer = new IntersectionObserver(entries => {
if (entries.some(entry => entry.isIntersecting))
{
var pagination = 10;
fetch('/kernel/search.php?pagination='+pagination)
.then((response) => {
return response.text();
})
.then((html) => {
result.innerHTML = html;
});
}
});

observer.observe(paginate);

Here's the full code view with HTML:

<html>
<body>

<div class="row justify-content-sm-center justify-content-md-center justify-content-lg-center justify-content-xl-start no-gutters min-vw-100" id="result">
<div class="col-sm-11 col-md-11 col-lg-9-result col-xl-4-result order-0">

<div class="card mx-3 mt-3">
<div class="card-body">
<a class="text-decoration-none" href="?topic=result-1">
<h5 class="card-title"> 
Result 1    
</h5>
</a>
<p class="card-text text-truncate"> 
Result 1 description.</p>
</div>
</div>

<div class="card mx-3 mt-3">
<div class="card-body">
<a class="text-decoration-none" href="?topic=result-2">
<h5 class="card-title"> 
Result 2    
</h5>
</a>
<p class="card-text text-truncate"> 
Result 2 description.</p>
</div>
</div>

<div class="alert alert-light text-dark text-center border mx-3 my-3" id="paginate">
More results
</div>

</div>
</div>

<script>    
var result = document.querySelector('#result');
var paginate = document.querySelector('#paginate');

var observer = new IntersectionObserver(entries => {
if (entries.some(entry => entry.isIntersecting))
{
var pagination = 10;
fetch('/kernel/search.php?pagination='+pagination)
.then((response) => {
return response.text();
})
.then((html) => {
result.innerHTML = html;
});
}
});

observer.observe(paginate);
</script>

</body>
</html>

It works, but it only works the first time and it doesn't refresh the #result div thereafter. I can see the fetch working in Web Browser > Inspect > Network tab, but there's no activity after the first refresh of the #result div meaning it doesn't detect the #paginate div anymore.

What's going on here? I assume it's because that I'm using an innerHTML and the observer somehow can't detect the #paginate div after the first refresh of the #result div. How can I solve this?

haZh
  • 127
  • 2
  • 12

4 Answers4

0

I did it with jQuery and .scroll function and used ajax like this, maybe my code can help you and adapt it to your needs.

$('#customersList').scroll(function () {
    if ($(this).scrollTop() + $(this).innerHeight() >= $(this)[0].scrollHeight - 5000) {
        // Do your stuff here.
        getCustomers();
    }
})


function getCustomers(){
    let $customerList = $('#customersList');
    let offset        = ($customerList.attr('data-offset')) ? 
    
    $customerList.attr('data-offset') : 0;

    if ($customerList.data('requestRunning')) {
        return;
    }
    $customerList.data('requestRunning', true);

    return $.ajax({
                  type: "GET",
                  data: {offset: offset},
                  url : routes.routes.customers.get
              })
        .done(function (data) {
            let _htmlCustomersList = ($customerList.is(':empty')) ? '' : $customerList.html();
            let response           = data.data;
            
            if (response) {
                for (const i in response) {
                    let v = JSON.parse(response[i]);
                    _htmlCustomersList += '<div class="client-group edit" data-id="' + v['id'] + '"></div><hr>';
                }

                offset = parseInt(offset) + 200;
                $customerList.attr('data-offset', offset).html(_htmlCustomersList);
            }
        })
        .always(function () {
            $customerList.data('requestRunning', false);
        });

}

my getCustomer function runs before reaching the end of the page and loads 200 more items each time.

I hope this can help you a little bit

Skyd
  • 535
  • 1
  • 4
  • 20
0

It seems you are removing #paginate after the first update, because it is in the #result.

Please, use indentation :)

rudevich
  • 71
  • 3
  • Yes, it's in `#result` and `#paginate` doesn't get removed. I can see `#paginate` on the bottom of the page after the first `fetch` refresh, but it doesn't trigger the `observer` at all. – haZh Sep 09 '20 at 13:00
0

The #result div is the main wrapper of the content using innerHTML to update the contents of the div, will result in replacing the entire content inside of the div...

Beyond the fact that innerHTML is absolute, and erases any DOM objects (and their events) hence bad practice, it's not that good solution either, since you'd like to append rather then replace the data, upon scrolling

I would suggest to add a div above the paginate, which will hold the added data, something like:

...
<div id="result"></div>
<div class="alert alert-light text-dark text-center border mx-3 my-3" id="paginate">
More results
</div>

Then use some sort of appending, for the content added so something like:

.then((html) => {
let res = new DOMParser().parseFromString(html, "text/xml");
document.getElementById("result").appendChild(res);
});

Hope that helps

Guy Louzon
  • 1,175
  • 9
  • 19
  • There's no need to listen to an `onscroll` event, the `IntersectionObserver` does it and works for me. So can you tell me how can I do `result.innerHTML = html;` with `node.appendChild()`? – haZh Sep 09 '20 at 14:49
  • As I answered, I didn't see where the result node appears in the code – Guy Louzon Sep 09 '20 at 15:00
  • an examplw for append child: https://www.w3schools.com /jsref/met_node_appendchild.asp – Guy Louzon Sep 09 '20 at 15:03
  • yes. I don't see what's selected, and what's added to it – Guy Louzon Sep 09 '20 at 15:35
  • `#result` div is there in the HTML code, use the horizontal scroll because the code is overflown. The `
    `s with `card` classes are the results inside the `#result` div, and there are many results. I have only included 2 results here. And lastly there is the `#paginate` div, which is also inside the `#result` div. As soon as the scroll reaches `#paginate` div, the `#result` div should refresh. Right now it only works the first time and JavaScript can't detect the `#paginate` div thereafter even though I can see the `#paginate` div on the page.
    – haZh Sep 09 '20 at 15:44
  • my answer as to add a childnode is even better. let me fix my answer a bit – Guy Louzon Sep 10 '20 at 11:34
0

I have removed innerHTML and replaced it with insertAdjacentHTML.

Because innerHTML seems to reset/forget #paginate after the first refresh of the #result, so the observer can't detect #paginate anymore to trigger the next #result refresh.

I could have used innerHTML with appendChild to do the same but appendChild adds a new div on each #result refresh, which I didn't really like.

As the solution, I'm inserting the refreshed html data before the beginning of #paginate without resetting/forgetting it's element id that's required for the observer to trigger the next #result refresh.

.then((html) => {
paginate.insertAdjacentHTML('beforebegin', html);
});
haZh
  • 127
  • 2
  • 12