JavaScript Event Listener causing the Sticky Position to Break

I am currently dealing with a situation where I want to create a slide-in side menu that remains sticky. However, when I apply the position: sticky CSS property to my menu container, it causes the entire menu to be displayed (instead of being pushed off the screen with a negative left value), and the button used to open and close the menu stops functioning.

Interestingly, the menu animation works perfectly fine when the container is set to absolute positioning. This leads me to question whether there is a missing element in order for both functionalities to work together, or if it is simply not possible to achieve the desired outcome in the way I am attempting.

You can view the issue in action on this CodePen

Below is an extract of the CSS:

#side-menu-container{
  width: 100%;
  max-width: 300px;
  height: 100%;
  min-height: 1000px;
  position: absolute;
  left: 0;
  top: 0;
}

/* Changes to positioning cause issues here */
#side-menu{
  width: 100%;
  max-width: 330px;
  padding: 1rem 0;
  background: #cccccc;
  position: sticky;
  left: -300px;
  top: 100px;
}

#side-menu ul{
  text-align: center;
}

#side-menu li{
  list-style-type: none;
  margin: 1rem;
}

#side-menu-btn{
  width: 50px;
  height: 50px;
  position: absolute;
  left: 100%;
  background: #cccccc;
  cursor: pointer;
}

#side-menu.side-menu-open{
  left: 0;
}

I have attempted setting the main container element, which holds both the button and the menu, as relative, and I have also experimented with applying z-index values to different elements, but these attempts have not provided a solution. This makes me believe that there might not be an overlapping element causing the issue.

Answer №1

All your issues are outlined in this code.

  1. Your parent div, the container, has a style of left: 0;
  2. Remove the css height from your main element.
  3. The added class side-menu-open is not triggered by your javascript due to the parent element.

I trust this information is helpful for you. Feel free to ask any questions in the comments below. 😊

const sideMenu = document.querySelector('#side-menu-container');
const sideMenuBtn = document.querySelector('#side-menu-btn');


sideMenuBtn.addEventListener('click', () => {
  sideMenu.classList.toggle('side-menu-open');
})
*{
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

main{
  width: 100%;
}

section {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  margin: 2rem auto;
  position: relative;
}

h1{
  width: 100%;
  max-width: 750px;
  margin-bottom: 2rem;
  text-align: center;
}

section p{
  width: 100%;
  max-width: 750px;
  margin-bottom: 1rem;
  line-height: 25px;
}

#side-menu-container{
  width: 100%;
  max-width: 300px;
  height: 100%;
  position: absolute;
  left: -300px;
  top: 0;
}

/* if I change the positioning here, the event will work */
#side-menu{
  width: 100%;
  max-width: 330px;
  padding: 1rem 0;
  background: #cccccc;
  position: sticky;
  top: 100px;
}

#side-menu ul{
  text-align: center;
}

#side-menu li{
  list-style-type: none;
  margin: 1rem;
}

#side-menu-btn{
  width: 50px;
  height: 50px;
  position: absolute;
  left: 100%;
  background: #cccccc;
  cursor: pointer;
}

