Skip to content
This repository

bootstrap-affix.js: using Chrome, affixed menu moves violently up and down each time one scrolls to the bottom of page #4647

Closed
tim-peterson opened this Issue August 23, 2012 · 107 comments
tim peterson

I'm using Chrome version 21 and Mac OS X 10.7.

I don't have this same problem on Firefox 14, Safari 6, or Opera 12.

MichalKopec

Did you try to style the .affix-bottom? I had a similar jumpy experience with my affixed

. Adding .affix-bottom to CSS and styling it fixed the jumping at the bottom of the page.
tim peterson

@MichalKopec thanks, so the menu div gets the affix JS and a separate footer div gets the .affix-bottom class? might you explain in code what you mean?

Mark Otto
Owner
mdo commented August 27, 2012

The bottom bound can fix this, yes. Also, margin on the bottom of an element can cause the jumping, so double check that.

Mark Otto mdo closed this August 27, 2012
tim peterson

just to be clear, the behavior I observed was on the twitter bootstrap site itself, not my own html.

Jacob
Owner
fat commented August 27, 2012

@tim-peterson weird, i can't reproduce this (and i have an identical setup). What page are you seeing this on? fullscreen? etc?

Jacob fat reopened this August 27, 2012
tim peterson

http://twitter.github.com/bootstrap/javascript.html, scroll down all the way to the bottom using Chrome 21.0.1180.82, not fullscreen.

the affix menu,<ul class="nav nav-list bs-docs-sidenav affix-bottom">, goes berserk because the classes affix-bottom and affix get toggled rapidly.

mspivak

Same issue here on a custom implementation. Classes affix and affix-bottom get toggled rapidly while scrolling (and the affixed element exceeds the desired bottom offset). Using this:

$('.title').affix({ offset: { top: 350, bottom: 1500 } })
Mark Otto
Owner
mdo commented August 28, 2012

I'm not seeing this at all and have the same version of Chrome.

tim peterson

maybe its just a Mac trackpad issue, i'm using OS X 10.7.4 and have my system preferences->trackpad "Tracking speed" set to the 4th tickmark out of 10 (going left to right).

Btw, sorry to keep wasting your time on this, its not a big deal to me personally but can't hurt to have a record of this issue for others.

oxideous

I am also experiencing this issue, though I am on Windows 7. I experienced this issue in Chrome and Firefox. I believe at one point I had it functioning properly but through editing further this flickering behavior began to happen. I suppose there may be an error in my code, but if it is happening to others as well perhaps there is a bug.

Evan Bovie

I am also experiencing this issue. I'm really stuck here. I can't figure out what is going wrong. But I have an additional problem with absolute positioning. The toggling of the affix and affix-bottom classes are sequential and not random. Here's what I've found so far (and my code):

Live Example

http://phaseone.me/internets/github_issues/bootstrap/4647/prod/Printed-Tags.html

The JS

// DYNAMIC SUBNAV
// --------------
$('#subnav-list').affix({
  offset: {
    top: function() {
      brw = $(window).width();
      if (brw >= 1200) return 114;
      // Still need to include other widths
    },
    bottom: 171
  }
});

The HTML

The whole page: https://gist.github.com/3517294

Here's a screencap of the relevant section: http://grab.bovie.me/fJhS

The CSS

#subnav-list.affix-bottom {
  position: absolute;
  top: auto;
  bottom: 126px;
}
#subnav-list {
  width: 240px;
  padding: 15px;
  margin: 30px 0 0 0;
}
#subnav-list.affix {
  top: 50px;
  position: fixed
}

The other problem

Instead of the nav-list positioning itself to the page, it is somehow relevant to .subnav. Despite having the style the same as the bootstrap docs, it's positioned wrong.

With .affix-bottom:

> $('#subnav-list').offset().top
  76

With .affix:

> $('#subnav-list').offset().top
  2574
tim peterson

@phaseOne , weird i'm not seeing this issue on your site though I'm still seeing it on the twitter bootstrap site.

Laszlo Horvath

Having the same issue with custom implementation. Any updates on this issue? It would be really important for me.

MonsieurApple

I'm having this issue as well.

Laszlo Horvath

I figured out that the main problem is when the "affix" and "affix-bottom" CSS classes are toggled the CSS on the element are also changing aswell. And the jumpy experience caused because the top and bottom properties are changing from "auto" to a fix value.

For example in my case:

.affix { position: fixed; top: 40px; bottom: auto; }
.affix-bottom { position: fixed; top: auto; bottom: 595px; }

If I give a fix value for both classes it won't jump anymore but in my case for the bottom class I would need a negative top value because my element is higher than the avalaible space I have for it in my layout. That negative top value should be calculated by the plugin..

Jacob
Owner

You guys should fix this, i have faith in you bros

