Search code examples
javascripthtmlcss

Align absolute position element near its ancestor after scroll


I have a div with absolute position; it is a menu which display property toggles on a click action. Which has an ancestor div; by clicking in this ancestor div the menu toggles and the menu appears near to the ancestor. The position of it's appearance is fine for me. So far so good but as soon as I scroll a bit and click on the ancestor div the menu appears to its old position so the alignment is not longer maintained. How can I fix this.

let isVisible = false;
document.getElementById("r3").addEventListener('click', function(){
  let menuEle = document.getElementById("menu");
  if(!isVisible){
    menuEle.classList.add("show");
  }
  else{
  menuEle.classList.remove("show");
  }
  isVisible = !isVisible;
})
html{
    box-sizing: border-box;
}
body{
  padding: 0px;
  margin: 0;
  box-sizing: border-box;
  font-family: Arial, Helvetica, sans-serif;
}
.layout{
  height: 150px;
  width: 100%;
  border: solid 1px #000;
  overflow-y: auto;
  overflow-x: hidden;
}
.row{
  height:50px;
  width: 100%;
  border: solid 1px #a5a5a5;
  cursor: pointer;
  color: red;
}
.row:hover{
  background: #c9dcff;
}
.menu{
  display:none;
  justify-content: center;
  align-items: center;
  position: absolute;
  width: fit-content;
  background: #e7e7e7;
  padding: 0.8em;
  left:0;
  right:0;
  margin-left:auto;
  margin-right:auto;
}
.menu.show{
  display: flex;
}
b{
 color: red;
}
<!DOCTYPE html>
<html>
    <head>
        <title></title>
    </head>
    <body>
        <div class="layout">
            <div class="row"></div>
            <div class="row"></div>
            <div class="row" id="r3">
                <div class="menu" id="menu">
                    <span>Menu of r3</span>
                </div>
        r3: First click here to see the menu and click again to hide, then scroll a bit above and click again
            </div>
            <div class="row"></div>
        </div>
    </body>
</html>


Solution

  • You omitted to add position:relative to the containing .row.

    The menu is positioned absolutely in relation to the nearest (non-static) positioned ancestor.

    let isVisible = false;
    document.getElementById("r3").addEventListener('click', function() {
      let menuEle = document.getElementById("menu");
      if (!isVisible) {
        menuEle.classList.add("show");
      } else {
        menuEle.classList.remove("show");
      }
      isVisible = !isVisible;
    })
    html {
      box-sizing: border-box;
    }
    
    body {
      padding: 0px;
      margin: 0;
      box-sizing: border-box;
      font-family: Arial, Helvetica, sans-serif;
    }
    
    .layout {
      height: 150px;
      width: 100%;
      border: solid 1px #000;
      overflow-y: auto;
      overflow-x: hidden;
    }
    
    .row {
      height: 50px;
      width: 100%;
      border: solid 1px #a5a5a5;
      cursor: pointer;
      color: red;
      position: relative;
    }
    
    .row:hover {
      background: #c9dcff;
    }
    
    .menu {
      display: none;
      justify-content: center;
      align-items: center;
      position: absolute;
      width: fit-content;
      background: #e7e7e7;
      padding: 0.8em;
      left: 0;
      right: 0;
      margin-left: auto;
      margin-right: auto;
    }
    
    .menu.show {
      display: flex;
    }
    
    b {
      color: red;
    }
    <!DOCTYPE html>
    <html>
    
    <head>
      <title></title>
    </head>
    
    <body>
      <div class="layout">
        <div class="row"></div>
        <div class="row"></div>
        <div class="row" id="r3">
          <div class="menu" id="menu">
            <span>Menu of r3</span>
          </div>
          r3: First click here to see the menu and click again to hide, then scroll a bit above and click again
        </div>
        <div class="row"></div>
      </div>
    </body>
    
    </html>