Skip to content

Commit

Permalink
Fix and refactor Infinite scrolling with vanilla JS (#428)
Browse files Browse the repository at this point in the history
* fix(issue-387): add difficulty filter for challenges

* fix(issue-397): add viewport meta tag to html

* fix(issue-397): get posts based on doc height

* fix(issue-397): add resize event listener to rerender posts based on doc height

* fix(issue-397): add error handling and api retries
  • Loading branch information
harireddy7 authored Mar 10, 2024
1 parent 7980643 commit 473adf5
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 41 deletions.
10 changes: 9 additions & 1 deletion apps/javascript/src/challenges/infinite-scroll/index.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="index.js" type="module"></script>
<script src="../../helpers/header.js" type="module"></script>
<link rel="stylesheet" href="style.css" />
Expand All @@ -11,6 +13,12 @@
<div class="loader">
<h2>Loading...</h2>
</div>
<div class="end-of-content">
<h2>End of content</h2>
</div>
<div class="fetch-error">
<h2>Error occured while fetching posts, try refreshing!</h2>
</div>
</div>
</body>
</html>
126 changes: 94 additions & 32 deletions apps/javascript/src/challenges/infinite-scroll/index.js
Original file line number Diff line number Diff line change
@@ -1,59 +1,121 @@
// HTML ELEMENTS
const container = document.querySelector('.post-container');
const loader = document.querySelector('.loader');
let start = 0;
let end = start + 14;
let posts = [];
const endOfContentEl = document.querySelector('.end-of-content');
const errorEl = document.querySelector('.fetch-error');

// LOCAL STATE
let startIndex = 0;
let endIndex = getNextPostsCount(startIndex);
let isFetching = false;
let isError = false;
let endOfContent = false;
let attempt = 0;
const MAX_RETRIES = 3;

// Calculate window's height and get posts count based on start number
function getNextPostsCount(start) {
const postHeight = 90;
const newPostCount = Math.ceil(window.innerHeight / postHeight);
return start + newPostCount;
}

//Add Posts to DOM
function addPosts() {
posts.forEach((post, ind) => {
const postContainer = document.createElement('p');
// Add Posts to DOM
function addPosts(posts = []) {
posts.forEach((post, index) => {
const postContainer = document.createElement('div');
postContainer.className = 'post';
const postContent = document.createTextNode(post.body + ind);
postContainer.appendChild(postContent);

const postNumberEl = document.createElement('span');
postNumberEl.className = 'post-number';
postNumberEl.textContent = startIndex + index + 1;

const postContentEl = document.createElement('span');
postContentEl.className = 'post-body';
postContentEl.textContent = post.body;

postContainer.appendChild(postNumberEl);
postContainer.appendChild(postContentEl);
container.appendChild(postContainer);
});
}

function handleLoader(loaderStatus) {
loader.style.display = loaderStatus;
// Show end of content on DOM
function showEndContent() {
endOfContentEl.style.display = 'block';
}

//api call
const url = `https://jsonplaceholder.typicode.com/posts?_start=${start}&_end=${end}`;
function toggleError(display) {
errorEl.style.display = display;
}

// Show/Hide Loading text on DOM
function toggleLoader(loaderStatus) {
loader.style.display = loaderStatus;
}

function getPosts() {
// Fetch posts by start and end numbers from server
function fetchPostsApi(start, end) {
const url = `https://jsonplaceholder.typicode.com/posts?_start=${start}&_end=${end}`;
isFetching = true;
handleLoader('block');
toggleError('none');
toggleLoader('block');
setTimeout(async () => {
try {
const res = await fetch(url);
const json = await res.json();
posts = json;
addPosts(posts);
start = end;
end = start + 14;
const posts = await res.json();

// End of content when posts are less than requested posts
if (posts.length < end - start) {
endOfContent = true;
toggleLoader('none');
if (posts.length > 0) {
addPosts(posts);
}
showEndContent();
} else {
addPosts(posts);
startIndex = end;
endIndex = getNextPostsCount(startIndex);
}
attempt = 0;
isError = false;
} catch (err) {
console.log(err);

// Retry incase of API error
attempt++;
const renderedPosts = document.getElementsByClassName('post').length;

if (attempt > MAX_RETRIES) {
toggleError('block');
isError = true;
} else if (renderedPosts === 0) {
fetchPostsApi(start, end);
}

toggleLoader('none');
} finally {
isFetching = false;
handleLoader('none');
}
}, 500);
}

//initial Load for posts
getPosts();
// Fetch initial posts on page load
fetchPostsApi(startIndex, endIndex);

//scroll eventListener
window.addEventListener('scroll', () => {
if (isFetching) {
handleLoader('block');
return;
// verify app state and call fetch posts api
function checkAndGetPosts() {
if (isFetching || endOfContent || isError) return;
const scrolledHeight = Math.ceil(window.innerHeight + window.scrollY);
const docOffset = window.document.body.offsetHeight - 36;
if (scrolledHeight >= docOffset) {
fetchPostsApi(startIndex, endIndex);
}
}

if (window.innerHeight + window.scrollY >= window.document.body.offsetHeight) {
getPosts();
}
});
//scroll eventListener
window.addEventListener('scroll', checkAndGetPosts);

// resize eventListener
window.addEventListener('resize', checkAndGetPosts);
41 changes: 33 additions & 8 deletions apps/javascript/src/challenges/infinite-scroll/style.css
Original file line number Diff line number Diff line change
@@ -1,14 +1,39 @@
.container {
display: flex;
flex-direction: column;
padding: 1rem;
display: flex;
flex-direction: column;
padding: 1rem;
}

.post {
padding: 1rem;
border: 1px solid;
border-radius: 8px;
display: flex;
align-items: center;
padding: 0.5rem;
margin-block: 1rem;
border: 1px solid;
border-radius: 8px;
}

.post-body {
padding-inline: 1rem;
}

.post-number {
padding: 1rem;
background-color: oldlace;
border-radius: 8px;
}

.loader {
text-align: center;
}
text-align: center;
}

.end-of-content {
display: none;
text-align: center;
}

.fetch-error {
display: none;
text-align: center;
color: red;
}

0 comments on commit 473adf5

Please sign in to comment.