tim peterson

@fat since you aren't working at Twitter are you also not working on Bootstrap anymore? Regardless, what will you be up to now?

I'm just curious as someone who is eternally grateful for your and Mark's colossal contribution to the community,

Vasilis Tsaknis

I had the same issue and i made a patch for it,

vaionicle@8a74ad7

try it and let me know if it is working.

Evan Bovie

Patch doesn't work for me. Now the nav toggles between affix-top and affix-bottom rapidly.

Kazuhide Fukuyama

I'm using Mac OS X 10.8.1, Firefox 15.0 and Chrome 21.0.1180.89.
This patch works fine.
kzxtreme@be411d8

Laszlo Horvath

None of those patches work for me. (Windows 7, Chrome 22 beta).

Alexander Baron

I'm experiencing the same issue - Google Chrome 21.0.1180.89 m - Windows XP. Seems like we already have a few examples, so let me know if I need to contribute another one.

After digging into the code a little bit, it looks like position.top has a value that we're not expecting. Once you're in the bottom offset range, position.top will bounce between the actual top offset, and a negative number.

Maybe that's helpful. Maybe not.

Bogdan Litescu

It happens to me too. The moment the element changes from fixed to static, position.top changes too causing the calculations to alternate.

Bogdan Litescu

I removed some borders and padding for the root affix element and the issue went away mostly. It only happens for +/- 20px around the place where it's supposed to change from fixed to static.
I'd still like to get it fixed completely.
I tried the 2 patches. The first introduces the issue again when I scroll beyond the bottom affix, the 2nd patch introduces the flicker while I scroll down until the affix becomes changes from fixed.

Matej Janovčík

Can confirm, I'm having same issue on custom implementation.

Bruno Lazzaro

@fat has spoken. Fix this bros.

Jacob
Owner

@tim-peterson i'll still be working on bootstrap. However my open source time is getting more and more segmented (now with bower just kicking off).

Which is why i want to start looking to grow the bootstrap team beyond just Mark and I :)

tim peterson

oh i got it thanks so you're still working at Twitter I see and that Tweet awhile back about it being your last day I took out of context?

As I said to you before I'd like to help Bootstrap grow into a more mobile direction with touch handling in particular. I'm busy with my own app and learning how to code but just know that I'm keeping it in mind...

For everyone else: I closed this issue b/c there is no consensus and b/c this and the duplicate issue (#4510) has already been closed by either @Mark or @fat.

tim peterson tim-peterson closed this September 14, 2012
Jakub Sawicki

What is causing the flickering are the position.top value changes as @thealexbaron said.
But the flickering of the value itself is caused by the fact, that specified in html offset-bottom happens to be smaller than actual element's offset when having affix-bottom class set.
So what happens is:
1. plugin sees, that it needs to go to affix-bottom and changes it
2. the element is to placed high to use affix-bottom, normal affix is appended
3. go to 1
To fix it you have to make sure offset-bottom is greater than than actual offset when in affix-bottom state.

Michael Ryan

Monkey-patched vaionicle's changes onto 2.1.1 and it didn't work for me... going to apply JurekS's suggestion and see if anything changes.

cberzan

Had the same issue with flickering affix-bottom. Fixed by adding position: relative to body. If I understand correctly, the problem was that the affix element had no positioned parent -- all parents all the way to body were positioned static by default.

(FWIW, my data-offset-bottom and .affix-bottom's bottom property are set to the same value.)

hascoulijn

Thanks JurekS, your explanation made my brain figure it out

rdooh

Overview:

I have apparently fixed this issue on my current project, with similar result as @JurekS (one value larger than the other), with possible further insight: check the styles applied to whatever element you are affixing for css properties that might affect height calculations (e.g. borders, margins, etc), and either 1) remove them, or 2) account for them by modifying your offsets. I will explain my particular case below, and summarize at the end.

Scenario:

On Chrome and Firefox (Mac), I had the flickering sidebar issue after retrofitting an existing site with the new affix behaviour. After playing around with

$('.my-sidenav').affix({
    offset: {
        top: 210
        , bottom: 270 // value1 
    }
})

and

.my-sidenav.affix-bottom{
    bottom:310; /* value2 */
}

I found that when value1 and value2 were the same (the initial state, using example settings from Version 2.1.1), I was in flicker hell. After some playing, a difference of 40px (see examples above) sorted things out. Seems arbitrary, yes? After stripping down my styles, I determined that other style properties attached to this <div> included borders and margins which collectively added up to 40px

.other-awesome-sidenav-styles {
    margin-top: 19px;
    margin-bottom: 19px;
    border: 1px solid #999; /* =2 for top and bottom */
}

Recommendations:

