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

I'm confused as to why I am only receiving one object entry in my fetch response instead of the expected list of entries

Is there a way to fetch all entries as a response instead of just one value? For example, when the next value returned by the fetch is {"next":"/heretagium"}, I can replace "/hustengium" with "heretagium" to get th ...

Using the concept of method chaining in JavaScript, you can easily add multiple methods from

Hey there! I'm looking for some assistance with dynamically building a method chain. It seems like it should be pretty straightforward if you're familiar with how to do it... Currently, I am using mongoose and node.js to query a mongo database. ...

Is there a point in bundling NPM packages if they are ultimately going to be bundled by the project

I'm in the process of creating a TypeScript package for publication on NPM. My plan is to utilize this package in upcoming web development endeavors, most likely utilizing Vite. As I look ahead to constructing a future website with this module, I am c ...

Issue encountered while trying to load electron-tabs module and unable to generate tabs within electron framework

I've recently set up the electron-modules package in order to incorporate tabs within my Electron project. Below are snippets from the package.json, main.js, and index.html files. package.json { "name": "Backoffice", "version": "1.0.0", "descr ...

The accuracy of IE9 <section> is not quite up to par

When viewing This Page in IE9, the element section#tcs-website-content appears as 990px wide even though its CSS property is set to width: 100%. This occurs despite all parent elements also having a width of 100%, resulting in a browser width of 1024px. A ...

Is using readonly attribute for HTML inputs a security concern?

Is it reliable to trust the data from an html input field set to readonly? What exactly is the purpose of using a readonly field in a form? I understand that disabled fields are not included in $_POST, but what about readonly fields? I need a way to have ...

Fade-In and Fade-Out CSS Effect with Background Images Displaying a Blank Last Image

I've been experimenting with applying a CSS-only background image transition to a div, but I encountered an issue where after cycling through three specified images, the background reverts back to the original black color. Even stranger, when I adjust ...

Failure of event watcher on dynamically updated content

Any help would be greatly appreciated! :) I am currently using JavaScript to dynamically add fields to a document, but I have noticed that the event listener only works on predefined fields. For instance, in the code snippet below, the 'lozfield&apo ...

Stop user from navigating to specific route using React-router

I am currently utilizing react-router with history useQueries(createHashHistory)(), and I have a requirement to restrict navigation to certain routes based on the route's configuration. The route configuration looks like this: <Route path="/" name ...

If the user confirms it with a bootstrap dialog box, they will be redirected to the corresponding links

Firstly, I am not interested in using javascript confirm for this purpose. Please read on. For example, adding an onClick attribute to each link with a function that triggers a system dialog box is not what I want. Instead of the standard system dialog bo ...

Unable to pass an Array to the Controller

let formData = new FormData(); payloadData = JSON.stringify(payload.unitDoctors); for (var prop in payloadData) { formData.append(prop, payloadData[prop]); } axios({ method: "put", url: apiUrl + payload.id, data: formData }) .then(resp ...

Dropdown box not displaying any choices

I developed a basic reusable component in the following way: Typescript (TS) import {Component, Input, OnInit} from '@angular/core'; import {FormControl} from '@angular/forms'; @Component({ selector: 'app-select', templa ...

Sending a message to an iframe from a different origin using JavaScript

Just starting out with JavaScript and I'm attempting to send a message to my iframe in order to scroll it. Here is the code I am using: scroll(i) { var src = $("#iframe").attr("src"); $("#iframe").contentWindow.postMe ...

Troubleshooting issue: Asynchronous functionality not working with Ajax.BeginForm

Struggling to grasp ASP.Net MVC and facing challenges with using Ajax.BeginForm to update a partial view asynchronously. Here's the code snippet in the view for the action: @using (Ajax.BeginForm( new AjaxOptions { ...

Ways to create intersecting borders at the corners of a division

Is there a technique to expand the borders of a div by 1 or 2 pixels in each direction so that they create a cross at each corner? https://i.stack.imgur.com/mUAxM.png ...

Exploring the Integration of Google Maps and Angular Google Places in Angular 2

On my webpage, I am utilizing two components simultaneously: and angular2-google-map-auto-complete from https://www.npmjs.com/package/angular2-google-map-auto-complete. To set the Angular maps key, I have defined it as follows: AgmCoreModule.forRoot({ a ...

Trick to keep horizontal element alignment intact when scroll bar appears

Currently, all my pages are designed with the standard fixed-width-margin-left-right-auto layout. .container{ width:900px; margin:0 auto; } An issue arises when some of these pages exceed the height of the window, requiring a vertical scroll ba ...

Achieving Text Centering in kableExtra Cells with CSS: A Step-by-Step Guide

Recently, I received a helpful solution that involved using the extra_css = argument in cell_spec() from the kableExtra package. This allowed me to fill the entire cell of my table instead of just the text inside it. However, even after specifying align = ...

Show a dynamic dropdown box using AJAX if a specific variable is present

I am currently working on implementing an ajax call for a dynamic dropdown box. Once the variable $place is available, it triggers an ajax call to populate the dropdown box. My goal is to pass the variable $place to listplace.php, encode it as JSON data ...

Height of dropdown items in the horizontal navbar

I am working on a navbar that includes a dropdown menu. Currently, the dropdown items are being displayed horizontally in full width with a larger height. However, I would like them to be displayed vertically in full width and with a smaller, normal height ...