sticky-kit.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. /*!
  2. Sticky-kit
  3. Version: 1.1.3
  4. Plugin URL: https://github.com/leafo/sticky-kit
  5. License: Leaf Corcoran 2015 | Licensed under MIT
  6. !*/
  7. (function() {
  8. var $, win;
  9. $ = window.jQuery;
  10. win = $(window);
  11. $.fn.stick_in_parent = function(opts) {
  12. var doc, elm, enable_bottoming, fn, i, inner_scrolling, len, manual_spacer, offset_top, outer_width, parent_selector, recalc_every, sticky_class;
  13. if (opts == null) {
  14. opts = {};
  15. }
  16. sticky_class = opts.sticky_class, inner_scrolling = opts.inner_scrolling, recalc_every = opts.recalc_every, parent_selector = opts.parent, offset_top = opts.offset_top, manual_spacer = opts.spacer, enable_bottoming = opts.bottoming;
  17. if (offset_top == null) {
  18. offset_top = 0;
  19. }
  20. if (parent_selector == null) {
  21. parent_selector = void 0;
  22. }
  23. if (inner_scrolling == null) {
  24. inner_scrolling = true;
  25. }
  26. if (sticky_class == null) {
  27. sticky_class = "is_stuck";
  28. }
  29. doc = $(document);
  30. if (enable_bottoming == null) {
  31. enable_bottoming = true;
  32. }
  33. outer_width = function(el) {
  34. var _el, computed, w;
  35. if (window.getComputedStyle) {
  36. _el = el[0];
  37. computed = window.getComputedStyle(el[0]);
  38. w = parseFloat(computed.getPropertyValue("width")) + parseFloat(computed.getPropertyValue("margin-left")) + parseFloat(computed.getPropertyValue("margin-right"));
  39. if (computed.getPropertyValue("box-sizing") !== "border-box") {
  40. w += parseFloat(computed.getPropertyValue("border-left-width")) + parseFloat(computed.getPropertyValue("border-right-width")) + parseFloat(computed.getPropertyValue("padding-left")) + parseFloat(computed.getPropertyValue("padding-right"));
  41. }
  42. return w;
  43. } else {
  44. return el.outerWidth(true);
  45. }
  46. };
  47. fn = function(elm, padding_bottom, parent_top, parent_height, top, height, el_float, detached) {
  48. var bottomed, detach, fixed, last_pos, last_scroll_height, offset, parent, recalc, recalc_and_tick, recalc_counter, spacer, tick;
  49. if (elm.data("sticky_kit")) {
  50. return;
  51. }
  52. elm.data("sticky_kit", true);
  53. last_scroll_height = doc.height();
  54. parent = elm.parent();
  55. if (parent_selector != null) {
  56. parent = parent.closest(parent_selector);
  57. }
  58. if (!parent.length) {
  59. throw "failed to find stick parent";
  60. }
  61. fixed = false;
  62. bottomed = false;
  63. spacer = manual_spacer != null ? manual_spacer && elm.closest(manual_spacer) : $("<div />");
  64. if (spacer) {
  65. spacer.css('position', elm.css('position'));
  66. }
  67. recalc = function() {
  68. var border_top, padding_top, restore;
  69. if (detached) {
  70. return;
  71. }
  72. last_scroll_height = doc.height();
  73. border_top = parseInt(parent.css("border-top-width"), 10);
  74. padding_top = parseInt(parent.css("padding-top"), 10);
  75. padding_bottom = parseInt(parent.css("padding-bottom"), 10);
  76. parent_top = parent.offset().top + border_top + padding_top;
  77. parent_height = parent.height();
  78. if (fixed) {
  79. fixed = false;
  80. bottomed = false;
  81. if (manual_spacer == null) {
  82. elm.insertAfter(spacer);
  83. spacer.detach();
  84. }
  85. elm.css({
  86. position: "",
  87. top: "",
  88. width: "",
  89. bottom: ""
  90. }).removeClass(sticky_class);
  91. restore = true;
  92. }
  93. top = elm.offset().top - (parseInt(elm.css("margin-top"), 10) || 0) - offset_top;
  94. height = elm.outerHeight(true);
  95. el_float = elm.css("float");
  96. if (spacer) {
  97. spacer.css({
  98. width: outer_width(elm),
  99. height: height,
  100. display: elm.css("display"),
  101. "vertical-align": elm.css("vertical-align"),
  102. "float": el_float
  103. });
  104. }
  105. if (restore) {
  106. return tick();
  107. }
  108. };
  109. recalc();
  110. if (height === parent_height) {
  111. return;
  112. }
  113. last_pos = void 0;
  114. offset = offset_top;
  115. recalc_counter = recalc_every;
  116. tick = function() {
  117. var css, delta, recalced, scroll, will_bottom, win_height;
  118. if (detached) {
  119. return;
  120. }
  121. recalced = false;
  122. if (recalc_counter != null) {
  123. recalc_counter -= 1;
  124. if (recalc_counter <= 0) {
  125. recalc_counter = recalc_every;
  126. recalc();
  127. recalced = true;
  128. }
  129. }
  130. if (!recalced && doc.height() !== last_scroll_height) {
  131. recalc();
  132. recalced = true;
  133. }
  134. scroll = win.scrollTop();
  135. if (last_pos != null) {
  136. delta = scroll - last_pos;
  137. }
  138. last_pos = scroll;
  139. if (fixed) {
  140. if (enable_bottoming) {
  141. will_bottom = scroll + height + offset > parent_height + parent_top;
  142. if (bottomed && !will_bottom) {
  143. bottomed = false;
  144. elm.css({
  145. position: "fixed",
  146. bottom: "",
  147. top: offset
  148. }).trigger("sticky_kit:unbottom");
  149. }
  150. }
  151. if (scroll < top) {
  152. fixed = false;
  153. offset = offset_top;
  154. if (manual_spacer == null) {
  155. if (el_float === "left" || el_float === "right") {
  156. elm.insertAfter(spacer);
  157. }
  158. spacer.detach();
  159. }
  160. css = {
  161. position: "",
  162. width: "",
  163. top: ""
  164. };
  165. elm.css(css).removeClass(sticky_class).trigger("sticky_kit:unstick");
  166. }
  167. if (inner_scrolling) {
  168. win_height = win.height();
  169. if (height + offset_top > win_height) {
  170. if (!bottomed) {
  171. offset -= delta;
  172. offset = Math.max(win_height - height, offset);
  173. offset = Math.min(offset_top, offset);
  174. if (fixed) {
  175. elm.css({
  176. top: offset + "px"
  177. });
  178. }
  179. }
  180. }
  181. }
  182. } else {
  183. if (scroll > top) {
  184. fixed = true;
  185. css = {
  186. position: "fixed",
  187. top: offset
  188. };
  189. css.width = elm.css("box-sizing") === "border-box" ? elm.outerWidth() + "px" : elm.width() + "px";
  190. elm.css(css).addClass(sticky_class);
  191. if (manual_spacer == null) {
  192. elm.after(spacer);
  193. if (el_float === "left" || el_float === "right") {
  194. spacer.append(elm);
  195. }
  196. }
  197. elm.trigger("sticky_kit:stick");
  198. }
  199. }
  200. if (fixed && enable_bottoming) {
  201. if (will_bottom == null) {
  202. will_bottom = scroll + height + offset > parent_height + parent_top;
  203. }
  204. if (!bottomed && will_bottom) {
  205. bottomed = true;
  206. if (parent.css("position") === "static") {
  207. parent.css({
  208. position: "relative"
  209. });
  210. }
  211. return elm.css({
  212. position: "absolute",
  213. bottom: padding_bottom,
  214. top: "auto"
  215. }).trigger("sticky_kit:bottom");
  216. }
  217. }
  218. };
  219. recalc_and_tick = function() {
  220. recalc();
  221. return tick();
  222. };
  223. detach = function() {
  224. detached = true;
  225. win.off("touchmove", tick);
  226. win.off("scroll", tick);
  227. win.off("resize", recalc_and_tick);
  228. $(document.body).off("sticky_kit:recalc", recalc_and_tick);
  229. elm.off("sticky_kit:detach", detach);
  230. elm.removeData("sticky_kit");
  231. elm.css({
  232. position: "",
  233. bottom: "",
  234. top: "",
  235. width: ""
  236. });
  237. parent.position("position", "");
  238. if (fixed) {
  239. if (manual_spacer == null) {
  240. if (el_float === "left" || el_float === "right") {
  241. elm.insertAfter(spacer);
  242. }
  243. spacer.remove();
  244. }
  245. return elm.removeClass(sticky_class);
  246. }
  247. };
  248. win.on("touchmove", tick);
  249. win.on("scroll", tick);
  250. win.on("resize", recalc_and_tick);
  251. $(document.body).on("sticky_kit:recalc", recalc_and_tick);
  252. elm.on("sticky_kit:detach", detach);
  253. return setTimeout(tick, 0);
  254. };
  255. for (i = 0, len = this.length; i < len; i++) {
  256. elm = this[i];
  257. fn($(elm));
  258. }
  259. return this;
  260. };
  261. }).call(this);