Skip to content

Commit 77c01a3

Browse files
author
puneet baser
committed
full code commit
0 parents  commit 77c01a3

File tree

2 files changed

+179
-0
lines changed

2 files changed

+179
-0
lines changed

index.html

+172
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<meta name="viewport" content="width=300, initial-scale: .3" />
4+
<head>
5+
<title>Page Title</title>
6+
7+
<style>
8+
body {
9+
box-sizing: border-box;
10+
margin: 0;
11+
padding: 0;
12+
width: 100vw;
13+
height: 90vh;
14+
}
15+
ul {
16+
list-style: none;
17+
}
18+
.wrapper {
19+
height: 600px;
20+
overflow: scroll;
21+
}
22+
.container {
23+
padding-bottom: 300px;
24+
}
25+
</style>
26+
</head>
27+
<body>
28+
<h1>Infinte Scroll Vanilla JS, <i>The Right Way</i></h1>
29+
<div class="wrapper">
30+
<ul class="container" id="users"></ul>
31+
</div>
32+
33+
<script>
34+
(function(container) {
35+
const scrollBody = document.querySelector(".wrapper");
36+
const contentparent = document.querySelector(".container");
37+
const numberOfRecords = 20;
38+
39+
const imageBaseUrl =
40+
"https://via.placeholder.com/500x300.png/0000FF%20C/O%20https://placeholder.com/?text=image";
41+
const title = "Placeholder";
42+
43+
let currentIndex = 0; // To track the index for getting the updated Data.
44+
let previousIntersectionRatio = 1; // To compare with current intersection ratio
45+
46+
// Sample collection
47+
const data = Array(100)
48+
.fill(0)
49+
.map((each, index) => {
50+
return {
51+
title: title + index,
52+
src: imageBaseUrl + index
53+
};
54+
});
55+
56+
const handleIntersection = (entries, observer) => {
57+
entries.forEach(entry => {
58+
const id = entry.target.id;
59+
const intersectionRatio = entry.intersectionRatio;
60+
let scrollDirection;
61+
if (id === "record-0") {
62+
// Top element intersecting
63+
if (intersectionRatio > previousIntersectionRatio) {
64+
scrollDirection = "up";
65+
currentIndex = getSlidingWindow(scrollDirection);
66+
adjustPadding(scrollDirection);
67+
getMore(contentparent);
68+
}
69+
} else if (id === "record-19") {
70+
// Bottom element intersecting
71+
if (intersectionRatio > previousIntersectionRatio) {
72+
scrollDirection = "down";
73+
currentIndex = getSlidingWindow(scrollDirection);
74+
adjustPadding(scrollDirection);
75+
getMore(contentparent);
76+
}
77+
}
78+
79+
previousIntersectionRatio = entry.intersectionRatio;
80+
});
81+
};
82+
83+
const intersectionObserver = new IntersectionObserver(
84+
handleIntersection,
85+
{
86+
root: scrollBody,
87+
threshold: [0, 0.25, 0.5, 0.75, 1]
88+
}
89+
);
90+
91+
const adjustPadding = scrollDirection => {
92+
let topPadding = contentparent.style.paddingTop || 0;
93+
let bottomPadding = contentparent.style.paddingBottom || 0;
94+
95+
topPadding = parseInt(topPadding, 10);
96+
bottomPadding = parseInt(bottomPadding, 10);
97+
98+
const paddingToBeApplied = 344 * (numberOfRecords / 2);
99+
100+
if (scrollDirection === "down") {
101+
contentparent.style.paddingTop =
102+
topPadding + paddingToBeApplied + "px";
103+
104+
contentparent.style.paddingBottom =
105+
bottomPadding === 0
106+
? "0px"
107+
: bottomPadding - paddingToBeApplied + "px";
108+
} else {
109+
contentparent.style.paddingBottom =
110+
bottomPadding + paddingToBeApplied + "px";
111+
112+
contentparent.style.paddingTop =
113+
topPadding === 0 ? "0px" : topPadding - paddingToBeApplied + "px";
114+
}
115+
};
116+
117+
const getSlidingWindow = scrollDirection => {
118+
let firstIndex;
119+
120+
if (scrollDirection === "down") {
121+
firstIndex = currentIndex + numberOfRecords / 2;
122+
} else {
123+
firstIndex = currentIndex - numberOfRecords / 2;
124+
}
125+
126+
if (firstIndex < 0) firstIndex = 0;
127+
128+
return firstIndex;
129+
};
130+
131+
const getMore = body => {
132+
for (i = 0; i < numberOfRecords; i++) {
133+
const li = document.querySelector(`#record-${i}`);
134+
135+
li.firstElementChild.childNodes[0].nodeValue =
136+
data[i + currentIndex].title;
137+
li.lastElementChild.setAttribute("src", data[i + currentIndex].src);
138+
}
139+
};
140+
141+
const getFirstFewRecords = body => {
142+
for (i = 0; i < numberOfRecords; i++) {
143+
const li = document.createElement("li");
144+
li.setAttribute("id", `record-${i}`);
145+
146+
const recordTitle = document.createElement("h3");
147+
const recordTitleText = document.createTextNode(data[i].title);
148+
recordTitle.appendChild(recordTitleText);
149+
150+
const recordImg = document.createElement("img");
151+
recordImg.setAttribute("src", data[i].src);
152+
153+
li.appendChild(recordTitle);
154+
li.appendChild(recordImg);
155+
body.appendChild(li);
156+
}
157+
};
158+
159+
// Get initial 20 records
160+
getFirstFewRecords(contentparent);
161+
162+
const topElement = document.querySelector("#record-0");
163+
const bottomElement = document.querySelector("#record-19");
164+
165+
/* Observe the top and bottom li to identify the scrolling direction
166+
and if user has reached at top or bottom of the page **/
167+
intersectionObserver.observe(topElement);
168+
intersectionObserver.observe(bottomElement);
169+
})("#users");
170+
</script>
171+
</body>
172+
</html>

readme.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<p>
2+
In DOM, only 20 li elements are added, and based on user's scroll these 20 elements are updated with data,
3+
<ol>
4+
<li>Intersection observer used to detect bottom and top of the page instead of scroll event</li>
5+
<li>DOM is not heavy, only limited elements</li>
6+
</ol>
7+
</p>

0 commit comments

Comments
 (0)