In my case, any height-modifying styles applied to the affixed element may potentially be a hazard. I'm unclear as to whether different browsers will handle this differently, so any recommendations are highly speculative. The two solutions that I have tested on my scenario are 1) to compensate by adjusting the offset values, or 2) to modify layout and application of styles to avoid this collision of worlds.

1. Modify Offset Values

Just takes some quick calculation, and all is done. However, as stated, I'm not sure whether this is a browser-specific solution, and you may find that you're re-tweaking this if you are still making changes to any related styles.

2. Modify Layout to Avoid Collisions

This is the option I have chosen to go with. In my case, I simply nested my styled sidenav inside a parent div. I kept the beautification styles on the child div, and applied the affix styles and behaviour to the otherwise invisible parent. So

<div class="my-sidenav awesome-styles"> <!-- affix applied here -->
    <ul>...
    </ul>
</div>

becomes

<div class="my-sidenav"> <!-- affix applied here -->
    <div class="awesome-styles">
        <ul>...
        </ul>
    </div>
</div>

Some minor layout changes were required to accommodate, but the benefit that I see here is that any further changes to .awesome-styles should not require additional tinkering with any of the affix mechanics.

Summary:

The above approach has been working for me (so far). I've taken a copy of the Twitter Bootstrap examples, and played around with ways to break it, and so far, there seem to be multiple factors (but then, aren't there always?). For example, I have also come across cases where changing the position property of parent elements to position:relative have a positive effect (see @cberzan above). In any case, height-modifying styles seem to be the most obvious culprit.

In a nutshell, I think that when the browsers are calculating height/position, different metrics/procedures are being used for the affix behaviour versus the css positioning. In any case, avoiding the use of css styling directly to the affixed element is probably the best way to circumvent this, or using compensatory offsets can work if you prefer. I suspect someone out there has a better understanding of how these browsers handle height/position calculations and can confirm/refute this suggestion, and provide a better explanation of what is going on. Ideally this will lead to a mod to the affix behaviour to accommodate, or at least a note of caution in the documentation.

Alexandre Segura

+1 @cberzan, adding position: relative; to the body helped fixing random positioning issue.

solefald

Is there a fix for this yet? None of the work-arounds or patches listed in this thread have any effect and the issue still persists.

Ben Einstein

Agreed. Still have this issue. I can't figure out what's going on here either. The Math.ceil fix doesn't work for me.

Damon Skelhorn

As @jswk pointed out, this worked for me.

In my CSS;

&.affix-bottom {
    position: absolute;
    bottom: 490px;
}
&.affix {
    top: 65px;
    position: fixed;
}

And within my JavaScript;

$('#search-right').affix({
  offset: {
    top: 20
  , bottom: 510
  }
})

The key to getting rid of the jumping is to play with these two values. (offset-bottom (js) and .affix-bottom bottom (css)

Stefano Zoffoli

In my projects I solved by changing these lines:

    affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ?
      false    : offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ?
      'bottom' : offsetTop != null && scrollTop <= offsetTop ?
      'top'    : false

with these lines:

    affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ?
      false    : offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom - (this.affixed === 'bottom' ? offsetTop : 0)) ?
      'bottom' : offsetTop != null && scrollTop <= offsetTop ?
      'top'    : false
Ben Einstein
Igor Pruchanskiy

Yeah, demonsk's and stefanozoffoli's fixes had no effect here either. I had to completely disable affix to get rid of the flickering.

Ben Einstein
Alexandre Segura

These "nested" ternary operators in affix.js are unreadable

Manuel Boy

adding "position: relative;" to the body worked for me, too!

Harianto van Insulinde

Hey guys I got this working. I hope this works for you as well. I'm a bit new to Git so I don't know how to share.

Replace this

affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ?
      false    : offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ?
      'bottom' : offsetTop != null && scrollTop <= offsetTop ?
      'top'    : false

with these lines

var before_y_bool = (offsetTop != null && scrollTop < offsetTop)
    , inside_y_bool = (scrollTop >= offsetTop && scrollTop <= scrollHeight-offsetBottom-this.$element.height()-(this.unpin != null ? this.unpin : 0) )
    , before_offset_bottom_bool = (offsetBottom != null && (position.top + this.$element.height() <= scrollHeight - offsetBottom))

affix = before_y_bool ? 'top' : (inside_y_bool && before_offset_bottom_bool) ? false : 'bottom'

The flickering cause by position.top. To make use of position.top, I make sure that within range of inside_y_bool.

Now I can finally sleep, so tired. Have been working this fix 4 long days.

I got this working on these pages:
http://oib.mdstn.com/paulagyiri/javascript.html
http://oib.mdstn.com/paulagyiri/test.html

Ben Einstein
garakelian

I had similar flickering issues as described here, and after some investigation I made a change to my copy of bootstrap-affix.js which seemed to correct the issue for me. Perhaps it will work for others.

