Where it runs
The plugin hooks render_block_core/post-template. The first Post Template whose Query block inherits the main query, on a request that is not singular, admin or REST, gets the class lh-infinite-scroll-items on its list element. When the main query has a later page, the list also gets data-lh-infinite-scroll-next with that page’s URL (from get_pagenum_link(), so query strings such as a search term are kept), and the script is enqueued. Query blocks with their own custom query are left alone, and so are classic PHP templates.
In the browser
- An empty
div.lh-infinite-scroll-sentinelis placed after the list and watched with IntersectionObserver. When it comes within the load distance of the viewport the next page is fetched (same-origin credentials, so members see what they would see on that page), its.lh-infinite-scroll-itemschildren are imported and appended, and the next URL is read from that list and copied to this one’sdata-lh-infinite-scroll-next, which is removed after the last page. - Items are matched by their
post-{ID}class, and an item already on the page is skipped. - The first item of each page carries
data-lh-infinite-scroll-urlanddata-lh-infinite-scroll-title. On scroll the page whose first item has passed a third of the way down the viewport becomes current, and the address bar (history.replaceState) anddocument.titleare set to it. - Pagination after the list, in the same Query block, is hidden with an inline
display: none. This covers the Query Pagination block’s own markup (.wp-block-query-pagination) and core’s classicnav.navigation.paginationornav.navigation.posts-navigation, which some themes, LH 2015 among them, print for the block. Pagination before the list, such as a top pager, is left visible. If a fetch fails, loading stops, the pagination is shown again and the failure is announced. - A visually hidden
div.lh-infinite-scroll-statuswithrole="status"announces each loaded page by its title. The list hasaria-busywhile a page is loading, and the body gets the classlh-infinite-scroll-active. - Scripts inside loaded items do not run, as with any HTML parsed into the page.
- The script is deferred. A page optimiser that delays scripts until the first interaction (such as LiteSpeed Cache’s delayed JavaScript, used on LocalHero) holds it back until the visitor first scrolls or taps, which costs nothing here because loading only starts once they scroll towards the end.
Events
lh-infinite-scroll:loaded bubbles from the list after each page is appended, with detail.url, detail.title and detail.nextUrl (empty after the last page). Use it to start anything that needs to act on the new items.
Filters
lh_infinite_scroll_init_plugin: return false to stop the plugin loading without deactivating it.lh_infinite_scroll_is_enabled: return false to leave the current request’s list alone, for example on one post type archive.lh_infinite_scroll_return_load_distance: pixels below the viewport at which the next page starts loading (default 800).lh_infinite_scroll_return_next_page_url: the next page URL, or an empty string to stop at the current page.lh_infinite_scroll_return_script_handle: the script handle (defaultlh-infinite-scroll-script).
The script reads its settings from window.lhInfiniteScrollSettings (distance, and the translated loaded and failed messages), printed before it.

