Animate nav to move with another nav

Viewed 241

I have a script in PHP where i'm defining the menus and navbars of the website. I have a top menu that when you scroll down the page, it gets fixed to the top of the page. I want to have a second menu on the left "glued" to the top menu. The way I have done it, it goes well when the top menu reaches the top of the page, it gets fixed with the left menu like I want. The problem is that if the top menu doesnt reach the top of the page (like if it's just a little bit of scroll), the left menu doesnt get stick to the top menu.

The top menu is the one that has "Inicio", "Instrucciones" etc. The left menu is the one that has "Nueva cata", "Nueva cerveza", etc.

This is the page at the beginning (0 scroll). Start

This is the page after the top menu reaches the top of the page (>200 scroll). Finish

This is the page before the top menu reaches the top of the page (0 < scroll < 200). Third pic

As you can see in the third page, the left menu goes down with the scroll instead of going up with the top menu.

This is HTML:

<nav class="navbar navbar-expand-lg navbar-light bg-light" id="menu_arriba">
    <div class="navbar-brand" style="width: 200px;"></div>
    <a href="user.php" class="navbar-brand">
        <p>Inicio</p>
    </a>
    <a href="instrucciones.php" class="navbar-brand">
        <p>Instrucciones</p>
    </a>
    <a href="contacto.php" class="navbar-brand">
        <p>Contacto</p>
    </a>
    <a href="faq.php" class="navbar-brand">
        <p>FaQ</p>
    </a>
    <a href="ajax/logout.php" class="navbar-brand">
        <p><i class="fas fa-sign-out-alt"></i> Salir</p>
    </a>
</nav>

<nav id="menu_izq" class="sidenav">
    <div></div>
    <a href="nueva_cata.php">
        <p>Nueva Cata</p>
    </a>
    <a href="nueva_cerveza.php">
        <p>Nueva Cerveza</p>
    </a>
    <a href="cata.php">
        <p>Mis catas</p>
    </a>
    <a href="mis_cervezas.php">
        <p>Mis cervezas</p>
    </a>
    <a href="mis_amigos.php">
        <p>Mis amigos</p>
    </a>
</nav>

This is CSS:

#menu_izq {
  height: 100%;
  width: 200px;
  position: fixed;
  left: 0;
  top: 252px;
  z-index: 3;
  background-color: #503522;
  overflow-x: hidden;
  padding-top: 20px;
}
#menu_arriba{
    background-color: #503522;
    width: 100%;
    z-index: 1;
}

This is my JQuery:

$(window).scroll(function(){
    var elementPosition = $('#menu_arriba').offset();

    if($(window).scrollTop() >= elementPosition.top){
        $('#menu_arriba').css('position','fixed').css('top','0');
        $("#menu_izq").css('position','fixed').css('top', '35');
    } else {
        $('#menu_arriba').css('position','initial');
    }

    if(elementPosition.top <= 200){
        $('#menu_arriba').css('position','initial').css('top', '200');
        $("#menu_izq").css('top', '250');
    }
});

I know the problem is in the JQuery method but i don't get to write what I want. Thank you very much.

----- EDIT

The way I'm doing my website is with a menus.php where I write the top and left menu like this

<?php function izquierda() { ?>

<nav id="menu_izq" class="sidenav">
    <div></div>
    <a href="nueva_cata.php"><p>Nueva Cata</p></a>
    <a href="nueva_cerveza.php"><p>Nueva Cerveza</p></a>
    <a href="cata.php"><p>Mis catas</p></a>
    <a href="mis_cervezas.php"><p>Mis cervezas</p></a>
    <a href="mis_amigos.php"><p>Mis amigos</p></a>
</nav>

<?php } ?>
<?php function arriba() { ?>

<nav class="navbar navbar-expand-lg navbar-light bg-light" 
id="menu_arriba">
    <div class="navbar-brand" style="width: 200px;"></div>
    <a href="user.php" class="navbar-brand"><p>Inicio</p></a>
    <a href="instrucciones.php" class="navbar-brand"><p>Instrucciones</p>            
</a>
<a href="contacto.php" class="navbar-brand"><p>Contacto</p></a>
<a href="faq.php" class="navbar-brand"><p>FaQ</p></a>
<div class="navbar-brand" style="width: 450px;"></div>
<a href="ajax/logout.php" class="navbar-brand"><p><i class="fas fa-sign- 
out-alt"></i> Salir</p></a>
</nav>

So in the actual pages of the website, i just write:

<?php echo banner(); ?>
<?php echo arriba(); ?>
<?php echo izquierda(); ?>
<div class="main">
// HERE GOES THE CONTENT OF THE PAGE
</div>

I edit this to make you know that the reason of menu_izq is position: fixed is because like that, it shows the content of the page to the right of the left menu, and in position: sticky||relative, the content is shown at the end of the menu. I need to find a solution without changing (I think) the position: fixed of the menu_izq (left menu).

4 Answers

Looks like you are using Bootstrap. Currently, your left nav is initially set to position: fixed, I recommend using position: relative to your left nav initially so that positioning your nav elements can be relative to the height of the background image. Using Bootstrap, this solution wraps the left nav & the content in a flex container so that the content can be positioned relative to this container easily, since later on the navs are going to get position: fixed.

Basically on the script, just detect if the top of the scroll bar's Y position is already past the height of the background image element. If it is, assign the fixed position to the nav elements and adjust the content's position as needed relative to the container wrapping the left nav & the content. Check the CSS properties involving .navs-are-fixed to see how the navs are assigned the fixed position.

$(window).scroll(function() {

  // innerHeight is used to include any padding
  var bgImgHeight = $('.some-bg-img').innerHeight();

  // if the the scroll reaches the end of the background image, this is when you start to assign 'fixed' to your nav elements
  if ($(window).scrollTop() >= bgImgHeight) {
    $('body').addClass("navs-are-fixed");
  } else {
    $('body').removeClass("navs-are-fixed");
  }
});
#menu_izq {
  height: 100%;
  width: 200px;
  position: relative;
  left: 0;
  top: 0;
  z-index: 3;
  background-color: #503522;
  overflow-x: hidden;
  padding-top: 20px;
}

