-2

I got problem with positioning 2 divs - I don't know which one will have longer. When my #rightcontent div is longer than #leftcontent i want to see end of content in #leftcontent stays at bottom of screen, like on this image:

screenshot

And here is a code snippet:

for(var i=6000;i--;){
 $('#rightcontent').append(i+ ' ');
 $('#leftcontent').append("i ");
}
@font-face {
    font-family: Gill Sans MT;
    src: url("Gill Sans MT.ttf");
}

body{
 margin-top: 0px;
 margin-left: 0px;
 margin-right: 0px;
  margin-bottom: 0px;
}

#header{
 position: fixed;
 text-align: center;
 font-size: 32px;
 background: #f4f4e6;
 padding-top: 2px;
 min-width: 100%;
 min-height: 28px;
}
#content{ 
 padding-top: 38px;
 text-align: center;
}
#footer{
 position: fixed;
 text-align: center;
 background: #f4f4e6;
 min-width: 100%;
 font-style: italic;
 font-family: Gill Sans MT;
 letter-spacing: -1;
}
#rightcontent{
 float: right;
 max-width: 55%;
 padding-bottom: 20px;
}
#leftcontent{
 padding-bottom: 20px;
 max-width: 45%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="header">
  Worki do odkurzaczy
</div>
<script>
document.write("<div id=content width=" + document.documentElement.clientWidth + "px height=" + (document.documentElement.clientHeight - 64) + "px style=padding-bottom:20px;min-height:" + (document.documentElement.clientHeight - 52) + "px; data-scroll-offset=28>");
</script>
<div id="rightcontent" name="target">
</div>

<div id="leftcontent">
</div>
</div>

<script>
  document.write("<div id=footer style=top:" + (document.documentElement.clientHeight - 22) + "px;>");
  //IDK WHY ON LOCAL TEST IS HERE -32px
</script>

Post Scriptum

I found a quiet simple answer here but I don't get this color example.

rycho2009
  • 3
  • 3
  • 2
    Please clarify your specific problem or add additional details to highlight exactly what you need. As it's currently written, it’s hard to tell exactly what you're asking. Also have a look here [ask] and here [mcve] on how to improve your question further. – Asons Jun 24 '18 at 09:45
  • I make my best but sadly English is foreign language for me :( (i think I made my question more understandable) – rycho2009 Jun 28 '18 at 20:58

1 Answers1

0

The behaviour you want is doable, but requires some basic understanding of mathematics, data binding, and event listening. What you basically want is to check, for each scroll event fired on the window object, that:

  1. The shorter of the two elements, #leftcontent vs #rightcontent, has its bottom touching the bottom of the viewport
  2. If yes, the shorter element will have a fix position so that it "sticks" to the bottom of the viewport
  3. If no, the shorter element will be unfixed

The position fixing can be done by adding a .fixed class, which tells the browser to use position: fixed; bottom: 0; on the element. Now the interesting part: how do you know if the element has touched the bottom? The trick is simple:

  1. You calculate the bottom of the viewport from the top. This is just the viewport height + the viewport scroll from the top of the document.
  2. You calculate the bottom of the element from the top. This is the element's offset + the element's height.

When the value in point #1 is more than point #2, you know that you have scrolled beyond the bottom of the element. With that in mind, all you need is some logic to determine which of the two element is the shorter one: this is already covered in another StackOverflow question, so we can repurpose that logic for our use.

The last bit is that we only want to store the element's offset once: we use jQuery's .data() method to store the left + top offsets, and its height.

Here is a code that can be used:

function isBottom(el) { 

  // Cache element
  var $el = $(el);

  // Store element's offset and height once only
  var elOffsetTop = $el.offset().top;
  var elOffsetLeft = $el.offset().left;
  var elHeight = $el.height();
  if ($el.data('offsetTop') === void 0)
    $el.data('offsetTop', elOffsetTop);
  if ($el.data('offsetLeft') === void 0)
    $el.data('offsetLeft', elOffsetLeft);
  if ($el.data('height') === void 0)
    $el.data('height', elHeight);

  // Check if element is at bottom of viewport
  var viewportBottom = $(window).height() + $(window).scrollTop();
  var elementBottom = $el.data('offsetTop') + $el.data('height');
  return viewportBottom > elementBottom;
}

$(window).on('scroll', function() {
  // Get the shorter of the two elements
  // From: https://stackoverflow.com/a/13319029/395910
  var shortest = [].reduce.call($('#leftcontent, #rightcontent'), function(sml, cur) {
    return $(sml).height() < $(cur).height() ? sml : cur;
  });

  // If element is bottom, add the class 'fixed'
  $(shortest)
    .toggleClass('fixed', isBottom(shortest))
    .css('left', isBottom(shortest) ? $(shortest).data('offsetLeft') : 'auto');
});

Note that we need to assign a left property, because if the right column is the shorter one, we need to know how far it is originally offset from the left, so that position: fixed will position it correctly.

The .fixed class is dead simple:

#header {
    // Other styles
    // Add z-index so the fixed content does not overlay header
    z-index: 1;
}
.fixed {
    position: fixed;
    bottom: 0;
    z-index: 1;
}

