Jump to content

Continuous Scroll - Recommended Approach?


Gazley
 Share

Recommended Posts

I had a working version in my previous comment, but can't get PW $posts array to play nice with the rest of the script; I have to convert PW $posts to another $posts_array and then echo with the `$post['title']` syntax to make it work - why? what am I getting wrong?

That works OK if you only want to show one or two fields, like `$post->title`, but obviously not if you want to do more complicated stuff. 

So I'm trying another approach. I first grab a batch of IDs with $pages->findIDs() and then use those to lazy load each entire object as they are needed scrolling down the page. Should be a very efficient approach, right? Or not?

I had trouble echoing the IDs with $post->id or $post['id'] or whatever, but apparently $post alone works with this simple one-dimensional array (?) - it's like all clever and stuff...

This works ? :

Spoiler
<?php 

	if($config->ajax) {
		
			$template = $input->get('template');
			
			$batch = $input->get('batch');
		
			$posts = $pages->findIDs("template=$template, limit=$batch, sort=-date");
						
			// Default limit
			$limit = isset($_GET['limit']) ? $_GET['limit'] : 2;
			
			// Default offset
			$offset = isset($_GET['offset']) ? $_GET['offset'] : 0;
			
			$queried_posts = array_slice($posts, $offset, $limit);
			
			$total_posts = count($posts);
			
			// Get the total rows rounded up the nearest whole number
			$total_rows = (int)ceil( $total_posts / $limit );
			
			$current_row = !$offset ? 1 : ( $offset / $limit ) + 1;
			
			if (count($queried_posts)) {

			    foreach ($queried_posts as $post) {
			    
						$post = $pages->get($post);


								echo '<a href="'. $post->url .'"><h3>'. $post->title .'</h3>';					
								echo '<p class=dateline>' . $post->city->title .' '. date( 'F Y', $post->getUnformatted('date') ) .'</p>';
								echo $post->body .'</a>';
								
								echo '</div>';

			           
						}

			} else {
			
			    return false;
			}
		
	return $this->halt();

} else {

include 'inc/head.php'; ?>

<body>

     <div class=container style='padding: 8% 8% 50%'>
     <div id=loadposts></div>
     <button id=loadmore>Load more</button>
     </div>

</body>

<?php } ?>

<script>
var jLoad = jQuery.noConflict(); 

var template = 'video|post';
var batch = 32;
var limit = 1;
var offset = 0;
var noMorePosts = false;
var loadingInProgress = false;

function loadPosts() {
    
    if (noMorePosts) return;
    
    let queries = {
			'template' : template,
			'batch' : batch,
			'offset' : offset,
			'limit' : limit
    };
    
    if (!loadingInProgress) {
        
			loadingInProgress = true;
        
			jLoad('#loadmore').html('Loading...').prop('disabled', true);
        
			jLoad.get('', queries, function(data) {
            
            if(!data) {
					noMorePosts = true;
					jLoad('#loadmore').remove();
            }
            else {
                offset += limit;               
                jLoad('#loadposts').append(data);
            }

			loadingInProgress = false;
            
			jLoad('#loadmore').html('Load more').prop('disabled', false);            
            
        });
    }
}


const io = new IntersectionObserver(entries => {
  entries.forEach(entry => {
    if (!entry.isIntersecting) {
      return;
    }
        loadPosts();
  });
});

io.observe(document.getElementById('loadmore'));



jLoad(document).on('click', '#loadmore', function() {
    loadPosts();    
});


jLoad(document).ready(function() {
    loadPosts();
})
</script>

 

Let me know if you see problems, possible improvements, bits that can be replaced with PW methods, etc. 

Link to comment
Share on other sites

Is there a way to detect if an element is already in viewport on load with IntersectionObserver, without having to scroll or resize?

With my solution above you might get a #loadmore button when you only load one or two posts on initial load; I want to trigger that button to fill the page with posts until it's out of view. 

You can work around that by using this function:

Spoiler
jLoad.fn.isInViewport = function() {
    var elementTop = jLoad(this).offset().top;
    var elementBottom = elementTop + jLoad(this).outerHeight();

    var viewportTop = jLoad(window).scrollTop();
    var viewportBottom = viewportTop + jLoad(window).height();

    return elementBottom > viewportTop && elementTop < viewportBottom;
};

 

And then this in the loadPosts() function below where the button is added in #loadmore:

Spoiler
			if (jLoad('#loadmore').isInViewport()) {
				jLoad('#loadmore').trigger('click');
			}

 

That does the trick, but feels hacky and IntersectionObserver was supposed to replace that outerHeight/elementTop stuff.

Is there any way to achieve the same by changing my IntersectionObserver code in some way? This version only triggers #loadmore when you scroll to it, not when it is already in view on load:

Spoiler
const io = new IntersectionObserver(entries => {
  entries.forEach(entry => {
    if (!entry.isIntersecting) {
      return;
    }
        loadPosts();
  });
});

io.observe(document.getElementById('loadmore'));

 



Edit: Thanks @gebeer for the suggestion below. That certainly looks cleaner. Should I just forget about IntersectionObserver? I won't get around to testing and looking into this until end of this month. 

Link to comment
Share on other sites

10 hours ago, modifiedcontent said:

Is there a way to detect if an element is already in viewport on load with IntersectionObserver, without having to scroll or resize?

Have a look here https://stackoverflow.com/questions/53214116/intersectionobserver-callback-firing-immediately-on-page-load

10 hours ago, modifiedcontent said:

I want to trigger that button to fill the page with posts until it's out of view.

I made a codepen that does just that https://codepen.io/gebeer/pen/KKBMWzV?editors=1011 It uses getBoundingClientRect, though.

 

  • Like 2
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...