I put a simple loop around the code in checkPosition like so:

for(var iter=0; iter<2; iter++) { // add this loop
var scrollHeight = $(document).height()
, scrollTop = this.$window.scrollTop()
..........
this.$element.removeClass(reset).addClass('affix' + (affix ? '-' + affix : ''))
}

This ensured that the affix state could transition from "affix-top" to "affix" (first iteration through loop), and then from "affix" to "affix-bottom" (on the second iteration).

In my case, checkPosition would not transition the state from "affix-top" directly to "affix-bottom" (my CSS is below, in case anyone finds it useful).

An easy way for me to experiment with this issue was to position the browser at the bottom of the page (e.g. once affix-bottom had been applied), and then to refresh the web page. After the page reloaded, the "affix" style was applied by bootstrap -- not affix-bottom. If I scrolled the page even one pixel, checkPosition would then apply the "affix-bottom" style.

I am using Chrome.

My CSS looks like this:

#affixed-item.affix {
    position: fixed;
    top: 60px; 
}
#affixed-item.affix-bottom {
    position: absolute;
    top: auto;
    bottom: 0px; 
}
Kerry-Lee Kerry-Lee referenced this issue from a commit in Kerry-Lee/bootstrap November 03, 2012
Kerry-Lee Should solve #4647 52d2328
Kerry-Lee

