Kumo
Kumo

Reputation: 189

Sticky navbar has glitchy behavior

So I've tried to custom make a navbar that is set right below a border around a page. I've made an illustration to help explain: enter image description here

On the page, I have a border (blue) around the whole page, then the navbar (red) which scrolls with the page until it hits the top of the navbar and sticks until the user scrolls back up.

My problem is that it seems like something is shoving my navbar and its contents to the right once the js kicks (when the user scrolls past the top border) making an obvious jerk movement.

enter image description here

Here is my html:

    <html>
    <head>
      <title>Title</title>
      <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track' => true %>
      <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
      <%= csrf_meta_tags %>
    </head>

    <body>
      <%= yield :scss %>

      <div class="container-main <%= layout %>">

        <nav class="main-nav">
          <a class="navbar-brand" href="#">
            <i class="fa fa-bars menu-icon"></i>
          </a>
        </nav>

        <div class="container">
          <%= yield %>
        </div>

      </div>

      <%= yield :js %>

    </body>
    </html>

Would anyone know how to fix this? I'm not sure if its a bootstrap thing that needs to be overridden or what...I've tried to set the navbar's margin-left to equate the movement but it ends up messing up the bootstrap columns I have set up...any advice appreciated!

Here is my CSS:

    .main-nav {
      text-align: center;
      margin-top: 50px;
      background: transparent;
      height: 50px;
      z-index: 150;
      margin-bottom: -50px;
    }

    .main-nav-scrolled {
      position: fixed;
      width: 100%;
      top: 0;
    //  margin-left: -35px; not working
    }

    .main-nav,
    .container {
      position: relative; 
    }
    .container {
      background: transparent;
      padding: 100px 
    }
    .layout-with-border {
      background: #eaeae8; //fixed for all product pages
      border: 35px solid #dad4c9; //theme color light
    }
    .layout-without-order {
    }

Here is my JS:

      var  mn = $(".main-nav");
          mns = "main-nav-scrolled";

      var BorderWidth = 35;

      $(window).scroll(function() {
        if( $(this).scrollTop() > BorderWidth ) {
          mn.addClass(mns);
        } else {
          mn.removeClass(mns);
        }
      });

Sorry, it's a little confusing how to post so many parts since I'm so new. I created a helper for the border layout:

    module ApplicationHelper
      def layout
        if params[:controller] == 'products' && params[:action] == 'show' 
          'layout-with-border' 
        elsif params[:controller] == 'volumes' && params[:action] == 'show' 
          'layout-with-border' 
        else 
          'layout-without-border'
        end
      end
    end

Upvotes: 4

Views: 2921

Answers (2)

Marc Audet
Marc Audet

Reputation: 46785

Your code is very close to what you need.

The glitch is due to changing from position: relative to position: fixed in .main-nav.

Fixed elements widths are computed with respect to the viewport, not the parent container.

First, zero-out the margin on body (if needed, depends on your design).

For illustration, I added the 35px blue border on .main-container.

Then, for .main-nav-scrolled, set both the left and right offsets to 35px to set the correct width of the nav block, you don't need to set the width value.

Note: The content in .container jumps upward by 50px when the scroll hits the trigger point. To stop this, you would need to add 50px padding to .container-main or a 50px top margin to .container when you fix the nav element. I chose to add padding, see changes in the jQuery below.

var mn = $(".container-main");
mns = "main-nav-scrolled";

var BorderWidth = 35;

$(window).scroll(function() {
  if ($(this).scrollTop() > BorderWidth) {
    mn.addClass(mns);
  } else {
    mn.removeClass(mns);
  }
});
body {
  margin: 0;
}
.container-main {
  border: 35px solid lightblue;
}
.main-nav {
  text-align: center;
  margin-top: 0px;
  background: yellow;
  height: 50px;
}
.main-nav-scrolled .main-nav {
  position: fixed;
  top: 0;
  left: 35px;
  right: 35px;
  z-index: 1;
}
.main-nav-scrolled.container-main {
  padding-top: 50px;
}
.container {
  position: relative;
  background: beige;
  padding: 100px;
  height: 400px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container-main">
  <nav class="main-nav">
    <a class="navbar-brand" href="#">
      <i class="fa fa-bars menu-icon">Icon</i>
    </a>
  </nav>
  <div class="container">container content...</div>
</div>

Upvotes: 5

Lieutenant Dan
Lieutenant Dan

Reputation: 8264

define a max-width: to your nav element itself or parent wrapper.

You should have a div wrapper around the middle of the page outside of the full width wrapper - that I am seeing. Eg something like below.

#middle_page_wrap { 
    width: 960px;
    max-width: 960px;
    margin: 0 auto;
    position: relative;
}

And like:

<div class="container-main">
 <div id="middle_page_wrap"><!-- nest -->
 <nav class="main-nav">
          <a class="navbar-brand" href="#">
            <i class="fa fa-bars menu-icon"></i>
          </a>
  </nav>
<!-- etc -->

Also, yeah I agree with the comments; not sure what JS or dynamic tool or other languages are at play here. But sounds like a pure CSS thing

If this doesn't work then its because of code you are not showing, you'll have to create a demo / jsfiddle.

Upvotes: 0

Related Questions