ui.bootstrap.affix

HTML

<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0-rc.3/angular.js"></script>
<div class="page-header container">
    <h2>Affix bootstrap directive example. </h2>
    <b>Features</b>:<br> 
    <ul>
        <li>flexible classes and naming convention</li>
        <li>no jquery dependancy, only Angular js.  <code>$scope.$root.isIE</code> should be defined for default IE compatibility</li>
        <li>vertical affix, Top only currently</li>
        <li>offset scroll containers from the root document scroll</li>
    </ul>
    
    </br><b>HTML:</b><br>
    <code>
        &lt;div id="affixMe" affix="" ng-class="{affix:affixed}" ng-style="offset" relative-to="affix-demo" &gt;&lt;/div&gt;       
    </code>
    </br><i>Check html window for full example code</i></br>
    
    </br></br><b>Usage:</b><br>   
    <ul>       
        <li> 'ng-class="{affix:affixed}"': bind a class to the affixed boolean                </li>
        <li> 'ng-style="offset"' used to set the top offset of the affixed element            </li>
        <li> 'relative-to': specify the id of an element you want affix to be relative to     </li>
    </ul>
</div>

<!-- BEGIN EXAMPLE --> 

<div class="container" id="affix-demo">
    
    <!-- Filler top -->
    <div class="jumbotron">
      <h1>Hello, world!</h1>
      <p>...</p>
      <p><a class="btn btn-primary btn-lg" role="button">Learn more</a></p>
    </div>   

    <!-- Affix directive -->
    <div id="affixMe" affix="" ng-class="{affix:affixed}" ng-style="offset" relative-to="affix-demo">
        <div class="well">
            Am I affixed? {{affixed ? 'yes' : 'no'}}
        </div>
    </div>
    
    <!-- Filler bottom -->
    <div id="stretch"></div>
</div>

<!-- END EXAMPLE -->

CSS

#affix-demo {
    -margin-top:30px;
    height: 200px;
    overflow-y:scroll;
    background: lightblue;
    
}
#affixMe{
    width: 200px;
}
#affixMe.affix {
    top: 0px;
}
#stretch {
    height: 500px;  
}

JavaScript

myApp = {};
angular.module('myApp', ['ui.bootstrap.affix']);

console.log('find me');

angular.module('ui.bootstrap.affix', [])
.directive('affix', ['$window', function ($window) {
    return {
        $scope: true,
        link: function ($scope, $element, $attrs) {
            var relativeTo = // overide or use default: body (html for IE) element
                !!$attrs.relativeTo ? angular.element(window.document.getElementById($attrs.relativeTo)) : 
                    !$scope.$root.isIE ? // specify your own isIE check will default to body element
                        angular.element(window.document.getElementsByTagName('body')) : 
                        angular.element(window.document.getElementsByTagName('html')), // required by IE
                win = !!$attrs.relativeTo ? relativeTo : angular.element($window),
                // if a relativeTo other than the default is set we will 
                // offset the affixed position by it's offset
                relativeToOffset = null, 
                fixedAt = null;
            
            // boolean
            $scope.affixed = true;
            $scope.offset = 0;

            // Obviously, whenever a scroll occurs, we need to check and possibly 
            // adjust the position of the affixed $element.
            win.bind('scroll', checkPosition);

            // on resize recalculate fixedAt position 
            win.bind('resize', function () {
                $scope.$apply(function () {
                    $scope.affixed = false;
                });
                relativeToOffset = null;
                fixedAt = null;
                checkPosition();
            });

            // calculate if we need to affix element
            function checkPosition() {

                relativeToOffset = relativeToOffset || win[0].offsetTop;
                fixedAt = fixedAt || $element[0].offsetTop - relativeToOffset;
                
                $scope.$apply(function () {
                   ...