Pro-tip: you might want to consider throttling/debouncing your scroll handler callback, for performance reasons.


See proof-of-concept example below:

for(var i=1000;i--;){
 $('#rightcontent').append(i+ ' ');
 $('#leftcontent').append("i ");
}

function isBottom(el) { 

  // Cache element
  var $el = $(el);
  
  // Store element's offset and height once only
  var elOffsetTop = $el.offset().top;
  var elOffsetLeft = $el.offset().left;
  var elHeight = $el.height();
  if ($el.data('offsetTop') === void 0)
    $el.data('offsetTop', elOffsetTop);
  if ($el.data('offsetLeft') === void 0)
    $el.data('offsetLeft', elOffsetLeft);
  if ($el.data('height') === void 0)
    $el.data('height', elHeight);
  
  // Check if element is at bottom of viewport
  var viewportBottom = $(window).height() + $(window).scrollTop();
  var elementBottom = $el.data('offsetTop') + $el.data('height');
  return viewportBottom > elementBottom;
}

$(window).on('scroll', function() {
  // Get the shorter of the two elements
  // From: https://stackoverflow.com/a/13319029/395910
  var shortest = [].reduce.call($('#leftcontent, #rightcontent'), function(sml, cur) {
    return $(sml).height() < $(cur).height() ? sml : cur;
  });
 
  // If element is bottom, add the class 'fixed'
  $(shortest)
    .toggleClass('fixed', isBottom(shortest))
    .css('left', isBottom(shortest) ? $(shortest).data('offsetLeft') : 'auto');
});
@font-face {
    font-family: Gill Sans MT;
    src: url("Gill Sans MT.ttf");
}

body{
 margin-top: 0px;
 margin-left: 0px;
 margin-right: 0px;
  margin-bottom: 0px;
}

#header{
 position: fixed;
 text-align: center;
 font-size: 32px;
 background: #f4f4e6;
 padding-top: 2px;
 min-width: 100%;
 min-height: 28px;
  z-index: 2;
}
#content{ 
 padding-top: 38px;
 text-align: center;
}
#footer{
 position: fixed;
 text-align: center;
 background: #f4f4e6;
 min-width: 100%;
 font-style: italic;
 font-family: Gill Sans MT;
 letter-spacing: -1;
}
#rightcontent{
 float: right;
 max-width: 55%;
 padding-bottom: 20px;
}
#leftcontent{
 padding-bottom: 20px;
 max-width: 45%;
}

.fixed {
  position: fixed;
  bottom: 0;
  z-index: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="header">
  Worki do odkurzaczy
</div>
<script>
document.write("<div id=content width=" + document.documentElement.clientWidth + "px height=" + (document.documentElement.clientHeight - 64) + "px style=padding-bottom:20px;min-height:" + (document.documentElement.clientHeight - 52) + "px; data-scroll-offset=28>");
</script>
<div id="rightcontent" name="target">
</div>

<div id="leftcontent">
</div>

<script>
  document.write("<div id=footer style=top:" + (document.documentElement.clientHeight - 22) + "px;>");
  //IDK WHY ON LOCAL TEST IS HERE -32px
</script>
Terry
  • 63,248
  • 15
  • 96
  • 118