views:

811

answers:

1

I've created a function that scrolls a given child element into view within its parent. It goes as follows:

function keepScrolledOver( elem )
{
 frame = elem.parent();

 var scrollPos = frame.scrollTop();
 var offset = elem.attr( "offsetTop" );

 // If the element is scrolled too high...
 if( offset < scrollPos )
 {
  frame.scrollTop( offset );
  // frame.attr( "scrollTop", offset );
 }

 // If the element is scrolled too low...
 else
 {
  var frameHeight = frame.height();
  var offsetBottom = offset + elem.height();
  var scrollBottom = scrollPos + frameHeight;


  if( offsetBottom > scrollBottom )
  {
   // frame.attr( "scrollTop", offsetBottom );
   if( frameHeight < offsetBottom )
    frame.scrollTop( offsetBottom - frameHeight );
    // frame.attr( "scrollTop", offsetBottom - frameHeight );
  }
 }
}

So far, for my Firefox web app (Firefox is all I've tested it on thus far, I mean), this works great. Only issue is that for elements scrolled too low it always tends to scroll just a tiny bit past the target element rather than right up to its end. I'm not sure if element padding has something to do with or else if my maths just suck.

Anyone have any brilliant ideas on how to improve this?

+1  A: 

The section of code that fires if the element is too high works well, assuming the frame element and it's child elements are relatively positioned. If not, it uses the absolute offset and it doesn't work.

As for the section that fires if the element is too low, I assume that when you say "it always tends to scroll just a tiny bit past the target element rather than right up to its end" you are referring to the fact that the element is cut off if the frame is smaller than its offset. Try the following:

// If the frame is less than or equal to the element's height
if( frameHeight <= elem.attr('height') ){
   //Scroll to it's offsetBottom - the total frameHeight, so that the full element will be displayed
   frame.scrollTop( offsetBottom - frameHeight ); 
}else {
   //Else, the element's height is less than the frame, so the entire element will be displayed if we just scroll to the element's offsetBottom.
   frame.scrollTop( offsetBottom );
}

I created an HTML demo page as follows and did not experience any other issues. Play around with the heights of elements and see if you can get it to break. I used Firefox 3.5 and everything was cool.

    <button id="buttonTest1">Slide to Test1</button> <button id="buttonTest2">Slide to Test2</button>
<div style="position:relative;width:100%;height:620px;overflow:scroll;">
 <div style="position:relative;height:50px;"></div>
 <div id="childSlide1" style="width:50px;height:50px;position:relative;">Test</div>
 <div id="childSlide2" style="width:50px;height:50px;position:relative;top:850px;">Test2</div>
</div>


$(document).ready(function(){
 function keepScrolledOver( elem ){
        var frame = elem.parent();

        var scrollPos = frame.scrollTop();
        var offset = elem.attr( "offsetTop" );
  alert ('scrollPos: '+scrollPos+' offset: '+offset);
  //var jQoffset = elem.offset();
  //alert ('jQoffset: '+jQoffset.top+' '+jQoffset.left);
        // If the element is scrolled too high...
        if( offset < scrollPos )
        {
    alert ('scrollPos '+scrollPos+' is bigger than offset '+offset);
                frame.scrollTop( offset );
                // frame.attr( "scrollTop", offset );
        }

        // If the element is scrolled too low...
        else
        {
                var frameHeight = frame.height();
                var offsetBottom = offset + elem.height();
                var scrollBottom = scrollPos + frameHeight;
    alert('frameHeight: '+frameHeight+' offsetBottom: '+offsetBottom+' scrollBottom: '+scrollBottom);


                if( offsetBottom > scrollBottom )
                {
                        // frame.attr( "scrollTop", offsetBottom );
                        if( frameHeight <= elem.attr('height') )
      {
       frame.scrollTop( offsetBottom - frameHeight );
                            // frame.attr( "scrollTop", offsetBottom - frameHeight );
      }else {
       frame.scrollTop( offsetBottom );
      }
                }
        }
 }

 $('#buttonTest1').click(function(){
  var scrollElement = $('#childSlide1');
  keepScrolledOver(scrollElement);
 });

 $('#buttonTest2').click(function(){
  var scrollElement = $('#childSlide2');
  keepScrolledOver(scrollElement);
 });  
});
Van Miranda