skip to Main Content

I’ve set up a template literal that feeds a list of events to the page. There’s also a "Load More" button that pushes an additional event to the page each time the button is clicked.

I’ve already got a ternary statement indicating that if the events list is empty, a message "There’s nothing here." will display.

…but the "Load More" button still displays. If there are no events, then there should be no "Load More" button.

How can I hide the "Load More" button when the events array is empty.

const events = [
  {
    eventsLink: "",
    eventsTitle: "",
    eventsSubTitle: "",
    eventsText: "",
    eventsRegText: ""
  }
];

// const events = [
//   {
//     eventsLink: "https://dev.to",
//     eventsTitle: "Hello Dev",
//     eventsSubTitle: "Dev is here...",
//     eventsText: "Nextiva is proud to participate in Telarus",
//     eventsRegText: "Register Now"
//   },
//   {
//     eventsLink: "",
//     eventsTitle: "Hello Mozilla",
//     eventsSubTitle: "Mozilla is here...",
//     eventsText: "Held in Napa, the heart of the California",
//     eventsRegText: "Register Coming!"
//   },
//   {
//     eventsLink: "https://www.google.com",
//     eventsTitle: "Hello Google",
//     eventsSubTitle: "Google is here...",
//     eventsText: "Held in Silicon Valley, the heart of the technology",
//     eventsRegText: "Register Today!"
//   }
// ];

function eventsPageTemplate(event) {
  return `
    ${
      (event.eventsTitle,
      event.eventsSubTitle,
      event.eventsText,
      event.eventsRegText
        ? `
        <div class="col-sm-12 col-md-6 col-lg-4 event-item">
          <div class="card m-1">
            <div class="card-body">
              <h5 class="card-title">${event.eventsTitle}</h5>
              <h6 class="card-subtitle mb-2 text-muted">${
                event.eventsSubTitle
              }</h6>
              <p class="card-text">${event.eventsText}</p>
              ${
                event.eventsLink
                  ? `<a href="${event.eventsLink}" class="card-link stretched-link">${event.eventsRegText}</a>`
                  : ``
              }
            </div>
          </div>
        </div>
      `
        : `
        <div class="col-sm-12">
          <div class="card">
            <div class="card-body">
              There's nothing here.
            </div>
          </div>
        </div>
      `)
    }
    `;
}

document.getElementById("eventsList").innerHTML = `${events
  .map(eventsPageTemplate)
  .join(" ")}`;

//load more functionality
let list = document.querySelector("#listEvents");
let items = Array.from(list.querySelectorAll(".event-item"));
let loadMore = document.getElementById("loadMore");
maxItems = 1;
loadItems = 1;
hiddenClass = "hiddenStyle";
hiddenItems = Array.from(document.querySelectorAll(".hiddenStyle"));

items.forEach(function (item, index) {
  console.log(item.innerText, index);
  if (index > maxItems - 1) {
    item.classList.add(hiddenClass);
  }
});

loadMore.addEventListener("click", function () {
  [].forEach.call(
    document.querySelectorAll("." + hiddenClass),
    function (item, index) {
      if (index < loadItems) {
        item.classList.remove(hiddenClass);
      }

      if (document.querySelectorAll("." + hiddenClass).length === 0) {
        loadMore.style.display = "none";
      }
    }
  );
});
.hiddenStyle {
  position: absolute;
  overflow: hidden;
  clip: rect(0 0 0 0);
  height: 1px;
  width: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.min.css">

<div class="container p-3" id="listEvents">
  <div class="row" id="eventsList"></div>
  <div class="row mt-3">
    <div class="load-more-button mx-auto">
      <a class="btn btn-md btn-primary text-white" id="loadMore">Load More</a>
    </div>    
  </div>  
</div>

2

Answers


  1. I’m not sure what you intended to do with [].forEach.call(...).

    The load more button’s onclick handler does get fired. You need to reorganise the click handler logic:

    • First do the querySelectorAll to find the list items.
    • If there are none, then set button display to none.
    • Otherwise, resume your existing logic of removing the hidden class for a subset of elements.

    See working snippet below.

    const events = [
      {
        eventsLink: "",
        eventsTitle: "",
        eventsSubTitle: "",
        eventsText: "",
        eventsRegText: ""
      }
    ];
    
    // const events = [
    //   {
    //     eventsLink: "https://dev.to",
    //     eventsTitle: "Hello Dev",
    //     eventsSubTitle: "Dev is here...",
    //     eventsText: "Nextiva is proud to participate in Telarus",
    //     eventsRegText: "Register Now"
    //   },
    //   {
    //     eventsLink: "",
    //     eventsTitle: "Hello Mozilla",
    //     eventsSubTitle: "Mozilla is here...",
    //     eventsText: "Held in Napa, the heart of the California",
    //     eventsRegText: "Register Coming!"
    //   },
    //   {
    //     eventsLink: "https://www.google.com",
    //     eventsTitle: "Hello Google",
    //     eventsSubTitle: "Google is here...",
    //     eventsText: "Held in Silicon Valley, the heart of the technology",
    //     eventsRegText: "Register Today!"
    //   }
    // ];
    
    function eventsPageTemplate(event) {
      return `
        ${
          (event.eventsTitle,
          event.eventsSubTitle,
          event.eventsText,
          event.eventsRegText
            ? `
            <div class="col-sm-12 col-md-6 col-lg-4 event-item">
              <div class="card m-1">
                <div class="card-body">
                  <h5 class="card-title">${event.eventsTitle}</h5>
                  <h6 class="card-subtitle mb-2 text-muted">${
                    event.eventsSubTitle
                  }</h6>
                  <p class="card-text">${event.eventsText}</p>
                  ${
                    event.eventsLink
                      ? `<a href="${event.eventsLink}" class="card-link stretched-link">${event.eventsRegText}</a>`
                      : ``
                  }
                </div>
              </div>
            </div>
          `
            : `
            <div class="col-sm-12">
              <div class="card">
                <div class="card-body">
                  There's nothing here.
                </div>
              </div>
            </div>
          `)
        }
        `;
    }
    
    document.getElementById("eventsList").innerHTML = `${events
      .map(eventsPageTemplate)
      .join(" ")}`;
    
    //load more functionality
    let list = document.querySelector("#listEvents");
    let items = Array.from(list.querySelectorAll(".event-item"));
    let loadMore = document.getElementById("loadMore");
    maxItems = 1;
    loadItems = 1;
    hiddenClass = "hiddenStyle";
    hiddenItems = Array.from(document.querySelectorAll(".hiddenStyle"));
    
    function refreshLoadButtonState() {
      const hiddenElems = Array.from(document.querySelectorAll("." + hiddenClass));
      if (hiddenElems.length === 0) {
        loadMore.style.display = "none";
        return;
      }
      
      hiddenElems.forEach((item, index) => {
        if (index < loadItems) {
            item.classList.remove(hiddenClass);
        }
      });
    }
    
    items.forEach(function (item, index) {
      console.log(item.innerText, index);
      if (index > maxItems - 1) {
        item.classList.add(hiddenClass);
      }
    });
    
    loadMore.addEventListener("click", refreshLoadButtonState);
    
    refreshLoadButtonState();
    .hiddenStyle {
      position: absolute;
      overflow: hidden;
      clip: rect(0 0 0 0);
      height: 1px;
      width: 1px;
      margin: -1px;
      padding: 0;
      border: 0;
    }
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.min.css">
    
    <div class="container p-3" id="listEvents">
      <div class="row" id="eventsList"></div>
      <div class="row mt-3">
        <div class="load-more-button mx-auto">
          <a class="btn btn-md btn-primary text-white" id="loadMore">Load More</a>
        </div>    
      </div>  
    </div>
    Login or Signup to reply.
  2. I am going to suggest to not use templates, put the elements in the markup (hidden) and clone them instead of a problematic string manipulation to inject HTML.

    Not sure what your intent is with the global undeclared and your loadMore is about so I left it alone just to illustrate what I would propose.

    /*const events = [{
      eventsLink: "",
      eventsTitle: "",
      eventsSubTitle: "",
      eventsText: "",
      eventsRegText: ""
    }];*/
    const testEvents = [{
      eventsLink: "https//google.com",
      eventsTitle: "Google",
      eventsSubTitle: "Super Google",
      eventsText: "TestText",
      eventsRegText: "I RockReg"
    }, {
      eventsLink: "https//google.com",
      eventsTitle: "Google",
      eventsSubTitle: "I am ignored",
      eventsText: "TestText",
      eventsRegText: "" // controls if it is used
    }, {
      eventsLink: "https//google.com",
      eventsTitle: "Google",
      eventsSubTitle: "Super Google",
      eventsText: "TestText",
      eventsRegText: "I RockReg"
    }, {
      eventsLink: "https//google.com",
      eventsTitle: "Google",
      eventsSubTitle: "I am ignored",
      eventsText: "TestText",
      eventsRegText: "" // controls if it is used
    }];
    
    function emptyNode() {
      let p = document.getElementById("clone-2");
      let eventItem = p.firstElementChild;
      let p_prime = eventItem.cloneNode(true);
      return p_prime;
    }
    
    function eventNode(event) {
      // console.log("Event:", event);
      let p = document.getElementById("clone-1");
      let eventItem = p.getElementsByClassName("event-item")[0];
      let p_prime = eventItem.cloneNode(true);
      p_prime.getElementsByClassName("card-title")[0]
        .innerHTML = event.eventsTitle;
      p_prime.getElementsByClassName("card-subtitle")[0]
        .innerHTML = event.eventsSubTitle;
      p_prime.getElementsByClassName("card-text")[0]
        .innerHTML = event.eventsText;
      let newlink = p_prime.getElementsByClassName("card-link")[0];
      newlink.href = event.eventsLink;
      newlink.innerHTML = event.eventsRegText;
      return p_prime;
    }
    
    function loadValues(eventItems, count) {
      let loadNumber = !!eventItems.length && !!count && eventItems.length > count ? count :  eventItems.length;
      for (let i=0; i < loadNumber; i++) {
        let newnode = eventNode(eventItems[i]);
        document.getElementById("eventsList").appendChild(newnode);
      }
    }
    
    function showEmptyNode() {
      let newnode = emptyNode();
      document.getElementById("eventsList").appendChild(newnode);
    }
    
    function toggleButton(show) {
      let button = document.querySelector(".load-more-button");
      button.setAttribute('data-state', !!show ? 'shown' : 'hidden');
    }
    
    function showEvents(count) {
      if (testEvents.length > 0) {
        loadValues(testEvents, count);
        toggleButton(true);
      } else {
        // array is empty so show the Nothing and hide the button
        showEmptyNode();
        toggleButton(false);
      }
      if (testEvents.length - count > 0) {
        toggleButton(true);
      }
      let list = document.getElementById("listEvents");
      let items = list.querySelectorAll(".event-item");
      toggleButton(items.length < testEvents.length)
    }
    
     showEvents(3);
    /* not sure what this is all about or the intent so commenting out since it does nothing
    
    //load more functionality
    let list = document.getElementById("listEvents");
    let items = Array.from(list.querySelectorAll(".event-item"));
    let loadMore = document.getElementById("loadMore");
    let maxItems = 1;
    let loadItems = 1;
    let hiddenClass = "hiddenStyle";
    let hiddenItems = Array.from(document.querySelectorAll(".hiddenStyle"));
    
    items.forEach(function(item, index) {
      // console.log(item.innerText, index);
      if (index > maxItems - 1) {
        item.classList.add(hiddenClass);
      }
    });
    
    loadMore.addEventListener("click", function() {
      console.log('yup');
      [].forEach.call(document.querySelectorAll("." + hiddenClass),
        function(item, index) {
          console.log('never happens');
          if (index < loadItems) {
            item.classList.remove(hiddenClass);
          }
          if (document.querySelectorAll("." + hiddenClass).length === 0) {
            loadMore.style.display = "none";
          }
        }
      );
    });
    */
    .hiddenStyle {
      position: absolute;
      overflow: hidden;
      clip: rect(0 0 0 0);
      height: 1px;
      width: 1px;
      margin: -1px;
      padding: 0;
      border: 0;
    }
    
    #clone-1[data-state=hidden],
    #clone-2[data-state=hidden],
    .load-more-button[data-state=hidden] {
      display: none;
    }
    
    .load-more-button[data-state=shown] {
      display: inherit;
      background-color: #AAFFAA;
    }
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.min.css">
    
    <div class="container p-3" id="listEvents">
      <div class="row" id="eventsList"></div>
      <div class="row mt-3">
        <div class="load-more-button mx-auto p-2" data-state="hidden">
          <a class="btn btn-md btn-primary text-white" id="loadMore">Load More</a>
        </div>
      </div>
    </div>
    
    
    <div id="clone-1" data-state="hidden">
      <div class="col-sm-12 col-md-6 col-lg-4 event-item">
        <div class="card m-1">
          <div class="card-body">
            <h5 class="card-title"></h5>
            <h6 class="card-subtitle mb-2 text-muted"></h6>
            <p class="card-text"></p>
            <a href="" class="card-link stretched-link"></a>
          </div>
        </div>
      </div>
    </div>
    <div id="clone-2" data-state="hidden">
      <div class="col-sm-12">
        <div class="card">
          <div class="card-body">
            There's nothing here.
          </div>
        </div>
      </div>
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search