modifiedcontent Posted December 26, 2022 Share Posted December 26, 2022 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 More sharing options...
modifiedcontent Posted January 1, 2023 Share Posted January 1, 2023 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 More sharing options...
gebeer Posted January 2, 2023 Share Posted January 2, 2023 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. 2 Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now