0

When menu is clicked I want it to be fixed not scrollable. How can I remove the event listener on scroll so the menu is staying fixed and scrolling up and down? JavaScript is not my strong skill and if someone with more experience can help me out, it would be greatly appreciated. Thank you

const header = document.querySelector(".hiding-header");
const triggerMenu = document.querySelector(".page-header .trigger-menu");
const nav = document.querySelector(".page-header nav");
const menu = document.querySelector(".page-header .menu");
const scrollUp = "scroll-up";
const scrollDown = "scroll-down";
let lastScroll = 0;

triggerMenu.addEventListener("click", () => {
  header.classList.toggle("menu-open");
});


window.addEventListener("scroll", () => {
  const currentScroll = window.pageYOffset;
  if (currentScroll <= 0) {
    header.classList.remove(scrollUp);
    return;
  }

  if (currentScroll > lastScroll && !header.classList.contains(scrollDown)) {
    // down
    header.classList.remove(scrollUp);
    header.classList.add(scrollDown);
  } else if (
    currentScroll < lastScroll &&
    header.classList.contains(scrollDown)
  ) {
    // up
    header.classList.remove(scrollDown);
    header.classList.add(scrollUp);
  }
  lastScroll = currentScroll;
});
:root {
  --white: #fff;
  --black: #221f1f;
  --lightpurple: #9e91f2;
  --darkgray: #1e1f26;
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

button {
  background: transparent;
  border: none;
  cursor: pointer;
  outline: none;
}

ul {
  list-style: none;
}

a {
  text-decoration: none;
  color: inherit;
}

body {
  position: relative;
  font: 16px/1.5 sans-serif;
  color: var(--white);
  -ms-overflow-style: none;
  /* IE and Edge */
  scrollbar-width: none;
  /* Firefox */
}


/* MAIN RULES
–––––––––––––––––––––––––––––––––––––––––––––––––– */

.trigger-menu-wrapper {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  display: flex;
  justify-content: end;
  padding: 20px;
  z-index: 2;
  background: var(--lightpurple);
  transition: transform 0.4s;
}

.page-header .trigger-menu {
  display: flex;
  align-items: center;
  font-size: 1.3rem;
  color: var(--white);
  letter-spacing: 0.2em;
}

.page-header .trigger-menu svg {
  fill: var(--white);
  margin-right: 8px;
  transition: transform 0.3s;
}

.page-header .menu {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: none;
  text-align: center;
  padding: 15vh 0 5vh;
  overflow: auto;
  z-index: 1;
  background: var(--lightpurple);
}

.page-header .menu a {
  font-size: 3rem;
}

.page-header .sub-menu a {
  font-size: 1.5rem;
}

.lottie-wrapper {
  position: fixed;
  bottom: 50px;
  right: 25px;
  z-index: 1;
  padding: 5px;
  border-radius: 5px;
}

.page-main section {
  position: relative;
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
  height: 100vh;
}

.page-main section::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.25);
}


/* BODY CLASSES
–––––––––––––––––––––––––––––––––––––––––––––––––– */

.menu-open {
  overflow: hidden;
}

.menu-open .trigger-menu-wrapper {
  background: transparent;
  position: fixed;
}

.menu-open .page-header .menu {
  display: block;
}

.menu-open .page-header svg {
  transform: rotate(45deg);
}

.menu-open-with-lottie .page-header .menu {
  padding: 5vh 0;
}

.scroll-down .trigger-menu-wrapper {
  transform: translate3d(0, -100%, 0);
}

.scroll-down .lottie-wrapper {
  background: var(--darkgray);
}

.scroll-up .trigger-menu-wrapper {
  transform: none;
}

.scroll-up:not(.menu-open) .trigger-menu-wrapper {
  background: var(--lightpurple);
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.35);
}


/* FOOTER
–––––––––––––––––––––––––––––––––––––––––––––––––– */

.page-footer {
  position: fixed;
  right: 25px;
  bottom: 10px;
  display: flex;
  align-items: center;
  font-size: 1rem;
  padding: 5px;
  border-radius: 5px;
  background: var(--darkgray);
}

