Skip to content

Instantly share code, notes, and snippets.

@Alfa-Q
Last active November 22, 2022 19:15
Show Gist options
  • Save Alfa-Q/d7bbcf7d0727a90c6f0dae3bd05ec846 to your computer and use it in GitHub Desktop.
Save Alfa-Q/d7bbcf7d0727a90c6f0dae3bd05ec846 to your computer and use it in GitHub Desktop.
list.js - Infinite Scroll with Pagination
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Infinite Scroll Example</title>
<!-- Styles -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link rel="stylesheet" href="styles.css">
<!-- Scripts -->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/list.js/1.5.0/list.min.js"></script>
<script src="script.js"></script>
</head>
<body>
<!-- Really Simple Example -->
<div id="top-section">
<h4 class="display-4">Infinite Pagination Example!</h4>
<p>
In the following example, 5 items are initially loaded.<br/>
Each time you scroll down past towards the bottom, the next 5 items are retrieved.
</p>
<hr />
<div id="info">Total Items: 0</div>
</div>
<div id="items-container" class="border" onscroll="scrollHandler()">
<ul class="list"></ul>
<ul class="pagination" hidden></ul>
</div>
</body>
</html>
/* ===============================================================================
// Setup with list.js
// =============================================================================*/
let itemList; // Set when the page is loaded.
const itemsPerPage = 5; // Total number of items you want to render on each page
window.addEventListener("load", () => {
itemList = new List("items-container", {
pagination: true, // Required for pagination to work
page: itemsPerPage, // Items per page
valueNames: [ // Properties to extract from JSON object and be inserted into our item template below
"name",
"category",
"subcategory",
{ name: "thumbnail", attr: "src" },
{ name: "link", attr: "href" },
],
item: `<li>
<img class="mr-3 img-thumbnail thumbnail" src="" width="125px" height="125px" />
<div class="media-body">
<div class="name display-6 mt-2 mb-1"></div>
<div class="mt-auto">
<div class="category badge badge-primary"></div> &gt;
<div class="subcategory badge badge-secondary"></div>
</div>
<a class="link" href="" target="_blank">View Info</a>
</div>
</li>`,
});
// Load items (see bottom of page to view the items)
itemList.add(items, () => {
$("#info").text(`Total Items: ${itemList.items.length}`);
});
});
/* ===============================================================================
// Infinite Pagination v(⌒o⌒)v
// =============================================================================*/
let scrolling = false; // Current state (are we currently scrolling or not?)
let page = 1; // Starting page
const scrollThrottle = 250; // Interval between scroll checks.
// Event which is binded to the unordered list element's onscroll event.
// Simply updates the current state.
function scrollHandler() {
scrolling = true;
}
// Infinite Scroll with Pagination
setInterval(() => {
// Only bother checking if we can load next page items if the user is currently scrolling
if (scrolling) {
const container = $("#items-container");
const containerPosY = container.height(); // Y position of the container top
const scrollPosY = container.scrollTop(); // Scroll position Y
const containerList = $("#items-container > .list"); // The actual list containing the items
const containerListY = containerList.height(); // Container list height size
const offsetY = 15; // Distance from bottom or top that is considered within range to
// items on the next page
// Can paginate, near the bottom of the list of items
if (scrollPosY + containerPosY > containerListY - offsetY) {
console.log("REACHED BOTTOM! Loading More Items!");
page++;
itemList.show(0, page * itemsPerPage);
}
// Near the top of the list (not used for infinite scroll, but
// if you need to do something here, feel free to...)
else if (scrollPosY + containerPosY < containerPosY + offsetY) {
console.log("REACHED TOP!");
// Do stuff
}
scrolling = false;
}
}, scrollThrottle);
/* ===============================================================================
// Random Data
// =============================================================================*/
const items = [
{
name: "Iroha Isshiki",
category: "Anime",
subcategory: "Character",
thumbnail: "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png",
link: "https://www.google.com",
},
{
name: "Elden Ring",
category: "Video Games",
subcategory: "Miyazaki Plz",
thumbnail: "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png",
link: "https://www.google.com",
},
{
name: "Dark Souls",
category: "Video Games",
subcategory: "JRPG",
thumbnail: "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png",
link: "https://www.google.com",
},
{
name: "Bioshock",
category: "Video Games",
subcategory: "Shooter",
thumbnail: "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png",
link: "https://www.google.com",
},
{
name: "Kirby",
category: "Video Games",
subcategory: "Character",
thumbnail: "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png",
link: "https://www.google.com",
},
{
name: "ICE +Caffeine",
category: "Drink",
subcategory: "Energy Drinks",
thumbnail: "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png",
link: "https://www.google.com",
},
{
name: "Demon Slayer",
category: "TV",
subcategory: "Anime",
thumbnail: "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png",
link: "https://www.google.com",
},
{
name: "Deadmau5",
category: "Music",
subcategory: "Music Artist",
thumbnail: "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png",
link: "https://www.google.com",
},
{
name: "Daft Punk",
category: "Music",
subcategory: "Music Artist",
thumbnail: "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png",
link: "https://www.google.com",
},
{
name: "League of Legends",
category: "Games",
subcategory: "Cancer",
thumbnail: "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png",
link: "https://www.google.com",
},
{
name: "Mark Zuckerberg",
category: "Robots",
subcategory: "Evil",
thumbnail: "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png",
link: "https://www.google.com",
},
{
name: "Nhato",
category: "Music Artist",
subcategory: "EDM",
thumbnail: "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png",
link: "https://www.google.com",
},
{
name: "Python",
category: "Tech",
subcategory: "Programming Language",
thumbnail: "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png",
link: "https://www.google.com",
},
{
name: "Tik Tok",
category: "Spyware",
subcategory: "Cancer",
thumbnail: "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png",
link: "https://www.google.com",
},
{
name: "Portal 2",
category: "Video Games",
subcategory: "Puzzler",
thumbnail: "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png",
link: "https://www.google.com",
},
{
name: "Lack of a Better Name",
category: "Music",
subcategory: "Song",
thumbnail: "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png",
link: "https://www.google.com",
},
{
name: "Lucid Dream",
category: "Music",
subcategory: "Song",
thumbnail: "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png",
link: "https://www.google.com",
},
];
body {
overflow-y: hidden;
}
#top-section {
margin-left: 8px;
}
#items-container {
overflow-y: scroll;
height: 400px;
}
#items-container > .list {
background: darkslategrey;
padding: 8px 8px;
}
#items-container .list li {
background: grey;
color: white;
margin-bottom: 8px;
}
@Alfa-Q
Copy link
Author

Alfa-Q commented Aug 27, 2020

I couldn't really find anything covering infinite scrolling using list.js other than a closed issue made 2 years ago, so I decided to make my own utilizing pagination! The best part is that you can still perform searches and can filter the data, it just requires resetting the page variable.

I tested it on a list of over 6000 entries in a separate project that also contained images that were around 250px x 250px, an image size that is too small to be lazy loaded automatically by chrome but the sheer number of requests still made the browser very laggy and unresponsive. This infinite scrolling example solved almost all of my performance issues as it won't request 6000 images immediately. Instead it only loads the next itemsPerPage number of items on each scroll near the bottom of the container. As long as this value is a reasonable amount (i.e. 60) you shouldn't see much of a performance hit.

The above code uses jquery, bootstrap, and list.js although it can certainly be done without jquery and bootstrap. Hopefully it helps anyone else out who is facing similar challenges!


Test It Live

jsfiddle


Feel free to add any suggestions, feedback, or comments. I'm relatively new to this gist stuff.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment