0

I am trying to get a function to be called on a click on any of the divs with the class of "service-option". The function isn't running. JavaScript functions. And it appears the serviceOptions is just null when outputted into the console.

Here is my index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="/css/styles.css" />
    <script src="/javascript/script.js"></script>
    <script
      src="https://kit.fontawesome.com/fe76fcb7fd.js"
      crossorigin="anonymous"
    ></script>
    <title>AccessGo | Select Service</title>
  </head>
  <body>
    <nav>
      <div id="back-button"><i class="fa-solid fa-arrow-left"></i></div>

      <div id="profile-head"><i class="fa-solid fa-user"></i></div>
    </nav>
    <div id="service-title">
      <h1>Select a type of<span class="next-line">Service</span></h1>
    </div>
    <div id="routing-title">
      <h1>
        Generating route
        <span class="next-line">for you <span class="loading"></span></span>
      </h1>
    </div>
    <div id="service-container">
      <div class="service-option">
        <i class="fa-solid fa-wheelchair"></i>
        <p class="service-name">Wheelchair</p>
      </div>

      <div class="service-option">
        <i class="fa-regular fa-eye-slash"></i>
        <p class="service-name">Visual</p>
      </div>

      <div class="service-option">
        <i class="fa-solid fa-ear-deaf"></i>
        <p class="service-name">Auditory</p>
      </div>
    </div>
    <div id="routing-container">
      <img src="/assets/images/routing.png" />
    </div>
    <footer id="footer">
      © Copyright, IXD5205, Kee-Fung Anthony Ho, 2023.
    </footer>
  </body>
</html>

Here is my javascript:

console.log("JS is here");

const serviceOptions = document.querySelectorAll(".service-option");

console.log(serviceOptions);

serviceOptions.forEach((serviceOption) => {
  serviceOption.addEventListener("click", generateRoute);
});

function generateRoute() {
  console.log("click registered");
  const serviceTitle = document.getElementById("service-title");
  const serviceContainer = document.getElementById("service-container");
  const routingTitle = document.getElementById("routing-title");
  const routingContainer = document.getElementById("routing-container");
  const profileHead = document.getElementById("profile-head");

  // hide service elements
  serviceTitle.style.display = "none";
  serviceContainer.style.display = "none";
  profileHead.style.display = "none";

  // show routing elements
  routingTitle.style.display = "block";
  routingContainer.style.display = "block";

  console.log(
    "generateRoute function is activated and service elements should be gone and routing elements should be showing"
  );
}

And in case it matters, here is my CSS:

@import url("https://fonts.googleapis.com/css2?family=Inter:wght@500&family=Roboto:wght@400&display=swap");

/* IMPORTED FONTS
* + Roboto (Medium 500)
* + Inter (Medoum 500)
*/

/* ==================== UNIVERSAL ==================== */
html {
  box-sizing: border-box;
  scroll-behavior: smooth;
  background-color: #000;
  font-family: "Roboto", "Inter", Arial, Helvetica, sans-serif;
  color: #fff;
  margin: 0;
  padding: 0;
}

*,
*::before,
*::after {
  box-sizing: inherit;
}

/***** NAV ******/

#back-button,
#profile-head {
  position: absolute;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  display: flex;
  justify-content: center;
  align-items: center;
  cursor: pointer;
}

#back-button {
  top: 12px;
  left: 12px;
  background-color: #000;
  border: 2px solid #fff;
}

#profile-head {
  top: 12px;
  background-color: lightgrey;
  border: 2px solid lightgrey;
  color: #000;
  right: 12px;
  font-size: 20px;
}

/****** Service Elements ******/
#service-title,
#routing-title {
  margin-top: 64px;
  margin-left: 24px;
}

#service-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 24px;
}

.next-line {
  display: block;
}

.service-option {
  background-color: #fff;
  text-align: center;
  border: 0.5px solid #575757;
  border-radius: 10px;
  width: 280px;
  height: 100px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

.service-option i {
  color: #000;
  font-size: 36px;
  margin-top: 24px;
}

.service-name {
  font-family: "Inter";
  color: #000;
}

/****** Routing Elements ******/

#routing-title {
  display: none;
}

#routing-container {
  display: none;
  filter: grayscale(100%);
}

.loading::after {
  content: "...";
  animation: ellipsis 2.5s infinite;
}

@keyframes ellipsis {
  25% {
    content: "";
  }
  50% {
    content: ".";
  }
  75% {
    content: "..";
  }
  100% {
    content: "...";
  }
}

#footer {
  font-size: 8px;
  font-family: "Roboto", "Inter";
  text-align: center;
  color: #fff;
  margin-top: 16px;
}

Why is this?

I simply want the function to be called so clicking any of the service-option divs will hide the service-container and service-title and the profile-head, and then show the routing-title and routing-container that's currently hidden.

I've tried using document.getElementsByClassName. That one returns a list of divs but the for loop won't work.

Sonnto
  • 1
  • 2
  • In the above, it says "querySelector(".service-option") but it actually is "querySelectorAll(".service-option") on my actual file. – Sonnto Apr 17 '23 at 04:04
  • Don't put that in a comment, click the Edit button and fix it. – Barmar Apr 17 '23 at 04:05
  • 1
    `querySelectorAll()` never returns `null`. If nothing matches the selector it returns an empty nodelist, not null. `querySelector()` will return `null` if it doesn't find anything. – Barmar Apr 17 '23 at 04:06
  • I guess, you have to wait until [DOM is loaded](https://developer.mozilla.org/en-US/docs/Web/API/Window/DOMContentLoaded_event) before searching for a HTML element – rzlvmp Apr 17 '23 at 04:10

1 Answers1

0

The problem is that you are using document.querySelector() instead of document.querySelectorAll(). querySelector returns only the first element that matches the given selector, while querySelectorAll returns a NodeList of all the elements matching the selector.

Here's the corrected JavaScript code:

console.log("JS is here");

const serviceOptions = document.querySelectorAll(".service-option");

console.log(serviceOptions);

serviceOptions.forEach((serviceOption) => {
  serviceOption.addEventListener("click", generateRoute);
});

function generateRoute() {
  console.log("click registered");
  const serviceTitle = document.getElementById("service-title");
  const serviceContainer = document.getElementById("service-container");
  const routingTitle = document.getElementById("routing-title");
  const routingContainer = document.getElementById("routing-container");
  const profileHead = document.getElementById("profile-head");

  // hide service elements
  serviceTitle.style.display = "none";
  serviceContainer.style.display = "none";
  profileHead.style.display = "none";

  // show routing elements
  routingTitle.style.display = "block";
  routingContainer.style.display = "block";

  console.log(
    "generateRoute function is activated and service elements should be gone and routing elements should be showing"
  );
}

Also, make sure that you defer the loading of your script or move the script tag to the bottom of the body so that the DOM is fully loaded before your JavaScript code runs:

<!-- Either use defer -->
<script defer src="/javascript/script.js"></script>
<!-- Or move the script tag to the end of the body -->
</body>
<script src="/javascript/script.js"></script>
</html>

With these changes, your code should work as intended.

druid
  • 1
  • 2
  • Hi, thank you. Someone mentioned in the comments that I might have to wait for the HTML DOM to load. I added defer and I think it functions now. Thank you! – Sonnto Apr 17 '23 at 04:14