1

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.

FattyDev1
  • 56
  • 10

1 Answers1

0

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

traynor
  • 5,490
  • 3
  • 13
  • 23
  • The projects still don't load in – FattyDev1 Jun 30 '23 at 21:24
  • the frontent code should work with `firstElementChild` modification, it could be some error that's hard to spot without reproducible data. In the template, in `projectHTML` there is `${getTimePassed(project.ListedDate)}`, and there is no such function in the code, so that should throw. You seem to be using that function on the server within ejs template, so make sure it's included in the javascript as well – traynor Jul 01 '23 at 20:00
  • I defined the function in the javascript and it still doesn't not load in – FattyDev1 Jul 10 '23 at 04:59
  • that's strange.. I'd guess you have some conflict, perhaps some other HTML/CSS which is interfering somehow.. try isolating and rendering _only_ this template, maybe even remove styles, if it's part of some other template, and then give it a try, rather than adding complete reproducible code – traynor Jul 10 '23 at 10:07
  • also, do you see any errors in browser dev tools, and check server side, try logging `req.query.page`, make sure it's incrementing, and that `projects` contains data. It would be great to include a few projects for testing, given that you made sure that the server side runs without problems, because when tested like I suggested, it works, even with fast scrolling, which is why I think there is some other code on the page interfering, or server side is not sending data or something like that.. – traynor Jul 12 '23 at 09:48
  • I had my code console.log when it goes to the backend route, and the route is being accessed. The page count is also being incremented, and projects does contain data – FattyDev1 Jul 13 '23 at 04:18
  • Also I updated the code with the one I'm using, and I added test projects – FattyDev1 Jul 13 '23 at 04:23
  • 1
    Oops looks like I just didn't define a function your solution worked though thank you! – FattyDev1 Jul 13 '23 at 05:07