3
\$\begingroup\$

The required behaviour is :

  1. The yellow span must be positioned relatively to viewport (position:fixed;) when it is inside the pink div.
  2. The height of yellow span must always be the same as viewport height minus 100px. It has a fixed width in px.
  3. The yellow span must be verticaly centered in the viewport (this rule is modified by rule 4).
  4. On scroll, the yellow span must never be outside the boundaries of the pink div. If the page is scrolled further than the pink div, the yellow span must be absolutely positionned to the bottom of the pink div and scroll up with it
  5. If the page is scrolled back up to the pink div, the yellow span must go back to fixed position (as in rule 1 and 2).

I know sticky positioning does exaclty that but I can't use it because of poor browser support. So I made this snippet and would like to know if it can be optimized or shortened :

$(document).scroll(function() {
  var span = $('span'),
    div = $('div'),
    spanHeight = span.outerHeight(),
    divHeight = div.height(),
    spanOffset = span.offset().top + spanHeight,
    divOffset = div.offset().top + divHeight;

  if (spanOffset >= divOffset) {
    span.addClass('bottom');
    var windowScroll = $(window).scrollTop() + $(window).height() - 50;
    if (spanOffset > windowScroll) {
      span.removeClass('bottom');
    }
  }
});
*{margin:0;}
div {
  position: relative;
  background: pink;
  height: 800px;
  margin-bottom: 800px;
}
span {
  position: fixed;
  right: 0;
  bottom: 50px;
  width: 10%;
  height: calc(100vh - 100px);
  background: gold;
}
.bottom {
  position: absolute;
  bottom: 0;
  right: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<div>
  <span>some content</span>
</div>

\$\endgroup\$
  • \$\begingroup\$ I don't understand: your posted example doesn't work as you describe. First the span location never changes when scrolling down, but it'd change if you keeped its position: fixed. Then once moved to the bottom it'll never go back, since henceforth spanOffset will always keep >= divOffset when scrolling up. \$\endgroup\$ – cFreed Jan 29 '16 at 18:48
  • \$\begingroup\$ @cFreed The posted example works as I described. the yellow span is fixed while it is in the pink div and moves up with it when the user scrolls furter than the pink div. \$\endgroup\$ – web-tiki Jan 30 '16 at 11:57
  • \$\begingroup\$ It's not what I observe when running the code snippet above: the yellow span never changes its location, inside the pink div! \$\endgroup\$ – cFreed Jan 30 '16 at 15:52
  • \$\begingroup\$ @cFreed if you scroll down to the bottom of the pink div, the yellow span change it's position to position:absolute and moves up with the pink div. When you scroll bac up, higher thant the bottom of the pink div, goes back to position:fixed here is a gif to demonstrate: i.imgur.com/7NU3oAO.gifv \$\endgroup\$ – web-tiki Jan 30 '16 at 16:54
  • 1
    \$\begingroup\$ Wow... now I understand. Yes, it's the behaviour I always observed, but from your original explanations I'd erroneously concluded you wanted, at the opposite, that the yellow span keeps visible always, going ouside of the pink div when this one went up! Sorry, my bad. \$\endgroup\$ – cFreed Jan 30 '16 at 17:45
4
\$\begingroup\$

This is not really a full answer yet, but after correcting my interpretation of your question (as reflected by comments under it) I keep having some incertainties, and a comment is not wide enough to clearly express them.

Anyway I can already notice some possible improvements, not depending on any further precision:

  • Cosmetic: the right: 0; property can be suppressed from span.bottom rule, since it doesn't change compared to span one.
  • Performance:
    • since their value never changes, declaration of span, div, and divHeight can be moved outside of the scroll() event.
    • we also can fix $(window) by declaring it as var win at the same level.
  • Operation: depending on the window size, the initial state may not follow your requirements, e.g.:

    • When running the code snippet and immediately switching to full page the yellow span should have the bottom class, while it doesn't.
    • The same happens when toggling between full page and reduced iframe: if the state should change when reaching the new window size, it doesn't till scrolling happens.

    Then to avoid that, the computation for affecting/dropping bottom class should occur not only when document.scroll() but also window.resize() event happens.
    Additionally, each of those events should be initially fired (although in practice only one is enough).

Here is the so modified snippet:

var span = $('span'),
    div = $('div'),
    win = $(window),
    divHeight = div.height();
$(document).scroll(compute).scroll();
$(window).resize(compute).resize();
  
function compute() {
  var spanHeight = span.outerHeight(),
      spanOffset = span.offset().top + spanHeight,
      divOffset = div.offset().top + divHeight;

  if (spanOffset >= divOffset) {
    span.addClass('bottom');
    var windowScroll = win.scrollTop() + win.height() - 50;
    if (spanOffset > windowScroll) {
      span.removeClass('bottom');
    }
  }
  console.log('spanOffset='+spanOffset, 'windowScroll='+($(window).scrollTop() + $(window).height() - 50));
}
*{margin:0;}
div {
  position: relative;
  background: pink;
  height: 800px;
  margin-bottom: 800px;
}
span {
  position: fixed;
  right: 0;
  bottom: 50px;
  width: 10%;
  height: calc(100vh - 100px);
  background: gold;
}
.bottom {
  position: absolute;
  bottom: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<div>
  <span>some content</span>
</div>

Now let me explain what keeps puzzling me.
Both snippets (yours and mine) have the following wrong behaviour: as of a certain window height (couldn't precisely figure out yet), once switched to bottom the yellow span will never go back when scrolling up.

So before thinking further I'd want to be certain of what is precisely your need. Expressed independently from the current implementation, it seems to me that it is like follows:

  1. as a fundamental requirement, the yellow span height must always be equal to window height minus a given height (say X, in this case 100px), and must not change
  2. if not upset by the rule #3 below, the yellow span must be vertically centered in the window (its bottom is half the above given X)
  3. taking over the rule #2, the yellow span must never cross the pink div (the bottom of the former can't move lower than the latter's one)

For now I remain pending. Thanks for confirming or correcting me.

\$\endgroup\$
  • \$\begingroup\$ Thanx for your review, you got some nice improvements I didn't think of in there. I was concerned about the window resize event but didn't include it in question to focus on the scroll event but it definetly is necessary. For the requirments you gave, you got them right and I'll add them in the question to clarify. For the wrong behaviour, Ill need to test some more as I haven't noticed it yet. \$\endgroup\$ – web-tiki Jan 31 '16 at 16:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.