#side-menu-container.side-menu-open{
  left: 0;
}
<main>

 <section>
   <div id="side-menu-container">
     <div id="side-menu">
       <div id="side-menu-btn">Open</div>
       <ul>
         <li><a href="">Some Link</a></li>
         <li><a href="">Some Link</a></li>
         <li><a href="">Some Link</a></li>
         <li><a href="">Some Link</a></li>
         <li><a href="">Some Link</a></li>
         <li><a href="">Some Link</a></li>
       </ul>
     </div> 
   </div>
   

 <h1>I'm Just Here to be Here</h1>

  <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. In sit amet finibus neque. Praesent fermentum consequat neque, ac iaculis eros maximus et. Suspendisse aliquet non sem sed eleifend. Sed pretium arcu id nulla sollicitudin venenatis. Nullam posuere ipsum eget pharetra ultrices. Aliquam erat volutpat. Duis quam tortor, dignissim at efficitur vel, volutpat eget orci. Nulla tristique mi odio, viverra rutrum odio molestie sed. Maecenas faucibus justo non rhoncus consequat. Vestibulum quis elementum purus.</p>
  <p>Suspendisse eu leo egestas, euismod justo at, blandit risus. Nam dapibus ac ante vitae pellentesque. Etiam congue ligula ut ante mollis mollis. Interdum et malesuada fames ac ante ipsum primis in faucibus. Ut egestas laoreet pulvinar. Cras sed neque nec est scelerisque maximus in eget turpis. Etiam pharetra accumsan justo, ac pellentesque libero dignissim et. Curabitur id egestas ligula, nec tristique elit. Aenean fringilla urna justo, a congue felis feugiat quis.</p>
  <p>Praesent vehicula consequat enim ac consectetur. Suspendisse eget sem nec mi posuere volutpat sit amet nec eros. Pellentesque et laoreet velit, ut ultricies sapien. Integer hendrerit lorem arcu, in consequat arcu accumsan quis. Sed diam nisl, fermentum id tristique sed, placerat vitae eros. Fusce pulvinar mi urna, in ultrices justo suscipit eget. Proin ut nunc arcu. Nullam dictum ut nulla pulvinar tempus. Sed enim sapien, lacinia vitae ullamcorper non, tempor sed velit. Duis euismod, turpis sed gravida euismod, odio purus finibus tortor, nec euismod libero risus porttitor velit. Duis suscipit erat a fermentum malesuada. Maecenas quam lacus, accumsan sed nunc id, fermentum lobortis augue. Etiam id tortor eros. Ut tincidunt iaculis felis, et efficitur arcu elementum et.</p>

</section>

</main>

Answer №2

Problem

The issue with the negative left value for #side-menu is that position sticky keeps the element within the normal flow of the document, preventing it from moving outside its containing elements like absolute or fixed positioned elements.

Resolution

To resolve this issue, set the left value of #side-menu-container to -300px and adjust the JavaScript code to target #side-menu-container instead of #side-menu directly. This adjustment will work as intended because #side-menu-container is absolutely positioned, allowing it to be placed outside the window or nearest ancestor with a relative positioning.

For further explanation on relative positioning, refer to MDN:

The element is positioned according to the normal flow of the document, and then offset relative to its nearest scrolling ancestor and containing block (nearest block-level ancestor), including table-related elements, based on the values of top, right, bottom, and left. The offset does not affect the position of any other elements.

https://developer.mozilla.org/en-US/docs/Web/CSS/position

I have made adjustments to the code provided below.

CSS

#side-menu-container{
  width: 100%;
  max-width: 300px;
  height: 100%;
  min-height: 1000px;
  position: absolute;
  left: -300px;
  top: 0;
}

/* Changing positioning here will make the event work */
#side-menu{
  width: 100%;
  max-width: 330px;
  padding: 1rem 0;
  background: #cccccc;
  position: sticky;
  top: 100px;
}

#side-menu ul{
  text-align: center;
}

#side-menu li{
  list-style-type: none;
  margin: 1rem;
}

#side-menu-btn{
  width: 50px;
  height: 50px;
  position: absolute;
  left: 100%;
  background: #cccccc;
  cursor: pointer;
}

#side-menu-container.side-menu-open{
  left: 0;
}

JavaScript

const sideMenu = document.querySelector('#side-menu-container');
const sideMenuBtn = document.querySelector('#side-menu-btn');


sideMenuBtn.addEventListener('click', () => {
  sideMenu.classList.toggle('side-menu-open');
});

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Utilizing Django to recycle an HTML block for multiple for loops within the same template

I have three specific states for an object - 1. to start, 2. started, and 3. finished. Upon filtering the objects in view, I pass three variables to the template - tostart_objects, started_objects, and finished_objects. My current approach involves loop ...

Looking to manipulate the form submit data before it is submitted using the standard submit event and jQuery? Read on to learn how to remove, enhance, or change

Is there a way to extract data from a form like the one below? <form action="/search/search" data-remote="true" data-type="json" id="search_form" method="get"> <div class="input-group"> <span class="input-group-addon"> <i ...

What might prevent an onSubmit event from triggering the execution of "checkTheForm()"?

Despite consuming a substantial amount of information on the internet, I still find myself puzzled by why my code isn't functioning as expected. I acknowledge that there are numerous tutorials out there guiding me to use <form action="index.html" o ...

As the user scrolls down, the navigation becomes obscured by the content

Whenever I scroll down on my website at Bradly Spicer, the navigation menu extends and ends up falling behind the content. This is the script I am using: .menu { position: relative; z-index:9999; } .post { background: #fff; border: 1px solid #ddd; font- ...

Optimal approach for handling large JSON files

I am in possession of a JSON file containing approximately 500 lines. I am hesitant to simply dump this JSON data into the end of my Node.JS file as it doesn't seem like the most efficient approach. What alternatives or best practices can be recommend ...

Merge HTML documents with this powerful JavaScript library

Our team has developed an email generator that creates HTML emails based on specific parameters. Users can simply select or deselect options and the email will be regenerated accordingly. Occasionally, users may need to make minor manual changes to the co ...

The attempt to install myapp using npx create-react-app has failed. The installation has been aborted

Attempting to create a new project using npx create-react-app my-app resulted in an error message saying Aborting installation. Initially, I had node v14.17.1 installed. Upgrading to the latest version 16.4.0 did not resolve the issue. Even running sudo ...

What is the best way to structure my POST data when conducting tests on an express API endpoint?

I have been exploring the MERN stack with the help of this guide: https://www.digitalocean.com/community/tutorials/getting-started-with-the-mern-stack. Currently, I am trying to test a POST API endpoint that is built using express. The node server is up ...

Struggling to set the value for a variable within an Angular factory?

When dealing with a variable as an array, I have no trouble pushing objects inside and retrieving the values within the controller. However, when trying to directly assign an object to that variable, I run into issues. If anyone can assist me in achieving ...

Error Message: The function "menu" is not a valid function

I've encountered an issue with a function not being called properly. The error message states "TypeError: menu is not a function." I attempted to troubleshoot by moving the function before the HTML that calls it, but unfortunately, this did not resolv ...

Modify the templateUrl in routeProvider according to the user's authorization

Is there a way to dynamically change the templateUrl in the router provider based on user permissions? Here's an example of what I'd like to achieve: $routeProvider .when('/',{ if(user) templateUrl:'app/views/pages/h ...

Sending the content of a textBox to a JavaScript function

Although my question may be somewhat outdated, after exhausting all possible solutions, I am still unable to find a resolution. I am trying to create a function for verifying input fields: This particular function is functioning correctly: function myFu ...

The success of a jquery ajax call always seems to elude me as it consistently throws

I've been facing an issue with my code snippet. The problem lies in the fact that the function on success is never executed; instead, it always triggers the error section. $.ajax({ url: 'http://localhost/zd/get_list', data: 'ter ...

Shifting the Ion Menu side dynamically based on screen size: A step-by-step guide

Working with Ionic 4, I encountered the need to dynamically change the side property of ion-menu. On larger screens, ion-menu is always visible or static, whereas on smaller screens, it remains hidden until the user clicks on the ion-menu-button. My goal i ...

Looking to optimize the JavaScript code for improved performance speed

Is there a more efficient way to avoid writing the same lines of code repeatedly without compromising performance? I've attempted using a for loop to categorize fields as 'mandatory' or 'optional', but it still requires duplicating ...

PHP Ajax Image Uploading and Storage

Is there a way to effortlessly upload an image and save it in the uploads folder without any page refresh? Not only that, but can we also have the uploaded image displayed on the screen within a designated div section? Unfortunately, I seem to encounter a ...

Combine array in MongoDB containing nested documents

I need assistance with querying my MongoDB database: Specifically, I want to retrieve data that is nested within an array and filter it based on a specific key within the nested structure. The example document looks like this: [ { "name": ...

Text that appears automatically in an input field

I've searched high and low, but I just can't seem to find a solution that works for me. What I need is a way to automatically populate a HTML text field with default text when the page loads. This default text should ideally be a PHP variable and ...

Generate a collection of elements using a different collection as a reference

I am struggling with an array of objects: let data = [{ createdDate: "2222", email: "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="087c6d7b7c3d487c6d7b7c266b6765">[email protected]</a>", histories: [ ...

Passing all selected items from a list to the controller

I am currently facing an issue with my two multi-select lists. One list contains a full list of names while the second one holds the names that have been selected from the first list. The names are stored in a Vue array which populates the names into the s ...