@beinstein, @solefald - can you guys let me know if my proposed solution works for you (it's a one-liner)? Thanks.

Ben Einstein

No sorry, it doesn't help. offsetTop is always undefined when at the bottom of the page (as far as I understand it). The problem definitely seems to be with the line:

affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ?
      false    : offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ?
      'bottom' : offsetTop != null && scrollTop <= offsetTop ?
      'top'    : false
Gabriel Mazzini

i took off the position top, as in my case the element is bigger than the window size.

affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ?
false : offsetBottom != null && (scrollHeight - ( scrollTop + $(window).height() ) < offsetBottom) ?
'bottom' : offsetTop != null && scrollTop <= offsetTop ?
'top' : false

Travis Simpson

The problem is with the positioning of the element when the affix-bottom class is applied to the element.

The flickering is affix detecting that you have hit the bottom threshold and applying the affix-bottom class to the element and removing the affix class.

If the affix-bottom class is not defined, the element will snap back to the top of the page (position: fixed, part of the affix class, is removed)....

Or the affix-bottom class is defined, but is moving the element out of the bottom threshold...

Then the next scroll event that is fired runs the code again finds the element at the top of the page and re-applies the affix class (and removes affix-bottom) which makes the position fixed again.

In a single scrolling motion the scroll event is fired several times and this process is repeated over and over, causing the flashing or violent movement (violent movement if it is still visible, flashing if it disappears from the visible part of the page).

The key: Properly implement the positioning of the affix-bottom so that it does not move out of the bottom threshold

If you would like to see the position fluctuate, you can put a console.log(position.top); after it has been set in Affix.prototype.checkPosition.

I don't think this is actually a bug with Bootstrap, it is just obscure and could really use some documentation in the official docs on the Bootstrap site.

Ben Einstein

I see your logic with that comment, but I'm a bit stumped. When I manually set the class of the affix element to affix-bottom the element sticks to the bottom of the content perfectly (with custom CSS). When it's added from JS it jumps every other scroll event.

Wayde Christie

@stefanozoffoli's fix worked for me, but it doesn't work without position:relative on the body.

Travis Simpson

@beinstein Any chance you can post a fully functioning copy of the page you have this in?

Ben Einstein

@codegoat Not straight out of trunk, but let me try to boil something down. I may need a day or two (and may figure out the issue when weeding out the unnecessary code).

Joyce Babu

I was able to fix the issue with the following two changes

  1. Added position:relative; to body
  2. Changed affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ? false : offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ? 'bottom' : offsetTop != null && scrollTop <= offsetTop ? 'top' : false to affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ? false : offsetBottom != null && (Math.ceil(position.top) + this.$element.height() >= scrollHeight - offsetBottom) ? 'bottom' : offsetTop != null && scrollTop <= offsetTop ? 'top' : false
iccoha

Thank you for all!

I could fix with:

CSS:

.my-affix.affix { position: fixed; top: 0px; bottom: auto; }
.my-affix.affix-bottom { position: fixed; top: auto; bottom: 120px; }

JS:

$('.my-affix').affix({
    offset: {
        top: 200,
        bottom: 120
    }
});

The height of the footer is 80px in my case.

Cristian Rojas

All that you need to do is to add position: relative; for body tag, you don't need to change anything in the javascript plugin.

You can test it in the bootstrap official page http://twitter.github.com/bootstrap/javascript.html#affix by removing this property for the body with firebug toolbar and you will see that there is the same problem.

Ben Einstein

Agreed, but unfortunately this seems to not work with certain browsers and/or other CSS styles. Haven't had enough time to figure out what's going on, but there's definitely something that needs to be tweaked to get this to work.

Roger Qiu

This is still happening, it should be fixed in the core?

Thomas Boyce

Adding position: relative; to body was all I had to do to fix this, like @cberzan said. I spent like a week searching for this solution though!

Thanks @cberzan

Kristijan

Make sure you set

.html, body {
  position: relative
  height: auto
}
Andre

I'm having this issue and have tried most if not all permutations of suggested solutions.

@dreweathers: the best way to implement this I've found is:

  • set .body { position: relative; }
  • define .affix-bottom { bottom: YYpx; position: absolute; } in your css
  • set your data-offset-top="XX" and data-offset-bottom="YY" on the data-spy="affix" tag for the element where you want the affix plugin. XX is the position from the top where the affix will occur and YY is the position from the bottom where you want it to scroll back up. Note that the data-offset-bottom value must be the same for the .affix-bottom value defined in the css.
Andre
Andre

got it working looks like the combination of top/bottom:auto was causing my issue
.cj-sidebar.affix {
top: 50px;
position: fixed;
}
.cj-sidebar.affix-top {
top: 345px;
}
.cj-sidebar.affix-bottom {
top: auto;
bottom: 550px;
position: absolute;
}
and data-spy="affix" data-offset-top="295" data-offset-bottom="550"

Jacob Friis Saxberg

What I learned solving my bug is that .affix-bottom { position: absolute; bottom: Xpx; } must be the exact same as data-offset-bottom="X".

davidnknight

Issue still exists and none of the above helped. When at the point of this flickering, even clicking the mouse shows/hides the afflix affected element. Can also confirm that the same issue is happening on Bootstrap site.

Vasiliy Kuzmenko

Followed by the two advice, from @dreweathers & @cristiangrojas and it's working correctly!
My CSS:

.b-scroll-nav.affix {
  top: 0;
  position: fixed;
}
.b-scroll-nav.affix-bottom {
  top: auto;
  bottom: 803px;
  position: absolute;
}
body {
  position: relative
}

affix plugin call via js, and affix-top calculated automatically via .position().

Ross Edman

This is still an issue for me. I have tried everything above. Doesn't work.

Deleted user
ghost commented April 02, 2013

The content you are editing has changed. Reload the page and try again.

I solved this by not attaching the "affix" class to an actual unordered nav list. I simply gave the unordered list a parent container and the "affix-top / affix / affix-bottom" classes are dynamically added to that.

Sending Request…

Attach images by dragging & dropping or selecting them. Octocat-spinner-32 Uploading your images… Unfortunately, we don't support that file type. Try again with a PNG, GIF, or JPG. Yowza, that's a big file. Try again with an image file smaller than 10MB. This browser doesn't support image attachments. We recommend updating to the latest Internet Explorer, Google Chrome, or Firefox. Something went really wrong, and we can't process that image. Try again.

atorBOT atorBOT referenced this issue in t3framework/t3 April 05, 2013
Closed

Affix-bottom jumping #69

Pedro Assumpção

I fixed this problem (BS version 2.1.1.0 because I couldn't update it) with:

1) adding a position: relative to body;

2) passing the offset with data attributes, no js. With .container-for-affixed-elements like this:

<div class="container-for-affixed-elements" data-spy="affix" data-offset-top="0" data-offset-bottom="470">
...
</div>

3) adding some css:

.affix {
top: 60px;
}
.affix-bottom {
position: absolute;
top: auto;
bottom: 470px;
}

PS: my footer has about 450px height and my affixed elements are 60px from top.

Peter Lau

@HariantoAtWork
You're a lifesaver. Your 4 sleepless nights are well appreciated - at least here!

I think this discussion is conflating different issues with similar symptoms. Not setting position:relative on the enclosing element creates a problem that is different from the one I'm encountering - which results from position.top toggling when the floated element goes from position:fixed to position:absolute when affix-bottom is applied.

sandra1n

@webjay, thanks! you're right! that was my problem

Bushi Zhang
Ceddy commented April 28, 2013

I was able to solve my problem with a combination of the solutions provided by @webjay and @rdooh.

First look for any margins, borders and padding in the element you are affixing. Then add this to your positioning bottom.

So you might have:
.affix-bottom { position: absolute; bottom: (x1 + x2) px; } and data-offset-bottom= x1, where x2 is the total additional padding/margin/borders of the element.

Michael Patten
m-ux commented May 14, 2013

@fat I think the issue is that you're checking the bottom break point against position.top + this.$element.height() - the value of which can change when the css class change is applied...

