modifiedcontent Posted December 26, 2022 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.
modifiedcontent Posted January 1, 2023 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.
gebeer Posted January 2, 2023 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
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