#menu_arriba {
  background-color: #503522;
  width: 100%;
  z-index: 1;
  position: relative;
  top: 0;
}

body {
  background-color: #ffc75a !important;
  height: 300vh; /* sample arbitrary value to force body scrolling */
}

.some-bg-img {
  background: url(https://via.placeholder.com/1920x200.png?text=Sample%20Background%20Image);
  height: 200px;
  background-repeat: no-repeat;
  background-size: cover;
  background-position: bottom;
}

.navs-are-fixed #menu_arriba {
  position: fixed;
}

.navs-are-fixed #menu_izq {
  position: fixed;
  top: 72px; /* the height of your top nav */
}

.navs-are-fixed .some-sample-content {
  position: absolute;
  top: 72px; /* the height of your top nav */
  left: 200px; /* the width of your left nav */
}
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>

<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">

<body>
  <div class="some-bg-img"></div>
  <nav class="navbar navbar-expand-lg navbar-light bg-light" id="menu_arriba">
    <div class="navbar-brand"></div>
    <a href="user.php" class="navbar-brand">
      <p>Inicio</p>
    </a>
    <a href="instrucciones.php" class="navbar-brand">
      <p>Instrucciones</p>
    </a>
    <a href="contacto.php" class="navbar-brand">
      <p>Contacto</p>
    </a>
    <a href="faq.php" class="navbar-brand">
      <p>FaQ</p>
    </a>
    <a href="ajax/logout.php" class="navbar-brand">
      <p><i class="fas fa-sign-out-alt"></i> Salir</p>
    </a>
  </nav>

  <div class="d-flex h-100 w-100 position-absolute">
    <nav id="menu_izq" class="sidenav">
      <div></div>
      <a href="nueva_cata.php">
        <p>Nueva Cata</p>
      </a>
      <a href="nueva_cerveza.php">
        <p>Nueva Cerveza</p>
      </a>
      <a href="cata.php">
        <p>Mis catas</p>
      </a>
      <a href="mis_cervezas.php">
        <p>Mis cervezas</p>
      </a>
      <a href="mis_amigos.php">
        <p>Mis amigos</p>
      </a>
    </nav>

    <div class="some-sample-content">
      <p>Lorem Ipsum</p>
      <p>Lorem Ipsum</p>
      <p>Lorem Ipsum</p>
      <p>Lorem Ipsum</p>
      <p>Lorem Ipsum</p>
      <p>Lorem Ipsum</p>
      <p>Lorem Ipsum</p>
      <p>Lorem Ipsum</p>
      <p>Lorem Ipsum</p>
      <p>Lorem Ipsum</p>
      <p>Lorem Ipsum</p>
      <p>Lorem Ipsum</p>
      <p>Lorem Ipsum</p>
    </div>
  </div>
</body>

If you really must keep your fixed positioning on your left nav, then you are going to have to compute it's top value based on on the height of both the banner & the top nav, such that if the top of scroll bar's Y position is past the height of the banner, the top value of the left nav will be equal to the height of the top nav - so you push it down so that they don't overlap. If the top of the scroll bar's Y position is not past the height of the banner, the top value of the left nav is going to be equal to the difference of the height of the banner & the top nav minus the top of the scroll bar's Y position.

