affix-native.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. /* Native Javascript for Bootstrap 3 | Affix
  2. -------------------------------------------*/
  3. // AFFIX DEFINITION
  4. var Affix = function(element, options) {
  5. // initialization element
  6. element = queryElement(element);
  7. // set options
  8. options = options || {};
  9. // read DATA API
  10. var targetData = element[getAttribute](dataTarget),
  11. offsetTopData = element[getAttribute](dataOffsetTop),
  12. offsetBottomData = element[getAttribute](dataOffsetBottom),
  13. // component specific strings
  14. affix = 'affix', affixed = 'affixed', fn = 'function', update = 'update',
  15. affixTop = 'affix-top', affixedTop = 'affixed-top',
  16. affixBottom = 'affix-bottom', affixedBottom = 'affixed-bottom';
  17. this[target] = options[target] ? queryElement(options[target]) : queryElement(targetData) || null; // target is an object
  18. this[offsetTop] = options[offsetTop] ? options[offsetTop] : parseInt(offsetTopData) || 0; // offset option is an integer number or function to determine that number
  19. this[offsetBottom] = options[offsetBottom] ? options[offsetBottom]: parseInt(offsetBottomData) || 0;
  20. if ( !this[target] && !( this[offsetTop] || this[offsetBottom] ) ) { return; } // invalidate
  21. // internal bind
  22. var self = this,
  23. // constants
  24. pinOffsetTop, pinOffsetBottom, maxScroll, scrollY, pinnedTop, pinnedBottom,
  25. affixedToTop = false, affixedToBottom = false,
  26. // private methods
  27. getMaxScroll = function(){
  28. return Math.max( DOC[body][scrollHeight], DOC[body][offsetHeight], HTML[clientHeight], HTML[scrollHeight], HTML[offsetHeight] );
  29. },
  30. getOffsetTop = function () {
  31. if ( self[target] !== null ) {
  32. return self[target][getBoundingClientRect]()[top] + scrollY;
  33. } else if ( self[offsetTop] ) {
  34. return parseInt(typeof self[offsetTop] === fn ? self[offsetTop]() : self[offsetTop] || 0);
  35. }
  36. },
  37. getOffsetBottom = function () {
  38. if ( self[offsetBottom] ) {
  39. return maxScroll - element[offsetHeight] - parseInt( typeof self[offsetBottom] === fn ? self[offsetBottom]() : self[offsetBottom] || 0 );
  40. }
  41. },
  42. checkPosition = function () {
  43. maxScroll = getMaxScroll();
  44. scrollY = parseInt(getScroll().y,0);
  45. pinOffsetTop = getOffsetTop();
  46. pinOffsetBottom = getOffsetBottom();
  47. pinnedTop = ( parseInt(pinOffsetTop) - scrollY < 0) && (scrollY > parseInt(pinOffsetTop) );
  48. pinnedBottom = ( parseInt(pinOffsetBottom) - scrollY < 0) && (scrollY > parseInt(pinOffsetBottom) );
  49. },
  50. pinTop = function () {
  51. if ( !affixedToTop && !hasClass(element,affix) ) { // on loading a page halfway scrolled these events don't trigger in Chrome
  52. bootstrapCustomEvent.call(element, affix, affix);
  53. bootstrapCustomEvent.call(element, affixTop, affix);
  54. addClass(element,affix);
  55. affixedToTop = true;
  56. bootstrapCustomEvent.call(element, affixed, affix);
  57. bootstrapCustomEvent.call(element, affixedTop, affix);
  58. }
  59. },
  60. unPinTop = function () {
  61. if ( affixedToTop && hasClass(element,affix) ) {
  62. removeClass(element,affix);
  63. affixedToTop = false;
  64. }
  65. },
  66. pinBottom = function () {
  67. if ( !affixedToBottom && !hasClass(element, affixBottom) ) {
  68. bootstrapCustomEvent.call(element, affix, affix);
  69. bootstrapCustomEvent.call(element, affixBottom, affix);
  70. addClass(element,affixBottom);
  71. affixedToBottom = true;
  72. bootstrapCustomEvent.call(element, affixed, affix);
  73. bootstrapCustomEvent.call(element, affixedBottom, affix);
  74. }
  75. },
  76. unPinBottom = function () {
  77. if ( affixedToBottom && hasClass(element,affixBottom) ) {
  78. removeClass(element,affixBottom);
  79. affixedToBottom = false;
  80. }
  81. },
  82. updatePin = function () {
  83. if ( pinnedBottom ) {
  84. if ( pinnedTop ) { unPinTop(); }
  85. pinBottom();
  86. } else {
  87. unPinBottom();
  88. if ( pinnedTop ) { pinTop(); }
  89. else { unPinTop(); }
  90. }
  91. };
  92. // public method
  93. this[update] = function () {
  94. checkPosition();
  95. updatePin();
  96. };
  97. // init
  98. if ( !(stringAffix in element ) ) { // prevent adding event handlers twice
  99. on( globalObject, scrollEvent, self[update], passiveHandler );
  100. !isIE8 && on( globalObject, resizeEvent, self[update], passiveHandler );
  101. }
  102. element[stringAffix] = self;
  103. self[update]();
  104. };
  105. // AFFIX DATA API
  106. // =================
  107. supports[push]([stringAffix, Affix, '['+dataSpy+'="affix"]']);