You can achieve the same thing by checking it against scrollTop + this.$window.height() without running into issues if the css class changes the element's position:

affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ? 
    false    : offsetBottom != null && (scrollTop + this.$window.height() >= scrollHeight - offsetBottom) ?
    'bottom' : offsetTop != null && scrollTop <= offsetTop ?
    'top'    : false

Did the trick for me.

Michael Patten
m-ux commented May 15, 2013

Actually, that code above only works in certain conditions as well... I don't think this block of code can be universally applied to all window / element sizes... might require some rethinking

kherapreet

@HariantoAtWork Your solution works like a charm. I am using minified version of bootstrap, I had to modify your code to use in my application.
var by = g != null && c < g, iy = c >= g && c <= b - f - this.$element.height() - (this.unpin != null ? this.unpin: 0), bo = f != null && d.top + this.$element.height() <= b - f; i = by ? 'top' : (iy && bo) ? false : 'bottom';

Thanks a lot you saved my life.

Jason Levine

I also had this issue and no margins, borders, or padding around the affixed element. What seems to have fixed it for me was the suggestion above of adding "position: relative" to the body. Without that, it flickers. With it, I'm fine.

andyicelab

I experienced the flickering issue when I first tried using affix-bottom as well as affix.

I was neglecting to take into account the height of my footer when calculating the bottom value. Make sure you add the height of any elements you're trying to stop overlapping (assuming you don't want your affixed element to reach the bottom of the screen).

For me, this was:

.affix-bottom {
  position: absolute;
  top: auto;
  bottom: 60px;
}

$('.fixed-nav').affix({ offset: { top: 30, bottom: 160 } });

160px being the footer height (100px) + the desired offset (60px). You can calculate the height of the footer dynamically to avoid layout fragility.

Getting this maths right solved any flickering.

strebor1982

I actually managed to fix this with a combination of @andyicelab and @HariantoAtWork

I added the following CSS courtesy of @andyicelab with 'bottom: 40px;' being the height of my footer

.affix-bottom {
  position: absolute;
  top: auto;
  bottom: 40px;
}

Set the offset in the options aswell

this.nav.affix({
    offset: {
         top: 0,
         bottom: 40
    }
});

I couldn't amend the bootstrap.js itself so I just extended that method with the fix from @HariantoAtWork

_.extend($.fn.affix.Constructor.prototype, {
        checkPosition: function () {
            if (!this.$element.is(':visible')) return

            var scrollHeight = $(document).height()
                , scrollTop = this.$window.scrollTop()
                , position = this.$element.offset()
                , offset = this.options.offset
                , offsetBottom = offset.bottom
                , offsetTop = offset.top
                , reset = 'affix affix-top affix-bottom'
                , affix

            if (typeof offset != 'object') offsetBottom = offsetTop = offset
            if (typeof offsetTop == 'function') offsetTop = offset.top()
            if (typeof offsetBottom == 'function') offsetBottom = offset.bottom()

            var before_y_bool = (offsetTop != null && scrollTop < offsetTop)
                , inside_y_bool = (scrollTop >= offsetTop && scrollTop <= scrollHeight-offsetBottom-this.$element.height()-(this.unpin != null ? this.unpin : 0) )
                , before_offset_bottom_bool = (offsetBottom != null && (position.top + this.$element.height() <= scrollHeight - offsetBottom))

            affix = before_y_bool ? 'top' : (inside_y_bool && before_offset_bottom_bool) ? false : 'bottom'

            if (this.affixed === affix) return

            this.affixed = affix
            this.unpin = affix == 'bottom' ? position.top - scrollTop : null

            this.$element.removeClass(reset).addClass('affix' + (affix ? '-' + affix : ''))
        }
    });

This sorted it all for me

pascalculator

Wow. I've spent a complete afternoon trying to solve this. Just like some of you I've tried all possible fixes, but to no avail. I've found a sollution, but I'm sure this will only work in particular cases.

As said before: the documentation for this plugin should be a lot more elaborate, because there are so many things you can get wrong if you don't know how the plugin works. But I must admit, it is in fact an awesome, well written plugin, once you know how to implement it.

Anyway, here's a guide that might be of help, including a cleanup of the problematic code that has been justifiably so been indicated as the big culprit:

1. Is your CSS working correctly?
First make sure that your affix, affix-bottom classes do what they are supposed to do. Disable the affix plugin for now and apply the classes on the element you want to fixate one by one.

Apply the affix, affix-bottom and eventually affix-top classes individually, if you have those, to see if the class puts the element in the right place. If the element doesn't show in the right place, you have a CSS problem. Most likely a position:relative container that's missing somewhere.

If your positioning works just fine without the affix method, you know the problem isn't CSS related. On to the next step.

2. Is your bottom value set to the right value?
The flickering seemed to occur (at least with me) when you defined a bottom position that is smaller than the absolute position of the bottom of the container that holds the fixated element.

!!! Do not add the element height to this absolute bottom position, the affix plugin does that for you.

Make sure the bottom value is defined as the absolute bottom position of the container (absolute meaning relative to the window object). If you have jQuery running, which you have to if you're working with bootstrap.js, you can fill out the following piece of code into your console, replacing #ID-OF-CONTAINER with the id of your container:

console.log(($('#ID-OF-CONTAINER').height() + $('#ID-OF-CONTAINER').position().top))

You can decide to use this value to insert it as the value of the offset top object. Or, you can fill in the function as the value, so you don't have to worry if the height of the container ever would change, like so:

var topPosition     =   $('#ID-OF-CONTAINER').position().top
var bottomPosition  =   ($('#ID-OF-CONTAINER').height() + topPosition);

$('#ID-OF-FIXATED-ELEMENT').affix({
    offset: {
            top     :   topPosition
            , bottom    :   bottomPosition
        }
    });

3. Is it still broken? Then the problem is probably somewhere in the bootstrap.js

Just a little disclaimer: be careful when you're venturing into this step, because you're altering native bootstrap code. Doesn't seem like a wise thing to do :)

That being said: in the bootstrap.js or the bootstrap-affix.js change the following piece of completely illegible code

affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ?
      false    : offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ?
      'bottom' : offsetTop != null && scrollTop <= offsetTop ?
      'top'    : false

by the more readable sollution that took me way too long to figure out:

if (this.unpin != null && (scrollTop + this.unpin <= position.top))
{
    affix   =   false;
}
else
{
    if (offsetBottom != null && (scrollTop + this.$element.height() >= offsetBottom))
    {
        affix   =   'bottom';
    }
    else
    {
            if (offsetTop != null && (scrollTop <= offsetTop) )
            {
                affix   =   'top';
            }
            else
            {
                affix   =   false;
            }
    }
}

If you asked me what was wrong, I would probably say that there was a logical error made in the 2nd and 3rd if-clause, making things way more complicated than they should have been in my opinion. This was probably with good reason, but we'll never know because there's no doc on the plugin (for now). But to be honest, I'm not entirely sure. I'm just glad I got it working.

I hope this, or any of the other sollutions saves you the hours I had to spend on this trivial, but incredibly frustrating conundrum.

Cheers and happy hacking.

Daniel Ice

After hours of working through this bug I found a combination that works. I wrote up the steps I took here: http://coderguy.com/2013/06/18/twitter-bootstrap-affix-nav-tutorial.html

Matthew Sprankle

Thank you @pascalculator it worked great.

Pascal Verstegen

@pedroassumpcao thanks yours did it for me :100:

Daniel Corn
cundd commented July 08, 2013

I fixed it for me by re-checking the position:

    ...
    this.$element.removeClass(reset).addClass('affix' + (affix ? '-' + affix : ''))

    // It changed so recheck
    if (affix === false && !this.recheck) {
        this.recheck = true
        this.checkPosition();
        this.recheck = false;
    }
}
...

This may produce a lot of overhead, but forces the Affix to check the element's position again, after it did remove the 'affix-bottom' or 'affix-top' class.

Finally it 'snapped' quite smoothly when setting

body {
    position: relative;
}
.affix-bottom {
    position: absolute;
    top: auto;
    bottom: 120px;
}

and

$(someelement).affix({
        offset: {
            top: 10 // Or whatever top-value
            bottom: 120 // Same as in the CSS above
        }
    });
}
Eli Silverman
jambox commented July 16, 2013