.page-footer a {
  display: flex;
  margin-left: 4px;
}
<script src="https://unpkg.com/@lottiefiles/lottie-player@latest/dist/lottie-player.js"></script>
<header class="hiding-header">
  <nav class="page-header">
    <div class="trigger-menu-wrapper">
      <button class="trigger-menu">
        <svg width="12" height="12" viewBox="0 0 24 24">
          <path d="M24 10h-10v-10h-4v10h-10v4h10v10h4v-10h10z" />
        </svg>
        <span>MENU</span>
      </button>
    </div>
    <ul class="menu">
      <li>
        <a href="">About</a>
        <ul class="sub-menu">
          <li>
            <a href="">History</a>
          </li>
          <li>
            <a href="">President</a>
          </li>
          <li>
            <a href="">Team</a>
          </li>
          <li>
            <a href="">Process</a>
          </li>
          <li>
            <a href="">Clients</a>
          </li>
        </ul>
      </li>

    </ul>
  </nav>
</header>

<!-- <a href="" role="button" aria-label="Toggle menu" class="lottie-wrapper">
  <lottie-player src="https://assets10.lottiefiles.com/datafiles/9gIwZ2uiiKglyb0/data.json" style="width: 60px; height: 60px;"></lottie-player>
</a> -->

<main class="page-main">
  <section style="background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/162656/freedom.jpg);"></section>
  <section style="background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/162656/travel.jpg);"></section>
  <section style="background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/162656/holidays.jpg);"></section>
</main>

<footer class="page-footer">
  <!-- <span>made by </span>
  <a href="https://georgemartsoukos.com/" target="_blank">
    <img width="24" height="24" src="https://assets.codepen.io/162656/george-martsoukos-small-logo.svg" alt="George Martsoukos logo">
  </a> -->
</footer>
Nickofthyme
  • 3,032
  • 23
  • 40
John
  • 13
  • 5

2 Answers2

2

You can use

  removeEventListener(type, listener);

although you may need to make your scroll event a function rather than an anonymous function

const header = document.querySelector(".hiding-header");
const triggerMenu = document.querySelector(".page-header .trigger-menu");
const nav = document.querySelector(".page-header nav");
const menu = document.querySelector(".page-header .menu");
const scrollUp = "scroll-up";
const scrollDown = "scroll-down";
let lastScroll = 0;

triggerMenu.addEventListener("click", () => {
  header.classList.toggle("menu-open");
  window.removeEventListener("scroll", handleScroll)
});


window.addEventListener("scroll", handleScroll);

function handleScroll() {
  const currentScroll = window.pageYOffset;
  if (currentScroll <= 0) {
    header.classList.remove(scrollUp);
    return;
  }

  if (currentScroll > lastScroll && !header.classList.contains(scrollDown)) {
    // down
    header.classList.remove(scrollUp);
    header.classList.add(scrollDown);
  } else if (
    currentScroll < lastScroll &&
    header.classList.contains(scrollDown)
  ) {
    // up
    header.classList.remove(scrollDown);
    header.classList.add(scrollUp);
    }
  lastScroll = currentScroll;
}
s6xy
  • 398
  • 1
  • 11
  • Thank you Jay for your comment, could you show me how to do it it exactly? JS is not my strength. It would be really appreciated. Thank you. – John Aug 31 '22 at 21:21
  • I have edited my answer - please try it! – s6xy Aug 31 '22 at 21:26
  • Thank you so much for your answer. It does, stay fixed when the menu is clicked, but after then when scroll down it stays fixed. I need it to be hidden when scroll down and visible when scroll up after the menu is closed. check this codepen https://codepen.io/johnindreica/pen/YzLKyNE?editors=0110 – John Aug 31 '22 at 21:40
0

To add to @jay's point you cannot call removeEventListener without the original listener function. If you call addEventListener with an anonymous function, you cannot easily remove it, note you could forcibly remove all listeners for DOM elements as mentioned here but it's ugly and not ideal.

Good ✅

const handleScroll = () => {
  console.log('scrolling...');
};
document.addEventListener('scroll', handleScroll);
document.removeEventListener('scroll', handleScroll);

Bad ❌

document.addEventListener('scroll', () => {
  console.log('scrolling...');
})
document.removeEventListener('scroll', () => {
  console.log('scrolling...');
}) // does not throw error but fails to remove listener

Note: It can be a little deceiving calling removeEventListener without getting an error thinking it worked but this is not the case. Removal is attempted but not certain if no matching listeners are found.


More considerations

Using the original listener is one part put the removal of listeners depends on the options as well, see docs for more details.

Dev Tools helper

You can always use the dev tools Event Listeners tab to inspect an element or document.body and see all active listeners to know for sure when listeners are removed.

enter image description here

Solution to disable scroll when menu open

Yeah your question was not really about the scroll listener but how to disable the scroll, as you have found out the listener does not stop the scroll just stops listening.

For what you are looking for there are few options the vary in complexity, but the easiest fastest way to make it work is just to add a class to turn scrolling off on the scrolling element.

In you case you are scrolling the document.body, so we need to add a class to the body when the menu is clicked and remove it when it's clicked again to close.

triggerMenu.addEventListener("click", () => {
  header.classList.toggle("menu-open");
   document.body.classList.toggle('disable-scroll'); // <-- add this line
});
body.disable-scroll {
  overflow: hidden; // prevents scrolling on the body
}

See working demo below...

const header = document.querySelector(".hiding-header");
const triggerMenu = document.querySelector(".page-header .trigger-menu");
const nav = document.querySelector(".page-header nav");
const menu = document.querySelector(".page-header .menu");
const scrollUp = "scroll-up";
const scrollDown = "scroll-down";
let lastScroll = 0;


triggerMenu.addEventListener("click", () => {
  header.classList.toggle("menu-open");
  document.body.classList.toggle('disable-scroll');
});


window.addEventListener("scroll", handleScroll);

function handleScroll(e) {
  const currentScroll = window.pageYOffset;
  if (currentScroll <= 0) {
    header.classList.remove(scrollUp);
    return;
  }

  if (currentScroll > lastScroll && !header.classList.contains(scrollDown)) {
    // down
    header.classList.remove(scrollUp);
    header.classList.add(scrollDown);
  } else if (
    currentScroll < lastScroll &&
    header.classList.contains(scrollDown)
  ) {
    // up
    header.classList.remove(scrollDown);
    header.classList.add(scrollUp);
    }
  lastScroll = currentScroll;
}
:root {
  --white: #fff;
  --black: #221f1f;
  --lightpurple: #9e91f2;
  --darkgray: #1e1f26;
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

button {
  background: transparent;
  border: none;
  cursor: pointer;
  outline: none;
}

ul {
  list-style: none;
}

a {
  text-decoration: none;
  color: inherit;
}

body {
  position: relative;
  font: 16px/1.5 sans-serif;
  color: var(--white);
   -ms-overflow-style: none;  /* IE and Edge */
  scrollbar-width: none;  /* Firefox */
}

body.disable-scroll {
  overflow: hidden;
}


/* MAIN RULES
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.trigger-menu-wrapper {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  display: flex;
  justify-content: end;
  padding: 20px;
  z-index: 2;
  background: var(--lightpurple);
  transition: transform 0.4s;
}

.page-header .trigger-menu {
  display: flex;
  align-items: center;
  font-size: 1.3rem;
  color: var(--white);
  letter-spacing: 0.2em;
}

.page-header .trigger-menu svg {
  fill: var(--white);
  margin-right: 8px;
  transition: transform 0.3s;
}

.page-header .menu {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: none;
  text-align: center;
  padding: 15vh 0 5vh;
  overflow: auto;
  z-index: 1;
  background: var(--lightpurple);
}

.page-header .menu a {
  font-size: 3rem;
}

.page-header .sub-menu a {
  font-size: 1.5rem;
}

.lottie-wrapper {
  position: fixed;
  bottom: 50px;
  right: 25px;
  z-index: 1;
  padding: 5px;
  border-radius: 5px;
}

.page-main section {
  position: relative;
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
  height: 100vh;
}

.page-main section::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.25);
}


/* BODY CLASSES
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.menu-open {
  overflow: hidden;
}

.menu-open .trigger-menu-wrapper {
  background: transparent;
  position: fixed;
}

.menu-open .page-header .menu {
  display: block;
}

.menu-open .page-header svg {
  transform: rotate(45deg);
}

.menu-open-with-lottie .page-header .menu {
  padding: 5vh 0;
}

.scroll-down .trigger-menu-wrapper {
  transform: translate3d(0, -100%, 0);
}

.scroll-down .lottie-wrapper {
  background: var(--darkgray);
}

.scroll-up .trigger-menu-wrapper {
  transform: none;
}

.scroll-up:not(.menu-open) .trigger-menu-wrapper {
  background: var(--lightpurple);
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.35);
}


/* FOOTER
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.page-footer {
  position: fixed;
  right: 25px;
  bottom: 10px;
  display: flex;
  align-items: center;
  font-size: 1rem;
  padding: 5px;
  border-radius: 5px;
  background: var(--darkgray);
}

.page-footer a {
  display: flex;
  margin-left: 4px;
}
<script src="https://unpkg.com/@lottiefiles/lottie-player@latest/dist/lottie-player.js"></script>
<header class="hiding-header">
  <nav class="page-header">
    <div class="trigger-menu-wrapper">
      <button class="trigger-menu">
        <svg width="12" height="12" viewBox="0 0 24 24">
          <path d="M24 10h-10v-10h-4v10h-10v4h10v10h4v-10h10z" />
        </svg>
        <span>MENU</span>
      </button>
    </div>
    <ul class="menu">
      <li>
        <a href="">About</a>
        <ul class="sub-menu">
          <li>
            <a href="">History</a>
          </li>
          <li>
            <a href="">President</a>
          </li>
          <li>
            <a href="">Team</a>
          </li>
          <li>
            <a href="">Process</a>
          </li>
          <li>
            <a href="">Clients</a>
          </li>
        </ul>
      </li>

    </ul>
  </nav>
</header>

<!-- <a href="" role="button" aria-label="Toggle menu" class="lottie-wrapper">
  <lottie-player src="https://assets10.lottiefiles.com/datafiles/9gIwZ2uiiKglyb0/data.json" style="width: 60px; height: 60px;"></lottie-player>
</a> -->

<main class="page-main">
  <section style="background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/162656/freedom.jpg);"></section>
  <section style="background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/162656/travel.jpg);"></section>
  <section style="background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/162656/holidays.jpg);"></section>
</main>

<footer class="page-footer">
  <!-- <span>made by </span>
  <a href="https://georgemartsoukos.com/" target="_blank">
    <img width="24" height="24" src="https://assets.codepen.io/162656/george-martsoukos-small-logo.svg" alt="George Martsoukos logo">
  </a> -->
</footer>

Note: The biggest issues with approach is the scroll bars/width of the page can snap back and forth creating a jarring UI twitch. The second is that it clears the user scroll height, if they scroll to the bottom then open the menu and close it the scroll would jump back to the top. Since you hide the menu unless the user is at the top this doesn't matter in your case. The other issue is not noticeable in this case.

Nickofthyme
  • 3,032
  • 23
  • 40
  • 1
    thanks Nick for detailing all of that. Could you make it work please? I'm super exhausted and can't see it anymore. If you can make a working demo it would be a great help. Thank you. – John Aug 31 '22 at 21:56
  • @John I got you. See updated answer. – Nickofthyme Aug 31 '22 at 23:53
  • Hi Nick thanks a lot for giving me the updates on the code. Yes you are right the approach with disabling the body scroll isn't the prettiest one and I knew about it and kinda wanted to avoid it. I'll keep digging on the other one and hopefully I'll come around a solution. Thank you again and really appreciate your updates on code and explanations. – John Sep 01 '22 at 17:12