Talsja
Talsja

Reputation: 89

Fixed position on scroll flickering

I have a simple page with a header and a nav. On window scroll I set the header and nav to a fixed position. All goes well if the page is long enough to scroll. If the page is at a certain height I get a flickering situation.

var prev = 0;
var $window = $(window);
var nav = $('.context-nav');
var $navbar = $('.navbar');

$(window).scroll(function(e) {
  var scrollTop = $window.scrollTop();
  nav.toggleClass('fixed', scrollTop >= prev);
  $navbar.toggleClass('fixed', scrollTop >= prev);
  prev = scrollTop;
});
* {
  box-sizing: border-box;
  padding: 0;
  margin: 0;
  text-decoration: none;
}

html,
body {
  font-size: 1rem;
  background-image: linear-gradient(to right, #8ca986, #789a71);
}

.sp {
  height: 700px;
}

header {
  position: relative;
  background: #fff;
  height: 80px;
  text-align: center;
  box-shadow: 0 1px 15px rgba(0, 0, 0, 0.04), 0 1px 6px rgba(0, 0, 0, 0.04);
  transition: all 500ms cubic-bezier(0.25, 0.46, 0.45, 0.94) 0s;
}

header.fixed {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 60px;
}

.page-container {
  padding: 2rem;
  position: relative;
  color: white;
}

.page-container .context-nav {
  display: flex;
  flex-direction: row;
  color: white;
  position: relative;
  margin-bottom: 3rem;
  padding: 1rem;
  transition: all 0.4s ease;
}

.page-container .context-nav.fixed {
  position: fixed;
  left: 0;
  right: 0;
  top: 60px;
  background-color: rgba(255, 255, 255, 0.08);
}

.page-container .context-nav .nav-item {
  padding: 20px;
  border: 1px solid white;
  margin-right: 1rem;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<header class="navbar">
  My header goes here
</header>
<div class="page-container">
  <div class="context-nav">
    <div class="nav-item">Item 1</div>
    <div class="nav-item">Item 2</div>
    <div class="nav-item">Item 3</div>
  </div>
  <h1 class="sp">Scroll me... </h1>
</div>

You can see the problem if you adjust the height of the class .sp to a small size. In my sample I have set it to 700px. When I try to scroll it sets the class but removes it directly which is causing the flickering behavior. When I set the .sp class height to 1200px the problem is not there.

Does anyone have an idea to fix this?

Codepen

Upvotes: 0

Views: 6262

Answers (1)

San Jarral
San Jarral

Reputation: 112

Flicker happens because when you set the element to position fixed, it goes out of the flow and that changes the offset, if what you're looking for is to display stickyElements upon scroll, I'd suggest to clone the element you want to display as sticky into a fixed container and then display that container, this way the original element stay in their original position and do not cause the content jump, also i noticed you were using a counter to display and hide the stickyElements, which i think is inefficient unless you have a different thing in your mind.

Here is a codepen i quickly forked from yours to show what i mean:

var $window = $(window);
var $stickyHeader = $('.stickyHeader');
var $nav = $('.context-nav');
var navOffset = $nav.offset().top;
var $navbar = $('.navbar');
var $newNavBar = $navbar.clone();
var $newNav = $nav.clone();

$stickyHeader.append($newNavBar);
$stickyHeader.append($newNav);
var timer;

$(window).scroll(function (e) {
	if(timer) {
		window.clearTimeout(timer);
	}

	timer = window.setTimeout(function(){
		if($window.scrollTop() > navOffset) {
			$stickyHeader.addClass('active');
		}
		else {	
			$stickyHeader.removeClass('active');
		}
	}, 100);
});
* {
  box-sizing: border-box;
  padding: 0;
  margin: 0;
  text-decoration: none;
}

html,
body {
  font-size: 1rem;
  background-image: linear-gradient(to right, #8ca986, #789a71);
}

.sp {
  height: 700px;
}

header {
  position: relative;
  background: #fff;
  height: 80px;
  text-align: center;
  box-shadow: 0 1px 15px rgba(0, 0, 0, 0.04), 0 1px 6px rgba(0, 0, 0, 0.04);
}
.active header {
  width: 100%;
  height: 60px;
}

.page-container {
  padding: 2rem;
  position: relative;
  color: white;
}

.context-nav {
  display: flex;
  flex-direction: row;
  color: white;
  position: relative;
  margin-bottom: 3rem;
  padding: 1rem;
}
.active .context-nav {
  left: 0;
  right: 0;
  background-color: rgba(255, 255, 255, 0.08);
}
.context-nav .nav-item {
  padding: 20px;
  border: 1px solid white;
  margin-right: 1rem;
}

.stickyHeader {
  width: 100%;
  position: fixed;
  height: 0;
  overflow: hidden;
  top: -140px;
  transition: height 200ms ease, top 200ms ease;
}
.stickyHeader.active {
  height: 140px;
  top: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="stickyHeader">
	
</div>
<header class="navbar">
	My header goes here
</header>
<div class="page-container">	
	<div class="context-nav">
		<div class="nav-item">Item 1</div>
		<div class="nav-item">Item 2</div>
		<div class="nav-item">Item 3</div>
	</div>
	<h1 class="sp">Scroll me... </h1>
</div>

https://codepen.io/j-lastforone/pen/bzddmK

Upvotes: 1

Related Questions