Finally got it! @stefanozoffoli, your code and @pascalculator, your advice (to disable the plugin and append the CSS classes one by one to make sure they're doing what I want) helped me iron this out after a night of frustration. Appreciate it!

Roger Qiu

This should really go into the documentation.

Jacob
Owner
fat commented July 26, 2013

if you're following along – i've somewhat changed how affix works…

in 3.0.0 there are still 3 states: affix-top, affix, and affix-bottom.

The major difference however is that the styles for affix-bottom are set by the plugin based on the offset that you provide.

For example: if you say offset: { bottom: 20 } we will automatically position your element absolutely 20px from the bottom (doing the math etc for you).

You can take a look at the bootstrap 3 docs to see it in action… but it seems to be behaving a bit better

Bruno Lazzaro

Nicee to see this has been finally resolved!

geofili

I am getting flickering in bs3 version of the affix plugin :(

It only happens if the affixed container > viewport height

As you scroll towards the bottom of the page, it toggles between affix and affix-bottom class causing the flicker.

Chris Rebert
Owner

@geofili Please file a new issue with a live example of the problem.

Bertrand Bordage BertrandBordage referenced this issue from a commit in BertrandBordage/bootstrap October 22, 2013
Bertrand Bordage Affix no longer jumps when using sticky footer (fixes #7414).
This doesn't fix the other jumping issue #4647.
6b0accc
Buttink

My problem was that the body was larger then document. So the height calculation would always be off. I just changed $(document).height() to $('body').height().

EDIT: also had to make body position: relative

Dmitry Pytkin pytkin referenced this issue in designmodo/Flat-UI-Pro-Support November 01, 2013
Closed

Affix: once bottom is reached, it doesn't move up anymore #261

Markus

Such threads are perfect examples of how much CSS is broken.

bondydaa

@cundd 's recheck if statement solution fixed my problem. I wasn't having an issue with flickering but with my affixed nav getting it's 'affixed-bottom' and style removed in firefox. This is happening for me only in Firefox and it happens on http://getbootstrap.com/javascript/ for me as well.

To recreate:
when the page first loads, continuously scroll all the way to the bottom of the page (it takes awhile on the bs page, my page was much smaller). once you hit the bottom the affix-bottom and inline style that gets applied get removed and the class will revert to 'affix' cause the nav (or whatever you have fixed) to be placed on top of your footer. If you click or scroll up again it will apply the correct affix-bottom again.

happening in firefox 17.0.7 and 25.0 (also just realized my work machine is behind so downloaded the latest version to test). The problem did not happen for me in chrome or ie8.

jensdeschrijver

This is my solution:

In het file affix.js of bootstrap.js within the affix function you find "Affix.prototype.checkPosition". At the bottom of that function, you have:

if (affix == 'bottom') {
    this.$element.offset({ top: document.body.offsetHeight - offsetBottom - this.$element.height() })
}

Change it to:

if (affix == 'bottom') {
    this.$element.offset({ top: document.body.offsetHeight - offsetBottom - this.$element.height() }).css({ position: '' })
}

In your css (or less) add this:

.affix-bottom {
    position: relative;
}

It solves the problem for me.
The same for #6575

Matthew Nessworthy

document.body.offsetHeight reports an incorrect (or at least unexpected) height, when html, body { height: 100% } is set in the css.
scrollHeight or $(document).height() report the correct height.

Changing the following (along with following the instructions regarding setting the .affix-bottom styling) solved my issues.

From

this.$element.offset({ top: document.body.offsetHeight - offsetBottom - this.$element.height() })

To

this.$element.offset({ top: scrollHeight - offsetBottom - this.$element.height() })
kevinglover

I added a css rule that worked for me:

@media (max-width: 767px){
    .affix,
    .affix-bottom{
        position:relative;
    }
}
lizsterine

I've been having a flickering problem also. For my scenario it seemed to be a javascript position calculation problem in the checkPosition() function.

Here is a gif of what I want my affix to do:
affix-animation-eg

What is happening, that when my div has the class of "affix-top" - a.k.a when it's scrolling WITH the parent - the javascript was calculating that the bottom of my div was reaching the offsetBottom when in fact the bottom of the div wasn't moving towards the offset at all. If I removed following the line in Bootstrap's checkPosition() function the calculations are then correct and I no longer get the flicker:

if (this.affixed == 'top') position.top += scrollTop

I can't see why this line was included, but I am only looking at it from my own scenario. If anyone can tell me why it's bad to NOT have this line (aside from "don't hack plugins, you can overwrite them when you upgrade!"), feel free to let me know :)

EDIT:
An alternative to hacking the plugin code, I set up my affix like this:

var margin = 40;
var t = pageHeader.offset().top + ph.outerHeight() - margin;
var b = siteFooter.outerHeight() + margin;

myElement.affix({
      offset: {
         top: function () {
             return t;
         }
         , bottom: function () {
             return (myElement.hasClass("affix-top")) ? 0 : b;
         }
     }
});
thegreyspot

The solution to this problem is VERY unclear. Everyone seems to have their own. Is there a way we can sum it up and push a fix or update the documentation?

li(codes whth soul)

var margin = 40;
var t = pageHeader.offset().top + ph.outerHeight() - margin;
var b = siteFooter.outerHeight() + margin;

myElement.affix({
offset: {
top: function () {
return t;
}
, bottom: function () {
return (myElement.hasClass("affix-top")) ? 0 : b;
}
}
});

shu.chen

Re: #4647 (comment)
Modifying the bootstrap.js file by removing the line in CheckPosition fixed it for me. The alternative solution in that comment works only in cases where you scroll incrementally. Scrolling in large chunks (using pageup/pagedown) makes it set the wrong .affix class (that is, if you jump from .affix-top straight to somewhere below the (intended) bottom offset, it'll use 0 as the bottom.offset).

Harianto van Insulinde
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.