$(window).scroll(function() {

  // innerHeight is used to include any padding
  var bgImgHeight = $('.some-bg-img').innerHeight();
  var topNavHeight = $('#menu_arriba').innerHeight();
  var leftNavInitialCssTop = bgImgHeight + topNavHeight;

  // if the the scroll reaches the end of the background image, this is when you start to assign 'fixed' to the top nav
  if ($(window).scrollTop() >= bgImgHeight) {
    $('body').addClass("navs-are-fixed");
    $("#menu_izq").css("top", topNavHeight);
  } else {
    $('body').removeClass("navs-are-fixed");
    $("#menu_izq").css("top", leftNavInitialCssTop - $(window).scrollTop())
  }

});
#menu_izq {
  height: 100%;
  width: 200px;
  position: fixed;
  left: 0;
  top: 252px;
  z-index: 3;
  background-color: #503522;
  overflow-x: hidden;
  padding-top: 20px;
}

#menu_arriba {
  background-color: #503522;
  width: 100%;
  z-index: 1;
  top: 0;
}

body {
  background-color: #ffc75a !important;
  height: 400vh; /* sample arbitrary value to force body scrolling */
}

.some-bg-img {
  background: url(https://via.placeholder.com/1920x200.png?text=Sample%20Background%20Image);
  height: 179px;
  background-repeat: no-repeat;
  background-size: cover;
  background-position: bottom;
}

.navs-are-fixed #menu_arriba {
  position: fixed;
}
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>

<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">

<div class="some-bg-img"></div>
<nav class="navbar navbar-expand-lg navbar-light bg-light" id="menu_arriba">
  <div class="navbar-brand"></div>
  <a href="user.php" class="navbar-brand">
    <p>Inicio</p>
  </a>
  <a href="instrucciones.php" class="navbar-brand">
    <p>Instrucciones</p>
  </a>
  <a href="contacto.php" class="navbar-brand">
    <p>Contacto</p>
  </a>
  <a href="faq.php" class="navbar-brand">
    <p>FaQ</p>
  </a>
  <a href="ajax/logout.php" class="navbar-brand">
    <p><i class="fas fa-sign-out-alt"></i> Salir</p>
  </a>
</nav>

<nav id="menu_izq" class="sidenav">
  <div></div>
  <a href="nueva_cata.php">
    <p>Nueva Cata</p>
  </a>
  <a href="nueva_cerveza.php">
    <p>Nueva Cerveza</p>
  </a>
  <a href="cata.php">
    <p>Mis catas</p>
  </a>
  <a href="mis_cervezas.php">
    <p>Mis cervezas</p>
  </a>
  <a href="mis_amigos.php">
    <p>Mis amigos</p>
  </a>
</nav>

You should be able to have that working with just CSS and no javascript using position: sticky attribute.

Make both elements position: sticky, the top nav should have a top: 0 property and the side nav should have a top: x property where x is the height of the top nav.

That should be enough and you should be able to remove the js code.

Read more about sticky position here https://developer.mozilla.org/en-US/docs/Web/CSS/position

why you want to do this? you can move the top menu and side menu and text container together in one container to be relative with each other, set the text box container fixed height in percent and set its overflow-y to auto. make the whole container animatable like the top menu. this will solve your problem.

The side and top sliding <nav> elements cannot interact correctly with the page content. Especially the lateral <nav> with vertical sliding according to many changing rules - sit, follow, slide against, stick apart. It's too much at once.

Workaround 1 - smooth - top sliding, side fixed (anchors corrected)

I'm afraid this is not the solution to your dreams. Works smooth and easy as it avoids the crush point.

$(window).scroll(function(){
var elementPosition = $('#menu_arriba').offset();
      
if($(window).scrollTop() >= elementPosition.top){
$('#menu_arriba').css('position','fixed').css('top','0');
} else {
$('#menu_arriba').css('position','relative');    /* fixed */
}
if(elementPosition.top <= 200){
$('#menu_arriba').css('position','initial').css('top', '200');
}});

