I have this frontend code
<div class="bounties">
<% projects.forEach(function(project, index) { %>
<section class="box" style="margin: 1rem;">
<a href="/project/<%=project.UniqueID%>">
<article class="media" style="background: white;">
<div class="media-content">
<div class="content">
<p>
<span style="font-size: 2rem; color: #2d2d2d;margin-left: -2px;">Earn <strong
style="color: #5cb85c">$<%=project.DollarAmount.toLocaleString()%>!</strong></span>
<br>
<span><small><i class="fa-solid fa-clock" style="margin-right: 0.2rem;"></i> Listed <%= getTimePassed(project.ListedDate) %> </small> </span>
<% if(project.Status == 'open') { %>
<div class="status regular tag" style=""><p style="color:black">Open</p></div>
<% } else if(project.Status == 'claimed') { %>
<div class="status claimed tag">Claimed</div>
<% } else if(project.Status == 'closed') { %>
<div class="status closed tag">Closed</div>
<% } else if(project.Status == 'cancelled') { %>
<div class="status closed tag">Cancelled</div>
<% } else if(project.Status == 'completed') { %>
<div class="status claimed tag">Completed</div>
<% } %>
<style>
.status.regular {
font-size: 12px;
height: fit-content;
padding: 6px 10px;
border-radius: 16px;
background-color: lightblue;
font-weight: 400;
opacity: 1;
font-weight: 400;
color:black
}
.tags {
display: flex;
gap: 8px;
}
.tag {
border-radius: 16px;
background-color: #ffc6c2;
font-size: 12px;
color: #690D0D;
background-color: #690D0D;
border: 1px solid transparent;
transition: .4s border-color;
cursor: pointer;
}
.tag:hover {
border-color: #bb7676;
}
.tag,
.status,
.edit,
.delete {
padding: 8px 12px;
display: flex;
align-items: center;
justify-content: center;
}
.status.closed {
font-size: 12px;
height: fit-content;
padding: 6px 10px;
background-color: #FFCCCB;
border-radius: 16px;
}
.status.claimed {
font-size: 12px;
height: fit-content;
padding: 6px 10px;
background-color: lightgreen;
border-radius: 16px;
}
</style>
<h3> <%=project.Title%>
</h3>
</a>
<p> <%=project.Description%> </p>
<a href="/project/<%=project.UniqueID%>">
</div>
<% project.Tags.forEach(function(tag) { %>
<div class="tag"><%= tag %></div>
<% }); %>
<nav class="level is-mobile">
<div class="level-left"><small>
<button onclick="loadMoreProjects()"
style="border-radius: 5px;"
class="button bot_right is-small bountybutton has-text-black">View More</button>
</small></div>
</nav>
</div>
</article>
</a>
</section>
<% }); %>
</div>
</body>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <!-- Include jQuery library -->
<div class="bounties">
<% projects.forEach(function(project, index) { %>
<section class="box" style="margin: 1rem;">
<a href="/project/<%=project.UniqueID%>">
<article class="media" style="background: white;">
<div class="media-content">
<div class="content">
<p>
<span style="font-size: 2rem; color: #2d2d2d;margin-left: -2px;">Earn <strong
style="color: #5cb85c">$<%=project.DollarAmount.toLocaleString()%>!</strong></span>
<br>
<span><small><i class="fa-solid fa-clock" style="margin-right: 0.2rem;"></i> Listed <%= getTimePassed(project.ListedDate) %> </small> </span>
<% if(project.Status == 'open') { %>
<div class="status regular tag" style=""><p style="color:black">Open</p></div>
<% } else if(project.Status == 'claimed') { %>
<div class="status claimed tag">Claimed</div>
<% } else if(project.Status == 'closed') { %>
<div class="status closed tag">Closed</div>
<% } else if(project.Status == 'cancelled') { %>
<div class="status closed tag">Cancelled</div>
<% } else if(project.Status == 'completed') { %>
<div class="status claimed tag">Completed</div>
<% } %>
<style>
.status.regular {
font-size: 12px;
height: fit-content;
padding: 6px 10px;
border-radius: 16px;
background-color: lightblue;
font-weight: 400;
opacity: 1;
font-weight: 400;
color:black
}
.tags {
display: flex;
gap: 8px;
}
.tag {
border-radius: 16px;
background-color: #ffc6c2;
font-size: 12px;
color: #690D0D;
background-color: #690D0D;
border: 1px solid transparent;
transition: .4s border-color;
cursor: pointer;
}
.tag:hover {
border-color: #bb7676;
}
.tag,
.status,
.edit,
.delete {
padding: 8px 12px;
display: flex;
align-items: center;
justify-content: center;
}
.status.closed {
font-size: 12px;
height: fit-content;
padding: 6px 10px;
background-color: #FFCCCB;
border-radius: 16px;
}
.status.claimed {
font-size: 12px;
height: fit-content;
padding: 6px 10px;
background-color: lightgreen;
border-radius: 16px;
}
</style>
<h3> <%=project.Title%>
</h3>
</a>
<p> <%=project.Description%> </p>
<a href="/project/<%=project.UniqueID%>">
</div>
<% project.Tags.forEach(function(tag) { %>
<div class="tag"><%= tag %></div>
<% }); %>
<nav class="level is-mobile">
<div class="level-left"><small>
<button style="border-radius: 5px;"
class="button bot_right is-small bountybutton has-text-black">View More</button>
</small></div>
</nav>
</div>
</article>
</a>
</section>
<% }); %>
</div>
</body>
<script>
</script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
function getTimePassed(dateString) {
const date1 = new Date(dateString);
if (isNaN(date1.getTime())) {
return "Invalid date";
}
const now = new Date();
const timeDiff = now.getTime() - date1.getTime(); // Time difference in milliseconds
// Define time units in milliseconds
const oneSecond = 1000;
const oneMinute = 60 * oneSecond;
const oneHour = 60 * oneMinute;
const oneDay = 24 * oneHour;
const oneWeek = 7 * oneDay;
const oneMonth = 30 * oneDay;
const oneYear = 365 * oneDay;
// Determine the appropriate time unit and rounded value
if (timeDiff < oneMinute) {
return Math.round(timeDiff / oneSecond) + " seconds ago";
} else if (timeDiff < oneHour) {
return Math.round(timeDiff / oneMinute) + " minutes ago";
} else if (timeDiff < oneDay) {
return Math.round(timeDiff / oneHour) + " hours ago";
} else if (timeDiff < oneWeek) {
return Math.round(timeDiff / oneDay) + " days ago";
} else if (timeDiff < oneMonth) {
return Math.round(timeDiff / oneWeek) + " weeks ago";
} else if (timeDiff < oneYear) {
return Math.round(timeDiff / oneMonth) + " months ago";
} else {
return Math.round(timeDiff / oneYear) + " years ago";
}
}
alert(getTimePassed)
var PAGE_SIZE = 10;
var page = 1;
var isLoading = false;
const projectContainer = document.querySelector('.bounties');
const observer = new IntersectionObserver((entries) => {
// Check if the target element is intersecting with the viewport
if (entries[0].isIntersecting && !isLoading) {
loadMoreProjects();
}
});
const lastProject = document.querySelector('.bounties section:last-child');
if (lastProject) {
observer.observe(lastProject);
}
function loadMoreProjects() {
const nextPage = page + 1;
// Make an AJAX request to the "/loadmore" endpoint with the next page number
$.ajax({
url: '/loadmore',
method: 'GET',
data: { page: nextPage },
success: function (response) {
const projects = response.projects;
const hasMoreProjects = projects.length >= PAGE_SIZE;
const fragment = document.createDocumentFragment();
projects.forEach(function (project) {
const projectHTML = `
<section class="box" style="margin: 1rem;">
<a href="/project/${project.UniqueID}">
<article class="media" style="background: white;">
<div class="media-content">
<div class="content">
<p>
<span style="font-size: 2rem; color: #2d2d2d;margin-left: -2px;">Earn <strong style="color: #5cb85c">$${project.DollarAmount.toLocaleString()}!</strong></span>
<br>
<span><small><i class="fa-solid fa-clock" style="margin-right: 0.2rem;"></i> Listed ${getTimePassed(project.ListedDate)}</small></span>
${
project.Status === 'open'
? '<div class="status regular tag" style=""><p style="color:black">Open</p></div>'
: project.Status === 'claimed'
? '<div class="status claimed tag">Claimed</div>'
: project.Status === 'closed'
? '<div class="status closed tag">Closed</div>'
: project.Status === 'cancelled'
? '<div class="status closed tag">Cancelled</div>'
: project.Status === 'completed'
? '<div class="status claimed tag">Completed</div>'
: ''
}
<h3>${project.Title}</h3>
</p>
<p>${project.Description}</p>
<a href="/project/${project.UniqueID}">
<div class="tags">
${project.Tags.map((tag) => `<div class="tag">${tag}</div>`).join('')}
</div>
<nav class="level is-mobile">
<div class="level-left">
<small>
<button style="border-radius: 5px;" class="button bot_right is-small bountybutton has-text-black">View More</button>
</small>
</div>
</nav>
</a>
</div>
</div>
</article>
</a>
</section>
`;
const tempElement = document.createElement('div');
tempElement.innerHTML = projectHTML;
fragment.appendChild(tempElement.firstChild);
});
projectContainer.appendChild(fragment);
page = nextPage;
isLoading = false;
if (hasMoreProjects) {
observer.observe(document.querySelector('.bounties section:last-child'));
} else {
observer.disconnect();
}
},
error: function (xhr, status, error) {
console.log('Error:', error);
isLoading = false; // Reset the isLoading flag in case of an error
},
beforeSend: function () {
isLoading = true;
},
});
}
</script>
It fetches from this route
app.get('/loadmore', async function (req, res) {
console.log(req.query.page)
console.log('Load More route accessed');
const PAGE_SIZE = 10; // Make sure this matches the frontend code
const page = req.query.page || 1;
const skip = (page - 1) * PAGE_SIZE;
const openProjects = await projectSchema
.find({ Status: 'open', PaidFor: true })
.sort({ ListedDate: -1 })
.skip(skip)
.limit(PAGE_SIZE);
const otherProjects = await projectSchema
.find({ Status: { $ne: 'open' }, PaidFor: true })
.sort({ ListedDate: -1 })
.skip(skip)
.limit(PAGE_SIZE);
const projects = openProjects.concat(otherProjects);
console.log(projects)
res.json({ projects }); // Send the projects as a JSON object
});
Here are some example projects:
const projects = [
{
_id: new ObjectId("64975f944b1fb2bd347d26ff"),
Category: 'Code',
Title: 'yo',
Lister: 'burningfalco99@gmail.com',
Description: 'yyoyoyooyoyoy',
DollarAmount: 22,
ListingFeesCollected: 5,
Tags: [ 'css', 'html', 'javascript' ],
Applicants: [],
Criteria: 'none',
Details: 'yoyoyoyo',
Status: 'open',
DueDate: 2023-06-24T21:26:44.115Z,
ListedDate: 'Sat Jun 24 2023 14:26:44 GMT-0700 (Pacific Daylight Time)',
Discussion: [],
UniqueID: 'fdsfsdfsdf',
PaidFor: true,
StripeSession: '',
__v: 0
},
{
_id: new ObjectId("64975f944b1fb2bd347d2701"),
Category: 'Code',
Title: 'yo',
Lister: 'burningfalco99@gmail.com',
Description: 'yyoyoyooyoyoy',
DollarAmount: 22,
ListingFeesCollected: 5,
Tags: [ 'css', 'html', 'javascript' ],
Applicants: [],
Criteria: 'none',
Details: 'yoyoyoyo',
Status: 'open',
DueDate: 2023-06-24T21:26:44.200Z,
ListedDate: 'Sat Jun 24 2023 14:26:44 GMT-0700 (Pacific Daylight Time)',
Discussion: [],
UniqueID: 'fdsfsdfsdf',
PaidFor: true,
StripeSession: '',
__v: 0
},
{
_id: new ObjectId("64975f944b1fb2bd347d2707"),
Category: 'Code',
Title: 'yo',
Lister: 'burningfalco99@gmail.com',
Description: 'yyoyoyooyoyoy',
DollarAmount: 22,
ListingFeesCollected: 5,
Tags: [ 'css', 'html', 'javascript' ],
Applicants: [],
Criteria: 'none',
Details: 'yoyoyoyo',
Status: 'open',
DueDate: 2023-06-24T21:26:44.452Z,
ListedDate: 'Sat Jun 24 2023 14:26:44 GMT-0700 (Pacific Daylight Time)',
Discussion: [],
UniqueID: 'fdsfsdfsdf',
PaidFor: true,
StripeSession: '',
__v: 0
},
{
_id: new ObjectId("64975f944b1fb2bd347d2709"),
Category: 'Code',
Title: 'yo',
Lister: 'burningfalco99@gmail.com',
Description: 'yyoyoyooyoyoy',
DollarAmount: 22,
ListingFeesCollected: 5,
Tags: [ 'css', 'html', 'javascript' ],
Applicants: [],
Criteria: 'none',
Details: 'yoyoyoyo',
Status: 'open',
DueDate: 2023-06-24T21:26:44.852Z,
ListedDate: 'Sat Jun 24 2023 14:26:44 GMT-0700 (Pacific Daylight Time)',
Discussion: [],
UniqueID: 'fdsfsdfsdf',
PaidFor: true,
StripeSession: '',
__v: 0
},
{
_id: new ObjectId("64975f944b1fb2bd347d2705"),
Category: 'Code',
Title: 'yo',
Lister: 'burningfalco99@gmail.com',
Description: 'yyoyoyooyoyoy',
DollarAmount: 22,
ListingFeesCollected: 5,
Tags: [ 'css', 'html', 'javascript' ],
Applicants: [],
Criteria: 'none',
Details: 'yoyoyoyo',
Status: 'open',
DueDate: 2023-06-24T21:26:44.368Z,
ListedDate: 'Sat Jun 24 2023 14:26:44 GMT-0700 (Pacific Daylight Time)',
Discussion: [],
UniqueID: 'fdsfsdfsdf',
PaidFor: true,
StripeSession: '',
__v: 0
},
{
_id: new ObjectId("64975f944b1fb2bd347d270b"),
Category: 'Code',
Title: 'yo',
Lister: 'burningfalco99@gmail.com',
Description: 'yyoyoyooyoyoy',
DollarAmount: 22,
ListingFeesCollected: 5,
Tags: [ 'css', 'html', 'javascript' ],
Applicants: [],
Criteria: 'none',
Details: 'yoyoyoyo',
Status: 'open',
DueDate: 2023-06-24T21:26:44.935Z,
ListedDate: 'Sat Jun 24 2023 14:26:44 GMT-0700 (Pacific Daylight Time)',
Discussion: [],
UniqueID: 'fdsfsdfsdf',
PaidFor: true,
StripeSession: '',
__v: 0
},
{
_id: new ObjectId("64975f944b1fb2bd347d2703"),
Category: 'Code',
Title: 'yo',
Lister: 'burningfalco99@gmail.com',
Description: 'yyoyoyooyoyoy',
DollarAmount: 22,
ListingFeesCollected: 5,
Tags: [ 'css', 'html', 'javascript' ],
Applicants: [],
Criteria: 'none',
Details: 'yoyoyoyo',
Status: 'open',
DueDate: 2023-06-24T21:26:44.284Z,
ListedDate: 'Sat Jun 24 2023 14:26:44 GMT-0700 (Pacific Daylight Time)',
Discussion: [],
UniqueID: 'fdsfsdfsdf',
PaidFor: true,
StripeSession: '',
__v: 0
},
{
_id: new ObjectId("64975f934b1fb2bd347d26fb"),
Category: 'Code',
Title: 'yo',
Lister: 'burningfalco99@gmail.com',
Description: 'yyoyoyooyoyoy',
DollarAmount: 22,
ListingFeesCollected: 5,
Tags: [ 'css', 'html', 'javascript' ],
Applicants: [],
Criteria: 'none',
Details: 'yoyoyoyo',
Status: 'open',
DueDate: 2023-06-24T21:26:43.948Z,
ListedDate: 'Sat Jun 24 2023 14:26:43 GMT-0700 (Pacific Daylight Time)',
Discussion: [],
UniqueID: 'fdsfsdfsdf',
PaidFor: true,
StripeSession: '',
__v: 0
},
{
_id: new ObjectId("64975f934b1fb2bd347d26f9"),
Category: 'Code',
Title: 'yo',
Lister: 'burningfalco99@gmail.com',
Description: 'yyoyoyooyoyoy',
DollarAmount: 22,
ListingFeesCollected: 5,
Tags: [ 'css', 'html', 'javascript' ],
Applicants: [],
Criteria: 'none',
Details: 'yoyoyoyo',
Status: 'open',
DueDate: 2023-06-24T21:26:43.862Z,
ListedDate: 'Sat Jun 24 2023 14:26:43 GMT-0700 (Pacific Daylight Time)',
Discussion: [],
UniqueID: 'fdsfsdfsdf',
PaidFor: true,
StripeSession: '',
__v: 0
},
{
_id: new ObjectId("64975f884b1fb2bd347d26f5"),
Category: 'Code',
Title: 'yo',
Lister: 'burningfalco99@gmail.com',
Description: 'yyoyoyooyoyoy',
DollarAmount: 22,
ListingFeesCollected: 5,
Tags: [ 'css', 'html', 'javascript' ],
Applicants: [],
Criteria: 'none',
Details: 'yoyoyoyo',
Status: 'open',
DueDate: 2023-06-24T21:26:32.176Z,
ListedDate: 'Sat Jun 24 2023 14:26:32 GMT-0700 (Pacific Daylight Time)',
Discussion: [],
UniqueID: 'fdsfsdfsdf',
PaidFor: true,
StripeSession: '',
__v: 0
}
]
When I scroll to the bottom of the page, more projects are supposed to load in everytime I reach the bottom of the rendered projects.
However, if I reach the bottom of the page, it console.logs "Load More route accessed", meaning that the route has been accessed, and the code is running. This happens sometimes only. However, even with the route being accessed, the new projects are not rendered.
How do I fix this, and also ensure new projects are rendered everytime I reach the bottom of the current projects.
I have been trying to fix this for weeks, so I appreciate your time on helping me.
The section
element isn't appended, because tempElement.firstChild
is actually a newline character from template literal, so try using firstElementChild
instead to get the section
HTML element:
try replacing
fragment.appendChild(tempElement.firstChild);
with
fragment.appendChild(tempElement.firstElementChild);
However, fast scrolling will cause problems: when scrolling fast, it might not load, because observer is async, so it might not catch the last section
yet, in that case see: Intersection Observer fails sometimes when i scroll fast