*{ box-sizing: border-box}
body{
margin: 0;  
text-align: center;
z-index: -1;
}
#pilar{     /* added fix */
background: #503522;
width: 200px;
position: fixed;
top: 0;
left: 0;
bottom: 0;
z-index: -1
}
#menu_izq {
background: 0;  /* fixed */
width: 200px;    /* padding-top: 200px; removed */
position: fixed;
top: 200px; /* fixed */
left: 0;
bottom: 0;
z-index: 0; /* fixed */
}
#menu_arriba{
background: #503522;
width: 100%;
position: relative; /* added fix */
z-index: 1; /* added fix */
}
p{ margin: 100px 100px 100px 250px; z-index: -1 }
a{ display: inline-block; width: 150px; border: 1px solid white; text- align: center; padding: 10px 0; margin: 20px}
img{ border-bottom: 10px solid #503522; margin: 0 0 -3px; width: 100%; height: 200px; z-index: 1; z-index: 9;}  

<img src="img/blue.jpg" alt="blue">
<div id="pilar"></div>  <!-- added fix -->
<nav class="navbar navbar-expand-lg navbar-light bg-light" id="menu_arriba">
<div class="navbar-brand" ></div>   <!-- needed or redundancy? -->
<a href="user.php" class="navbar-brand">Inicio</a>
<a href="instrucciones.php" class="navbar-brand">Instrucciones</a>
<a href="contacto.php" class="navbar-brand">Contacto</a>
<a href="faq.php" class="navbar-brand">FaQ</a>
<a href="ajax/logout.php" class="navbar-brand"><i class="fas fa-sign-out-alt"></i>Salir</a>
</nav>
<nav class="sidenav" id="menu_izq">
<a href="nueva_cata.php">Nueva Cata</a>
<a href="nueva_cerveza.php">Nueva Cerveza</a>
<a href="cata.php">Mis catas</a>
<a href="mis_cervezas.php">Mis cervezas</a>
<a href="mis_amigos.php">Mis amigos</a>
</nav><p>... </p><p> ...</p>

Workaround 1.1 - smooth with a nice view
only changes

#menu_arriba{
background: 0;
width: 100%;
position: relative; /* added fix */
z-index: 0  /* added fix */
}
#hole{
background: #503522;
height: 100%;
margin-left: 200px;
width: auto;
}

<div id="pilar"></div>  <!-- added fix -->
<nav class="navbar navbar-expand-lg navbar-light bg-light" id="menu_arriba">
<div id="hole">
    <div class="navbar-brand" ></div>
    <a href="user.php" class="navbar-brand">Inicio</a>
    <a href="instrucciones.php" class="navbar-brand">Instrucciones</a>
    <a href="contacto.php" class="navbar-brand">Contacto</a>
    <a href="faq.php" class="navbar-brand">FaQ</a>
    <a href="ajax/logout.php" class="navbar-brand"><i class="fas fa-sign-out-alt"></i> Salir</a>
</div></nav>

Workaround 2 - dirty - hide or cover up side effects
If you still insist - not so smooth, slightly bouncy menu. Your dreams are also not coming true, but there are no visible holes. Here you have it:

$(window).scroll(function(){
var elementPosition = $('#menu_arriba').offset();

if($(window).scrollTop() >= elementPosition.top){
$('#menu_arriba').css('position','fixed').css('top','0');
$('#menu_izq').css('position','fixed').css('top','0');
} else {
$('#menu_arriba').css('position','initial');
$('#menu_izq').css('position','initial');
}
if(elementPosition.top <= 200){
$('#menu_arriba').css('position','initial').css('top', '200');
$('#menu_izq').css('position','initial').css('top', '200');
}});

*{ box-sizing: border-box}
body{
margin: 0;  
text-align: center;
}
#menu_izq {
background: 0;
height: auto;
width: 200px;
padding-top: 100px;
}
#menu_arriba{
background: #503522;
width: 100%;
z-index: 1
}
#pilar{
background: #503522;
width: 200px;
position: fixed;
top: 0;
left: 0;
bottom: 0;
z-index: -1
}
p { margin: 100px 300px; position: relative; bottom: 400px}
a{ display: inline-block; width: 150px; border: 1px solid white; text-align: center; padding: 10px 0; margin: 20px}
img{ border-bottom: 10px solid #503522; margin: 0 0 -3px; width: 100%; height: 200px;}  

<img src="img/blue.jpg" alt="blue">
<div id="pilar"></div>

<nav class="navbar navbar-expand-lg navbar-light bg-light" id="menu_arriba">
<div class="navbar-brand" ></div>   <!-- what for it is? needed? -->
<a href="user.php" class="navbar-brand">Inicio</a>
<a href="instrucciones.php" class="navbar-brand">Instrucciones</a>
<a href="contacto.php" class="navbar-brand">Contacto</a>
<a href="faq.php" class="navbar-brand">FaQ</a>
<a href="ajax/logout.php" class="navbar-brand"><i class="fas fa-sign-out-alt"></i>Salir</a>
</nav>
<nav class="sidenav" id="menu_izq">
<a href="nueva_cata.php">Nueva Cata</a>
<a href="nueva_cerveza.php">Nueva Cerveza</a>
<a href="cata.php">Mis catas</a>
<a href="mis_cervezas.php">Mis cervezas</a>
<a href="mis_amigos.php">Mis amigos</a>
</nav><p>... </p><p> ...</p>

There are other minor shortcomings in your code.
<a href=""><p> .... </p></a> may not be a crime, but neither is it good. <p> has its own characteristics and is not a stiffener or pillar - I fixed it with css.

Above you have whole code I used - as a hint and explanation. Resize elements to your needs and taste. Hope you like it
cheers

Related