From 15b23dea941a1ab1a4a89267b2531a32ed55d6bf Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 18 May 2018 02:14:00 -0700 Subject: [PATCH 1/7] Update container selector --- js/angular-sticky.js | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/js/angular-sticky.js b/js/angular-sticky.js index abde951..5eb396d 100755 --- a/js/angular-sticky.js +++ b/js/angular-sticky.js @@ -425,7 +425,7 @@ angular.module('hl.sticky', []) // @todo dffgdg function containerBoundsTop(scrolledDistance) { if (container === null) { - container = options.container !== undefined ? angular.isString(options.container) ? angular.element(documentEl.querySelector('#' + options.container))[0] : options.container : false; + container = getContainer(); } if (container) { var hasScrollDistance = !(scrolledDistance === null || scrolledDistance === undefined); @@ -437,7 +437,7 @@ angular.module('hl.sticky', []) } function containerBoundsBottom(scrolledDistance) { if (container === null) { - container = options.container !== undefined ? angular.isString(options.container) ? angular.element(documentEl.querySelector('#' + options.container))[0] : options.container : false; + container = getContainer(); } if (container) { var hasScrollDistance = !(scrolledDistance === null || scrolledDistance === undefined); @@ -448,6 +448,23 @@ angular.module('hl.sticky', []) return 0; } + function getContainer() { + var selector, el = false; + + if (angular.isDefined(options.container)) { + if (angular.isString(options.container)) { + selector = options.container; + if (selector.indexOf(".") === -1 && selector.indexOf("#") === -1) { + selector = "#" + selector; + } + el = angular.element(documentEl.querySelector(selector))[0]; + } else { + el = options.container; + } + } + return el; + } + var $api = {}; if (stack) { From 422726a68c1e697bbf5dd2d648a50a4e78a8707b Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 14 Jan 2020 17:29:44 -0800 Subject: [PATCH 2/7] Build release files --- dist/angular-sticky.js | 23 ++++++++++++++++++++--- dist/angular-sticky.min.js | 4 ++-- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/dist/angular-sticky.js b/dist/angular-sticky.js index 037250c..da1fe55 100644 --- a/dist/angular-sticky.js +++ b/dist/angular-sticky.js @@ -2,7 +2,7 @@ * angular-sticky-plugin * https://github.com/harm-less/angular-sticky - * Version: 0.5.0 - 2018-10-25 + * Version: 0.5.0 - 2020-01-14 * License: MIT */ 'use strict'; @@ -432,7 +432,7 @@ angular.module('hl.sticky', []) // @todo dffgdg function containerBoundsTop(scrolledDistance) { if (container === null) { - container = options.container !== undefined ? angular.isString(options.container) ? angular.element(documentEl.querySelector('#' + options.container))[0] : options.container : false; + container = getContainer(); } if (container) { var hasScrollDistance = !(scrolledDistance === null || scrolledDistance === undefined); @@ -444,7 +444,7 @@ angular.module('hl.sticky', []) } function containerBoundsBottom(scrolledDistance) { if (container === null) { - container = options.container !== undefined ? angular.isString(options.container) ? angular.element(documentEl.querySelector('#' + options.container))[0] : options.container : false; + container = getContainer(); } if (container) { var hasScrollDistance = !(scrolledDistance === null || scrolledDistance === undefined); @@ -455,6 +455,23 @@ angular.module('hl.sticky', []) return 0; } + function getContainer() { + var selector, el = false; + + if (angular.isDefined(options.container)) { + if (angular.isString(options.container)) { + selector = options.container; + if (selector.indexOf(".") === -1 && selector.indexOf("#") === -1) { + selector = "#" + selector; + } + el = angular.element(documentEl.querySelector(selector))[0]; + } else { + el = options.container; + } + } + return el; + } + var $api = {}; if (stack) { diff --git a/dist/angular-sticky.min.js b/dist/angular-sticky.min.js index c675357..5b4202f 100644 --- a/dist/angular-sticky.min.js +++ b/dist/angular-sticky.min.js @@ -2,7 +2,7 @@ * angular-sticky-plugin * https://github.com/harm-less/angular-sticky - * Version: 0.5.0 - 2018-10-25 + * Version: 0.5.0 - 2020-01-14 * License: MIT */ -"use strict";angular.module("hl.sticky",[]).factory("mediaQuery",function(){return{matches:function(t){return t&&(matchMedia("("+t+")").matches||matchMedia(t).matches)}}}).factory("hlStickyStack",["$document","StickyStackDefaults",function(t,e){function n(t){var n=(t=t||{}).name||e.defaultStack;if(r[n])return r[n];var a=t.zIndex||e.zIndex,i=[],c={};return c.options=t,c.stackName=n,c.add=function(t,e){return angular.isString(t)&&""!==t||(t=c.length()),e.id=t,e.zIndex=a,i.push(e),a-=1,e},c.get=function(t){for(var e=0;e=c()}function h(t){return(t=void 0!==t?t:window.pageYOffset||M.scrollTop)+window.innerHeight<=l()}function m(){return!1===Q||r.matches(Q)}function g(){var t=f(Y);angular.isDefined(i.enable)&&!i.enable&&(t=!1),angular.isDefined(i.alwaysSticky)&&i.alwaysSticky&&(t=!0),t&&!T?(k(),j({event:"stick"})):!t&&T&&(p(),j({event:"unstick"})),y(),T&&("top"===Y?o.css("top",L+C(Y)-I()+"px"):"bottom"===Y&&o.css("bottom",R+C(Y)-z()+"px"),o.css("width",v()+"px"))}function y(){var t=null;switch(o.removeClass(i.beforeStickyClass),o.removeClass(i.afterStickyClass),Y){case"top":T?I()>0&&(o.addClass(i.afterStickyClass),t="after"):(o.addClass(i.beforeStickyClass),t="before");break;case"bottom":z()>0?(o.addClass(i.beforeStickyClass),t="before"):T||(o.addClass(i.afterStickyClass),t="after")}"before"!==t&&o.removeClass(i.beforeStickyClass),"after"!==t&&o.removeClass(i.afterStickyClass)}function k(){T=!0,o.addClass(O),i.usePlaceholder&&((H=H||angular.element("
")).css("height",S()+"px"),o.after(H));var t=B.getBoundingClientRect(),e={width:v()+"px",position:"fixed",left:t.left+"px","z-index":q?q.get(N).zIndex-(U.zIndex||0):null};e["margin-"+Y]=0,o.css(e)}function p(){T=!1,o.removeClass(O),o.attr("style",W.style),H&&H.remove()}function v(){return B.offsetWidth}function S(){return B.offsetHeight}function b(t){var e=0;if(t&&t.offsetParent)do{e+=t.offsetTop,t=t.offsetParent}while(t);return e}function w(t){return b(t)+t.clientHeight}function C(t){var e=0;if("top"===t&&U.top>0&&(e+=U.top),"bottom"===t&&U.bottom>0&&(e+=U.bottom),q){var n=q.index(N);"top"===t&&n>0&&q.range(0,n).forEach(function(n){n.isSticky()&&(e+=n.computedHeight(t))}),"bottom"===t&&n!==q.length()-1&&q.range(n+1,q.length()).forEach(function(n){n.isSticky()&&(e+=n.computedHeight(t))})}return e}function x(){return C("top")}function E(){return C("bottom")}function $(t,e){return"top"===t?Math.max(0,S()-I(e)+L):"bottom"===t?Math.max(0,S()-z(e)+R):0}function z(t){if(null===F&&(F=void 0!==i.container&&(angular.isString(i.container)?angular.element(A.querySelector("#"+i.container))[0]:i.container)),F){var e=!(null===t||void 0===t),n=F.getBoundingClientRect(),o=e?b(F)+n.height-t:n.top-window.innerHeight+S();return Math.max(0,o+L+R-C(Y))}return 0}function I(t){if(null===F&&(F=void 0!==i.container&&(angular.isString(i.container)?angular.element(A.querySelector("#"+i.container))[0]:i.container)),F){var e=!(null===t||void 0===t),n=F.getBoundingClientRect(),o=e?b(F)+n.height-t:n.bottom;return Math.max(0,L+C(Y)+S()+R-o)}return 0}i=i||{};var D,P,H,T=!1,M=t[0].body,B=o[0],A=t[0].documentElement;angular.isFunction(i.event)||delete i.event,angular.forEach(a,function(t,e){angular.isUndefined(i[e])||""===i[e]?i[e]=t:i[e]&&!isNaN(i[e])&&(i[e]*=1)});var N=i.id,Q=i.mediaQuery,O=i.stickyClass,L=(i.usePlaceholder,i.offsetTop),R=i.offsetBottom,Y=i.anchor.toLowerCase().trim(),j=i.event,q=!1===i.stack?null:i.stack||n({zIndex:i.zIndex}),F=null,U={top:0,bottom:0},W={style:o.attr("style")||""};o.addClass("sticky-"+Y);var G={};if(q){var J=q.add(N,G);N=J.id}return G.draw=function(t){var e=(t=t||{}).offset;e&&(U.top=e.top||0,U.bottom=e.bottom||0,U.zIndex=e.zIndex),!0===t.force&&p(),g()},G.anchor=function(){return Y},G.isSticky=s,G.isEnabled=u,G.computedHeight=$,G.sticksAtPosition=f,G.destroy=function(){p(),q&&q.remove(N)},G}}]).constant("StickyStackDefaults",{id:null,enable:!0,mediaQuery:!1,stickyClass:"is-sticky",beforeStickyClass:"sticky-before",afterStickyClass:"sticky-after",usePlaceholder:!0,offsetTop:0,offsetBottom:0,anchor:"top",container:null,event:angular.noop,stack:null,defaultStack:"default-stack",collection:null,collectionParent:null,zIndex:1039}).provider("hlStickyElementCollection",function(){var t=0,e={collections:{},defaults:{checkDelay:20},elementsDefaults:{},$get:["$rootScope","$window","$document","$log","StickyStackDefaults","hlStickyElement","hlStickyStack","throttle",function(n,o,r,a,i,c,l,u){function s(){t++,h(),t>1||(p=u(h,e.defaults.checkDelay),v.on("resize",p),v.on("scroll",d),y=n.$on("$viewContentLoaded",p),k=n.$on("$includeContentLoaded",p),p())}function f(){--t>0||(v.off("resize",p),v.off("scroll",d),y(),k())}function d(){m()}function h(){m({force:!0})}function m(t){angular.forEach(e.collections,function(e){e.draw(t)})}function g(t){t&&angular.isObject(t)||(a.warn("Must supply an options object"),t={});var n=(t=angular.extend({},e.elementsDefaults,t)).name||i.defaultStack;if(e.collections[n])return e.collections[n];var o=l({name:n,zIndex:t.zIndex}),r=[],u={};return u.addElement=function(t,e){(e=e||{}).stack=o;var n=c(t,e);return r.push({stickyElement:n,element:t}),n},u.removeElement=function(t){for(var e,n=r.length;n--;)if(angular.isString(t)&&"#"+r[n].element.id===t||r[n].element===t){e=n;break}var o=r.splice(e,1)[0];return o&&o.stickyElement.destroy(),o},u.draw=function(e){var n={};if(t.parent){var o=l({name:t.parent,zIndex:t.zIndex});n.offset={top:o.heightCurrent("top"),zIndex:o.length()}}angular.extend(n,e||{}),angular.forEach(r,function(t){t.stickyElement.draw(n)})},u.destroy=function(){angular.forEach(angular.copy(r),function(t){u.removeElement(t)}),delete e.collections[n],f()},u.trackedElements=function(){return r},e.collections[n]=u,s(),u}var y,k,p,v=angular.element(o);return g}]};return e}).directive("hlSticky",["$log","$window","$document","hlStickyElementCollection",function(t,e,n,o){return{restrict:"A",scope:{container:"@",anchor:"@",stickyClass:"@",mediaQuery:"@",collection:"@",collectionParent:"@",offsetTop:"@",offsetBottom:"@",zIndex:"@",event:"&",usePlaceholder:"=?",enable:"=?",alwaysSticky:"=?",options:"=?"},link:function(t,e,n){e.addClass("hl-sticky");var r;t.options?r=t.options:(r={id:n.hlSticky,event:function(){var e=t.event();angular.isFunction(e)&&e.apply(null,arguments)}},angular.forEach(t,function(e,n){"event"!==n&&"$"!==n[0]&&angular.isDefined(e)&&(r[n]=t[n],r[n]&&!isNaN(r[n])&&(r[n]*=1))}));var a=o({name:r.collection,parent:r.collectionParent,zIndex:r.zIndex});a.addElement(e,r),t.$watch("options",function(t,e){t&&t!==e&&a.draw({force:!0})},!0),t.$watch("enable",function(e,n){e!==n&&(r.enable=t.enable,a.draw({force:!0}))}),t.$watch("alwaysSticky",function(e,n){e!==n&&(r.alwaysSticky=t.alwaysSticky,a.draw({force:!0}))}),t.$on("$destroy",function(){a.removeElement(e),a.trackedElements().length||a.destroy()})}}}]).factory("throttle",["$timeout",function(t){return function(e,n,o){var r=null;return o=o||{},function(){var o=this,a=arguments;t.cancel(r),r=t(function(){e.apply(o,a)},n,!1)}}}]); \ No newline at end of file +"use strict";angular.module("hl.sticky",[]).factory("mediaQuery",function(){return{matches:function(t){return t&&(matchMedia("("+t+")").matches||matchMedia(t).matches)}}}).factory("hlStickyStack",["$document","StickyStackDefaults",function(t,e){var n=t[0].documentElement,o={};return function(t){var r=(t=t||{}).name||e.defaultStack;if(o[r])return o[r];var a=t.zIndex||e.zIndex,i=[],c={};return c.options=t,c.stackName=r,c.add=function(t,e){return angular.isString(t)&&""!==t||(t=c.length()),e.id=t,e.zIndex=a,i.push(e),a-=1,e},c.get=function(t){for(var e=0;e=c()}function d(t){return(t=void 0!==t?t:window.pageYOffset||T.scrollTop)+window.innerHeight<=l()}function h(){return!1===A||r.matches(A)}function m(){var t=s(R);angular.isDefined(i.enable)&&!i.enable&&(t=!1),angular.isDefined(i.alwaysSticky)&&i.alwaysSticky&&(t=!0),t&&!H?(y(),Y({event:"stick"})):!t&&H&&(k(),Y({event:"unstick"})),g(),H&&("top"===R?o.css("top",Q+w(R)-$()+"px"):"bottom"===R&&o.css("bottom",L+w(R)-E()+"px"),o.css("width",p()+"px"))}function g(){var t=null;switch(o.removeClass(i.beforeStickyClass),o.removeClass(i.afterStickyClass),R){case"top":H?$()>0&&(o.addClass(i.afterStickyClass),t="after"):(o.addClass(i.beforeStickyClass),t="before");break;case"bottom":E()>0?(o.addClass(i.beforeStickyClass),t="before"):H||(o.addClass(i.afterStickyClass),t="after")}"before"!==t&&o.removeClass(i.beforeStickyClass),"after"!==t&&o.removeClass(i.afterStickyClass)}function y(){H=!0,o.addClass(N),i.usePlaceholder&&((P=P||angular.element("
")).css("height",v()+"px"),o.after(P));var t=M.getBoundingClientRect(),e={width:p()+"px",position:"fixed",left:t.left+"px","z-index":j?j.get(O).zIndex-(U.zIndex||0):null};e["margin-"+R]=0,o.css(e)}function k(){H=!1,o.removeClass(N),o.attr("style",q.style),P&&P.remove()}function p(){return M.offsetWidth}function v(){return M.offsetHeight}function S(t){var e=0;if(t&&t.offsetParent)do{e+=t.offsetTop,t=t.offsetParent}while(t);return e}function b(t){return S(t)+t.clientHeight}function w(t){var e=0;if("top"===t&&U.top>0&&(e+=U.top),"bottom"===t&&U.bottom>0&&(e+=U.bottom),j){var n=j.index(O);"top"===t&&n>0&&j.range(0,n).forEach(function(n){n.isSticky()&&(e+=n.computedHeight(t))}),"bottom"===t&&n!==j.length()-1&&j.range(n+1,j.length()).forEach(function(n){n.isSticky()&&(e+=n.computedHeight(t))})}return e}function x(){return w("top")}function C(){return w("bottom")}function E(t){if(null===F&&(F=z()),F){var e=!(null===t||void 0===t),n=F.getBoundingClientRect(),o=e?S(F)+n.height-t:n.top-window.innerHeight+v();return Math.max(0,o+Q+L-w(R))}return 0}function $(t){if(null===F&&(F=z()),F){var e=!(null===t||void 0===t),n=F.getBoundingClientRect(),o=e?S(F)+n.height-t:n.bottom;return Math.max(0,Q+w(R)+v()+L-o)}return 0}function z(){var t,e=!1;return angular.isDefined(i.container)&&(angular.isString(i.container)?(-1===(t=i.container).indexOf(".")&&-1===t.indexOf("#")&&(t="#"+t),e=angular.element(B.querySelector(t))[0]):e=i.container),e}i=i||{};var I,D,P,H=!1,T=t[0].body,M=o[0],B=t[0].documentElement;angular.isFunction(i.event)||delete i.event,angular.forEach(a,function(t,e){angular.isUndefined(i[e])||""===i[e]?i[e]=t:i[e]&&!isNaN(i[e])&&(i[e]*=1)});var O=i.id,A=i.mediaQuery,N=i.stickyClass,Q=(i.usePlaceholder,i.offsetTop),L=i.offsetBottom,R=i.anchor.toLowerCase().trim(),Y=i.event,j=!1===i.stack?null:i.stack||n({zIndex:i.zIndex}),F=null,U={top:0,bottom:0},q={style:o.attr("style")||""};o.addClass("sticky-"+R);var W={};if(j){var G=j.add(O,W);O=G.id}return W.draw=function(t){var e=(t=t||{}).offset;e&&(U.top=e.top||0,U.bottom=e.bottom||0,U.zIndex=e.zIndex),!0===t.force&&k(),m()},W.anchor=function(){return R},W.isSticky=function(){return u()&&H||i.alwaysSticky},W.isEnabled=u,W.computedHeight=function(t,e){return"top"===t?Math.max(0,v()-$(e)+Q):"bottom"===t?Math.max(0,v()-E(e)+L):0},W.sticksAtPosition=s,W.destroy=function(){k(),j&&j.remove(O)},W}}]).constant("StickyStackDefaults",{id:null,enable:!0,mediaQuery:!1,stickyClass:"is-sticky",beforeStickyClass:"sticky-before",afterStickyClass:"sticky-after",usePlaceholder:!0,offsetTop:0,offsetBottom:0,anchor:"top",container:null,event:angular.noop,stack:null,defaultStack:"default-stack",collection:null,collectionParent:null,zIndex:1039}).provider("hlStickyElementCollection",function(){var t=0,e={collections:{},defaults:{checkDelay:20},elementsDefaults:{},$get:["$rootScope","$window","$document","$log","StickyStackDefaults","hlStickyElement","hlStickyStack","throttle",function(n,o,r,a,i,c,l,u){function s(){t++,h(),t>1||(k=u(h,e.defaults.checkDelay),p.on("resize",k),p.on("scroll",d),g=n.$on("$viewContentLoaded",k),y=n.$on("$includeContentLoaded",k),k())}function f(){--t>0||(p.off("resize",k),p.off("scroll",d),g(),y())}function d(){m()}function h(){m({force:!0})}function m(t){angular.forEach(e.collections,function(e){e.draw(t)})}var g,y,k,p=angular.element(o);return function(t){t&&angular.isObject(t)||(a.warn("Must supply an options object"),t={});var n=(t=angular.extend({},e.elementsDefaults,t)).name||i.defaultStack;if(e.collections[n])return e.collections[n];var o=l({name:n,zIndex:t.zIndex}),r=[],u={};return u.addElement=function(t,e){(e=e||{}).stack=o;var n=c(t,e);return r.push({stickyElement:n,element:t}),n},u.removeElement=function(t){for(var e,n=r.length;n--;)if(angular.isString(t)&&"#"+r[n].element.id===t||r[n].element===t){e=n;break}var o=r.splice(e,1)[0];return o&&o.stickyElement.destroy(),o},u.draw=function(e){var n={};if(t.parent){var o=l({name:t.parent,zIndex:t.zIndex});n.offset={top:o.heightCurrent("top"),zIndex:o.length()}}angular.extend(n,e||{}),angular.forEach(r,function(t){t.stickyElement.draw(n)})},u.destroy=function(){angular.forEach(angular.copy(r),function(t){u.removeElement(t)}),delete e.collections[n],f()},u.trackedElements=function(){return r},e.collections[n]=u,s(),u}}]};return e}).directive("hlSticky",["$log","$window","$document","hlStickyElementCollection",function(t,e,n,o){return{restrict:"A",scope:{container:"@",anchor:"@",stickyClass:"@",mediaQuery:"@",collection:"@",collectionParent:"@",offsetTop:"@",offsetBottom:"@",zIndex:"@",event:"&",usePlaceholder:"=?",enable:"=?",alwaysSticky:"=?",options:"=?"},link:function(t,e,n){e.addClass("hl-sticky");var r;t.options?r=t.options:(r={id:n.hlSticky,event:function(){var e=t.event();angular.isFunction(e)&&e.apply(null,arguments)}},angular.forEach(t,function(e,n){"event"!==n&&"$"!==n[0]&&angular.isDefined(e)&&(r[n]=t[n],r[n]&&!isNaN(r[n])&&(r[n]*=1))}));var a=o({name:r.collection,parent:r.collectionParent,zIndex:r.zIndex});a.addElement(e,r),t.$watch("options",function(t,e){t&&t!==e&&a.draw({force:!0})},!0),t.$watch("enable",function(e,n){e!==n&&(r.enable=t.enable,a.draw({force:!0}))}),t.$watch("alwaysSticky",function(e,n){e!==n&&(r.alwaysSticky=t.alwaysSticky,a.draw({force:!0}))}),t.$on("$destroy",function(){a.removeElement(e),a.trackedElements().length||a.destroy()})}}}]).factory("throttle",["$timeout",function(t){return function(e,n,o){var r=null;return o=o||{},function(){var o=this,a=arguments;t.cancel(r),r=t(function(){e.apply(o,a)},n,!1)}}}]); \ No newline at end of file From c0a75354fc507fc6d2f43ab6748f8adbd1b2c301 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 14 Jan 2020 21:12:06 -0800 Subject: [PATCH 3/7] Attempt to make sticky inside a scrolling div --- demo/less/demo.less | 2 + .../container-scrolling/scaffolding.less | 31 ++++++++++++++++ demo/views/demos/container-scrolling.html | 27 ++++++++++++++ demo/views/header.html | 3 +- js/angular-sticky.js | 37 +++++++++++++++++-- 5 files changed, 96 insertions(+), 4 deletions(-) create mode 100644 demo/less/demos/container-scrolling/scaffolding.less create mode 100644 demo/views/demos/container-scrolling.html diff --git a/demo/less/demo.less b/demo/less/demo.less index 574154c..7da7406 100644 --- a/demo/less/demo.less +++ b/demo/less/demo.less @@ -35,6 +35,8 @@ @import "demos/container-bottom/scaffolding"; +@import "demos/container-scrolling/scaffolding"; + @import "demos/sticky-stack/scaffolding"; @import "demos/sticky-stack-bottom/scaffolding"; diff --git a/demo/less/demos/container-scrolling/scaffolding.less b/demo/less/demos/container-scrolling/scaffolding.less new file mode 100644 index 0000000..26fb308 --- /dev/null +++ b/demo/less/demos/container-scrolling/scaffolding.less @@ -0,0 +1,31 @@ +.demo-container-scrolling { + + #scrolling-container { + height: 500px; + overflow: scroll; + + + .box-shadow(inset 0 0 20px 0px fade(@gray-base, 20%)); + + .hl-sticky { + .sticky-element-default(); + } + + .divider { + .divider-default(300px); + } + .divider-scrolling-before { + .divider-default(200px); + } + .divider-scrolling-after { + .divider-default(800px); + } + + #sticky-container { + background-color: @gray-lighter; + .box-shadow(inset 0 0 20px 0px fade(@gray-base, 20%)); + } + + } +} + diff --git a/demo/views/demos/container-scrolling.html b/demo/views/demos/container-scrolling.html new file mode 100644 index 0000000..09e3c2b --- /dev/null +++ b/demo/views/demos/container-scrolling.html @@ -0,0 +1,27 @@ +
+

Scrolling Container

+

+ Scrolling within a div. + Setting a container is done by adding a scroll-body-container attribute the id of an element. + It will check how much the scroll-body-container has scrolled rather than the page. +

+
+ +
+
+
+
Space before the scrolling container.
+
+
+
+
+ I'll be sticky, but only for a short while. Once the grey box in the background disappears if you scroll too far, the sticky element goes with it. +
+
I'm just here to give the sticky elements some space.
+
+
+
+
Space after the scrolling container.
+
+
+
diff --git a/demo/views/header.html b/demo/views/header.html index 8dee30a..6e435a5 100644 --- a/demo/views/header.html +++ b/demo/views/header.html @@ -28,6 +28,7 @@
  • Events
  • Container
  • Container bottom
  • +
  • Scrolling Container
  • Sticky stack
  • @@ -43,4 +44,4 @@
    - \ No newline at end of file + diff --git a/js/angular-sticky.js b/js/angular-sticky.js index 5eb396d..e5d0811 100755 --- a/js/angular-sticky.js +++ b/js/angular-sticky.js @@ -116,6 +116,14 @@ angular.module('hl.sticky', []) }; for (var i = 0; i < stack.length; i++) { stick = stack[i]; + + if (i === 0) { + //first item in the stack, need to get scroll position for that item to start + + } + + + // check if the sticky element sticks at the queried position minus 1 pixel if the position is at the same place if (stick.sticksAtPosition(anchor, atAdjusted)) { var stickyAnchor = stick.anchor(); @@ -139,7 +147,7 @@ angular.module('hl.sticky', []) return stickyStack; }) - .factory('hlStickyElement', function($document, $log, hlStickyStack, throttle, mediaQuery, StickyStackDefaults) { + .factory('hlStickyElement', function($document, $window, $log, hlStickyStack, throttle, mediaQuery, StickyStackDefaults) { return function(element, options) { options = options || {}; @@ -179,6 +187,7 @@ angular.module('hl.sticky', []) var stack = options.stack === false ? null : options.stack || hlStickyStack({zIndex:options.zIndex}); var container = null; + var scrollBodyContainer = null; var globalOffset = { top: 0, bottom: 0 @@ -231,12 +240,12 @@ angular.module('hl.sticky', []) return false; } function sticksAtPositionTop(scrolledDistance) { - scrolledDistance = scrolledDistance !== undefined ? scrolledDistance : window.pageYOffset || bodyEl.scrollTop; + scrolledDistance = scrolledDistance !== undefined ? scrolledDistance : getPageScrolled(); var scrollTop = scrolledDistance - (documentEl.clientTop || 0); return scrollTop >= stickyLinePositionTop(); } function sticksAtPositionBottom(scrolledDistance) { - scrolledDistance = scrolledDistance !== undefined ? scrolledDistance : (window.pageYOffset || bodyEl.scrollTop); + scrolledDistance = scrolledDistance !== undefined ? scrolledDistance : getPageScrolled(); var scrollBottom = scrolledDistance + window.innerHeight; return scrollBottom <= stickyLinePositionBottom(); } @@ -465,6 +474,27 @@ angular.module('hl.sticky', []) return el; } + function getPageScrolled() { + var selector, el = false, adjustment = 0; + + if (angular.isDefined(options.scrollBodyContainer)) { + if (angular.isFunction(options.scrollBodyContainer)) { + return options.scrollBodyContainer(); + } else if (angular.isString(options.scrollBodyContainer)) { + selector = options.scrollBodyContainer; + if (selector.indexOf(".") === -1 && selector.indexOf("#") === -1) { + selector = "#" + selector; + } + el = angular.element(documentEl.querySelector(selector))[0]; + } else { + el = options.scrollBodyContainer; + } + adjustment = el.scrollTop; + } + + return (window.pageYOffset || documentEl.scrollTop || bodyEl.scrollTop) + adjustment; + } + var $api = {}; if (stack) { @@ -696,6 +726,7 @@ angular.module('hl.sticky', []) restrict: 'A', scope: { container: '@', + scrollBodyContainer: '@', anchor: '@', stickyClass: '@', mediaQuery: '@', From cf636f988345c1c0281a49e8c6ef6b616e492b83 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 15 Jan 2020 02:35:41 -0800 Subject: [PATCH 4/7] Add anchor top sticky for scrolling div container. Fix test bug with multiple elements different options. --- .../container-scrolling/scaffolding.less | 20 ++-- demo/less/mixins/sticky-elements.less | 1 + demo/views/demos/container-scrolling.html | 28 ++++- js/angular-sticky.js | 105 ++++++++++++++---- tests/angular-sticky.spec.js | 11 +- 5 files changed, 124 insertions(+), 41 deletions(-) diff --git a/demo/less/demos/container-scrolling/scaffolding.less b/demo/less/demos/container-scrolling/scaffolding.less index 26fb308..18445e7 100644 --- a/demo/less/demos/container-scrolling/scaffolding.less +++ b/demo/less/demos/container-scrolling/scaffolding.less @@ -1,31 +1,33 @@ .demo-container-scrolling { - #scrolling-container { + .hl-sticky { + .sticky-element-default(); + } + + .scrolling-container { height: 500px; overflow: scroll; .box-shadow(inset 0 0 20px 0px fade(@gray-base, 20%)); - .hl-sticky { - .sticky-element-default(); - } - .divider { .divider-default(300px); } - .divider-scrolling-before { + .divider-scrolling-small { .divider-default(200px); } - .divider-scrolling-after { - .divider-default(800px); + .divider-scrolling-big { + .divider-default(600px); } - #sticky-container { + #sticky-container, #sticky-container2 { + position: relative; background-color: @gray-lighter; .box-shadow(inset 0 0 20px 0px fade(@gray-base, 20%)); } } + } diff --git a/demo/less/mixins/sticky-elements.less b/demo/less/mixins/sticky-elements.less index 15f577d..eeab95e 100644 --- a/demo/less/mixins/sticky-elements.less +++ b/demo/less/mixins/sticky-elements.less @@ -20,4 +20,5 @@ .sticky-element(@minHeight); .sticky-element-shadow(); background-color: @gray-dark; + opacity: 0.8; } diff --git a/demo/views/demos/container-scrolling.html b/demo/views/demos/container-scrolling.html index 09e3c2b..e7fd8c4 100644 --- a/demo/views/demos/container-scrolling.html +++ b/demo/views/demos/container-scrolling.html @@ -2,26 +2,44 @@

    Scrolling Container

    Scrolling within a div. - Setting a container is done by adding a scroll-body-container attribute the id of an element. + Setting a container is done by adding a scroller-container attribute the id of an element. It will check how much the scroll-body-container has scrolled rather than the page.

    +

    Attached to Top

    -
    -
    Space before the scrolling container.
    +
    +
    Space before the scrolling container.
    -
    +
    I'll be sticky, but only for a short while. Once the grey box in the background disappears if you scroll too far, the sticky element goes with it.
    I'm just here to give the sticky elements some space.
    -
    Space after the scrolling container.
    +
    Space after the scrolling container.
    + + + + + + + + + + + + + + + + +
    diff --git a/js/angular-sticky.js b/js/angular-sticky.js index e5d0811..e0caf51 100755 --- a/js/angular-sticky.js +++ b/js/angular-sticky.js @@ -182,12 +182,13 @@ angular.module('hl.sticky', []) var offsetTop = options.offsetTop; var offsetBottom = options.offsetBottom; var anchor = options.anchor.toLowerCase().trim(); + var absoluteOffset = 0; var event = options.event; var stack = options.stack === false ? null : options.stack || hlStickyStack({zIndex:options.zIndex}); var container = null; - var scrollBodyContainer = null; + var scrollerContainer = null; var globalOffset = { top: 0, bottom: 0 @@ -241,7 +242,8 @@ angular.module('hl.sticky', []) } function sticksAtPositionTop(scrolledDistance) { scrolledDistance = scrolledDistance !== undefined ? scrolledDistance : getPageScrolled(); - var scrollTop = scrolledDistance - (documentEl.clientTop || 0); + var scrollTop = scrolledDistance + ( Math.max(getScrollerOffset(), 0) + getScrollerScrolled()); + // absoluteOffset = stickyLinePositionTop() - scrollTop; //possible use for absolute position return scrollTop >= stickyLinePositionTop(); } function sticksAtPositionBottom(scrolledDistance) { @@ -254,6 +256,7 @@ angular.module('hl.sticky', []) } function render() { + var offsetCalc = 0; var shouldStick = sticksAtPosition(anchor); if (angular.isDefined(options.enable) && !options.enable) { @@ -282,11 +285,25 @@ angular.module('hl.sticky', []) if (_isSticking) { // update the top offset at an already sticking element if (anchor === 'top') { - element.css('top', (offsetTop + _stackOffset(anchor) - containerBoundsBottom()) + 'px'); - } + + if (options.useAbsolutePosition) { + stickElementAbsolute(); + } else { + offsetCalc = offsetTop + _stackOffset(anchor) - containerBoundsBottom() + Math.max(getScrollerOffset(), 0); + element.css('top', (offsetCalc) + 'px'); + } + + } else if (anchor === 'bottom') { - element.css('bottom', (offsetBottom + _stackOffset(anchor) - containerBoundsTop()) + 'px'); - } + + if (options.useAbsolutePosition) { + stickElementAbsolute(); + } else { + offsetCalc = offsetBottom + _stackOffset(anchor) - containerBoundsTop(); + element.css('bottom', (offsetCalc) + 'px'); + } + + } element.css('width', elementWidth() + 'px'); } } @@ -325,6 +342,15 @@ angular.module('hl.sticky', []) } + function stickElementAbsolute() { + + // element.css({ + // 'position': 'absolute', + // 'left': 0, + // 'top': getScrollerScrolled() + // }); + } + function stickElement() { _isSticking = true; @@ -451,8 +477,10 @@ angular.module('hl.sticky', []) if (container) { var hasScrollDistance = !(scrolledDistance === null || scrolledDistance === undefined); var containerRect = container.getBoundingClientRect(); - var containerBottom = !hasScrollDistance ? containerRect.bottom : (_getTopOffset(container) + containerRect.height) - scrolledDistance; - return Math.max(0, (offsetTop + _stackOffset(anchor) + elementHeight() + offsetBottom) - containerBottom); + var containerBottom = !hasScrollDistance ? + containerRect.bottom + : (_getTopOffset(container) + containerRect.height) - scrolledDistance; + return Math.max(0, (offsetTop + _stackOffset(anchor) + elementHeight() + offsetBottom) - (containerBottom - Math.max(getScrollerOffset(), 0)) ); } return 0; } @@ -475,25 +503,61 @@ angular.module('hl.sticky', []) } function getPageScrolled() { - var selector, el = false, adjustment = 0; + return window.pageYOffset || documentEl.scrollTop || bodyEl.scrollTop; + } - if (angular.isDefined(options.scrollBodyContainer)) { - if (angular.isFunction(options.scrollBodyContainer)) { - return options.scrollBodyContainer(); - } else if (angular.isString(options.scrollBodyContainer)) { - selector = options.scrollBodyContainer; + function getScrollerContainer() { + var selector, el = false; + + if (angular.isDefined(options.scrollerContainer)) { + if (angular.isFunction(options.scrollerContainer)) { + + } else if (angular.isString(options.scrollerContainer)) { + selector = options.scrollerContainer; if (selector.indexOf(".") === -1 && selector.indexOf("#") === -1) { selector = "#" + selector; } el = angular.element(documentEl.querySelector(selector))[0]; } else { - el = options.scrollBodyContainer; + el = options.scrollerContainer; } - adjustment = el.scrollTop; } + return el; + } - return (window.pageYOffset || documentEl.scrollTop || bodyEl.scrollTop) + adjustment; + function getScrollerScrolled() { + var el, scrolled = 0, pixels = 0; + + // //Gets all additional scrolling from multiple embeded scrolling containers + // el = nativeEl.parentElement; + // if (el) { + // do { + // if (["BODY","HTML"].includes(el.tagName.toUpperCase())) { + // break; + // } + // pixels += el.scrollTop; + // el = el.parentElement; + // } while (el); + // } + // return pixels; + + el = getScrollerContainer(); + if (el) { + scrolled = el.scrollTop; + } + return scrolled; } + + function getScrollerOffset() { + var el, offset = 0; + + el = getScrollerContainer(); + if (el) { + offset = _getTopOffset(el) - getPageScrolled(); + } + + return offset; + } var $api = {}; @@ -592,9 +656,9 @@ angular.module('hl.sticky', []) // bind events throttledResize = throttle(resize, $stickyElement.defaults.checkDelay); windowEl.on('resize', throttledResize); - windowEl.on('scroll', drawEvent); + $window.addEventListener('scroll', drawEvent, true); - unbindViewContentLoaded = $rootScope.$on('$viewContentLoaded', throttledResize); + unbindViewContentLoaded = $rootScope.$on('$viewContentLoaded', throttledResize); unbindIncludeContentLoaded = $rootScope.$on('$includeContentLoaded', throttledResize); throttledResize(); @@ -610,6 +674,7 @@ angular.module('hl.sticky', []) // unbind events windowEl.off('resize', throttledResize); windowEl.off('scroll', drawEvent); + $window.removeEventListener('scroll', drawEvent, true); unbindViewContentLoaded(); unbindIncludeContentLoaded(); } @@ -726,7 +791,7 @@ angular.module('hl.sticky', []) restrict: 'A', scope: { container: '@', - scrollBodyContainer: '@', + scrollerContainer: '@', anchor: '@', stickyClass: '@', mediaQuery: '@', diff --git a/tests/angular-sticky.spec.js b/tests/angular-sticky.spec.js index a9d9ab9..052a2d6 100644 --- a/tests/angular-sticky.spec.js +++ b/tests/angular-sticky.spec.js @@ -719,10 +719,7 @@ describe('angular-sticky', function() { it('should handle multiple sticky elements with different values for the option parameter', function() { var options1 = { enable: true }; var options2 = { enable: true }; - compileSticky(templateMultipleStickyElements, null, [options1, options2]); - - var stickyElement1 = angular.element(element[0].querySelector('#sticky1')); - var sticky1 = hlStickyElement(stickyElement1, options1); + compileSticky(templateMultipleStickyElements, options1); var stickyElement2 = angular.element(element[0].querySelector('#sticky2')); var sticky2 = hlStickyElement(stickyElement2, options2); @@ -737,11 +734,11 @@ describe('angular-sticky', function() { expect(stickyElement2).not.toBeSticky(); options1.enable = false; - drawAt(40, sticky1); - expect(stickyElement1).not.toBeSticky(); + drawAt(40, sticky); + expect(stickyElement).not.toBeSticky(); options2.enable = true; - drawAt(40, sticky2); + drawAt(90, sticky2); expect(stickyElement2).toBeSticky(); }); From b5944bf99480bfa2f088962f1cd05d0c34fb9c81 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 15 Jan 2020 02:35:41 -0800 Subject: [PATCH 5/7] Add anchor bottom sticky for scrolling div container. --- .../container-scrolling/scaffolding.less | 6 +-- demo/views/demos/container-scrolling.html | 34 +++++++-------- js/angular-sticky.js | 41 ++++++++++--------- 3 files changed, 41 insertions(+), 40 deletions(-) diff --git a/demo/less/demos/container-scrolling/scaffolding.less b/demo/less/demos/container-scrolling/scaffolding.less index 18445e7..62818cc 100644 --- a/demo/less/demos/container-scrolling/scaffolding.less +++ b/demo/less/demos/container-scrolling/scaffolding.less @@ -6,13 +6,13 @@ .scrolling-container { height: 500px; - overflow: scroll; + overflow-y: scroll; .box-shadow(inset 0 0 20px 0px fade(@gray-base, 20%)); .divider { - .divider-default(300px); + .divider-default(400px); } .divider-scrolling-small { .divider-default(200px); @@ -21,7 +21,7 @@ .divider-default(600px); } - #sticky-container, #sticky-container2 { + #sticky-container, #sticky-container-bottom { position: relative; background-color: @gray-lighter; .box-shadow(inset 0 0 20px 0px fade(@gray-base, 20%)); diff --git a/demo/views/demos/container-scrolling.html b/demo/views/demos/container-scrolling.html index e7fd8c4..a543d5c 100644 --- a/demo/views/demos/container-scrolling.html +++ b/demo/views/demos/container-scrolling.html @@ -10,7 +10,7 @@

    Scrolling Container

    Attached to Top

    -
    +
    Space before the scrolling container.
    @@ -26,20 +26,20 @@

    Attached to Top

    - - - - - - - - - - - - - - - - +

    Attached to Bottom

    +
    +
    +
    Space before the scrolling container.
    +
    +
    +
    +
    I'm just here to give the sticky elements some space.
    +
    + I'll be sticky, but only for a short while. Once the grey box in the background disappears if you scroll too far, the sticky element goes with it. +
    +
    +
    +
    +
    Space after the scrolling container.
    +
    diff --git a/js/angular-sticky.js b/js/angular-sticky.js index e0caf51..099a74e 100755 --- a/js/angular-sticky.js +++ b/js/angular-sticky.js @@ -248,7 +248,7 @@ angular.module('hl.sticky', []) } function sticksAtPositionBottom(scrolledDistance) { scrolledDistance = scrolledDistance !== undefined ? scrolledDistance : getPageScrolled(); - var scrollBottom = scrolledDistance + window.innerHeight; + var scrollBottom = scrolledDistance + window.innerHeight - Math.max(getScrollerBottomOffset(), 0) + getScrollerScrolled(); return scrollBottom <= stickyLinePositionBottom(); } function matchesMediaQuery() { @@ -299,7 +299,7 @@ angular.module('hl.sticky', []) if (options.useAbsolutePosition) { stickElementAbsolute(); } else { - offsetCalc = offsetBottom + _stackOffset(anchor) - containerBoundsTop(); + offsetCalc = offsetBottom + _stackOffset(anchor) - containerBoundsTop() + Math.max(getScrollerBottomOffset(), 0); element.css('bottom', (offsetCalc) + 'px'); } @@ -465,8 +465,10 @@ angular.module('hl.sticky', []) if (container) { var hasScrollDistance = !(scrolledDistance === null || scrolledDistance === undefined); var containerRect = container.getBoundingClientRect(); - var containerBottom = !hasScrollDistance ? containerRect.top - window.innerHeight + elementHeight() : (_getTopOffset(container) + containerRect.height) - scrolledDistance; - return Math.max(0, (containerBottom + offsetTop + offsetBottom) - (_stackOffset(anchor))); + var containerBottom = !hasScrollDistance + ? containerRect.top - window.innerHeight + elementHeight() + : (_getTopOffset(container) + containerRect.height) - scrolledDistance; + return Math.max(0, (containerBottom + offsetTop + offsetBottom) - (_stackOffset(anchor)) + Math.max(getScrollerBottomOffset(), 0) ); } return 0; } @@ -477,9 +479,9 @@ angular.module('hl.sticky', []) if (container) { var hasScrollDistance = !(scrolledDistance === null || scrolledDistance === undefined); var containerRect = container.getBoundingClientRect(); - var containerBottom = !hasScrollDistance ? - containerRect.bottom - : (_getTopOffset(container) + containerRect.height) - scrolledDistance; + var containerBottom = !hasScrollDistance + ? containerRect.bottom + : (_getTopOffset(container) + containerRect.height) - scrolledDistance; return Math.max(0, (offsetTop + _stackOffset(anchor) + elementHeight() + offsetBottom) - (containerBottom - Math.max(getScrollerOffset(), 0)) ); } return 0; @@ -528,19 +530,6 @@ angular.module('hl.sticky', []) function getScrollerScrolled() { var el, scrolled = 0, pixels = 0; - // //Gets all additional scrolling from multiple embeded scrolling containers - // el = nativeEl.parentElement; - // if (el) { - // do { - // if (["BODY","HTML"].includes(el.tagName.toUpperCase())) { - // break; - // } - // pixels += el.scrollTop; - // el = el.parentElement; - // } while (el); - // } - // return pixels; - el = getScrollerContainer(); if (el) { scrolled = el.scrollTop; @@ -559,6 +548,18 @@ angular.module('hl.sticky', []) return offset; } + function getScrollerBottomOffset() { + var el = false, bottom = 0; + el = getScrollerContainer(); + if (el) { + var bound = el.getBoundingClientRect(); + bottom = getScrollerOffset() + bound.height; + bottom = window.innerHeight - bottom; + } + + return bottom; + } + var $api = {}; if (stack) { From 9682531a6ebdfeb1c64ff64eb9cf8224fc675399 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 15 Jan 2020 05:18:49 -0800 Subject: [PATCH 6/7] Update scrolling container demo --- .../less/demos/sticky-stack-bottom/scaffolding.less | 4 ++++ demo/less/demos/sticky-stack/scaffolding.less | 4 ++++ demo/views/demos/container-scrolling.html | 13 +++++++++++++ demo/views/demos/sticky-stack.html | 6 +++++- 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/demo/less/demos/sticky-stack-bottom/scaffolding.less b/demo/less/demos/sticky-stack-bottom/scaffolding.less index f1ca3f2..3e66cda 100644 --- a/demo/less/demos/sticky-stack-bottom/scaffolding.less +++ b/demo/less/demos/sticky-stack-bottom/scaffolding.less @@ -12,4 +12,8 @@ .divider { .divider-default(); } + + #container-stack-bottom { + .box-shadow(inset 0 0 20px 0px fade(@gray-base, 20%)); + } } diff --git a/demo/less/demos/sticky-stack/scaffolding.less b/demo/less/demos/sticky-stack/scaffolding.less index 0133169..5e49bd6 100644 --- a/demo/less/demos/sticky-stack/scaffolding.less +++ b/demo/less/demos/sticky-stack/scaffolding.less @@ -9,4 +9,8 @@ .divider { .divider-default(); } + + #stack-container { + .box-shadow(inset 0 0 20px 0px fade(@gray-base, 20%)); + } } diff --git a/demo/views/demos/container-scrolling.html b/demo/views/demos/container-scrolling.html index a543d5c..b8fc928 100644 --- a/demo/views/demos/container-scrolling.html +++ b/demo/views/demos/container-scrolling.html @@ -15,9 +15,15 @@

    Attached to Top

    +
    ...
    I'll be sticky, but only for a short while. Once the grey box in the background disappears if you scroll too far, the sticky element goes with it.
    +
    ...
    +
    ...
    +
    + I'll be sticky, but only for a short while. Once the grey box in the background disappears if you scroll too far, the sticky element goes with it. +
    I'm just here to give the sticky elements some space.
    @@ -37,6 +43,13 @@

    Attached to Bottom

    I'll be sticky, but only for a short while. Once the grey box in the background disappears if you scroll too far, the sticky element goes with it.
    +
    ...
    +
    ...
    +
    + I'll be sticky, but only for a short while. Once the grey box in the background disappears if you scroll too far, the sticky element goes with it. +
    +
    ...
    +
    ...
    diff --git a/demo/views/demos/sticky-stack.html b/demo/views/demos/sticky-stack.html index c34d912..85d8a28 100644 --- a/demo/views/demos/sticky-stack.html +++ b/demo/views/demos/sticky-stack.html @@ -46,12 +46,14 @@

    Service: Sticky stack

    options: { anchor: "top", offsetTop: 30, + container: 'stack-container', } }, { description: 'I\'ll append myself to "Element 1"', options: { anchor: "top", + container: 'stack-container', } }, { @@ -59,12 +61,14 @@

    Service: Sticky stack

    options: { anchor: "top", offsetTop: 30, + container: 'stack-container', } }, { description: 'I\'m the last item in the sticky stack', options: { anchor: "top", + container: 'stack-container', } } ]; @@ -73,7 +77,7 @@

    Service: Sticky stack

    -
    +
    Current stack height: {{stackHeightCurrent}}px
    From cb4745ad05a515376be25ee1c7db6a1de0cc4898 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 15 Jan 2020 19:02:49 -0800 Subject: [PATCH 7/7] WIP attempting to get transformY distance for sticky stack for positioning --- demo/views/demos/container-bottom.html | 2 +- demo/views/demos/container-scrolling.html | 61 ++++---- js/angular-sticky.js | 176 ++++++++++++++-------- 3 files changed, 145 insertions(+), 94 deletions(-) diff --git a/demo/views/demos/container-bottom.html b/demo/views/demos/container-bottom.html index 410380c..069bf77 100644 --- a/demo/views/demos/container-bottom.html +++ b/demo/views/demos/container-bottom.html @@ -27,4 +27,4 @@

    Scr

    -
    \ No newline at end of file +
    diff --git a/demo/views/demos/container-scrolling.html b/demo/views/demos/container-scrolling.html index b8fc928..e92e3a2 100644 --- a/demo/views/demos/container-scrolling.html +++ b/demo/views/demos/container-scrolling.html @@ -7,31 +7,31 @@

    Scrolling Container

    -

    Attached to Top

    -
    -
    -
    -
    Space before the scrolling container.
    -
    -
    -
    -
    ...
    -
    - I'll be sticky, but only for a short while. Once the grey box in the background disappears if you scroll too far, the sticky element goes with it. -
    -
    ...
    -
    ...
    -
    - I'll be sticky, but only for a short while. Once the grey box in the background disappears if you scroll too far, the sticky element goes with it. -
    -
    I'm just here to give the sticky elements some space.
    -
    -
    -
    -
    Space after the scrolling container.
    -
    -
    -
    + + + + + + + + + + + + + + + + + + + + + + + + +

    Attached to Bottom

    @@ -40,16 +40,21 @@

    Attached to Bottom

    I'm just here to give the sticky elements some space.
    -
    - I'll be sticky, but only for a short while. Once the grey box in the background disappears if you scroll too far, the sticky element goes with it. +
    + First element.
    ...
    ...
    -
    +
    I'll be sticky, but only for a short while. Once the grey box in the background disappears if you scroll too far, the sticky element goes with it.
    ...
    ...
    +
    + Three +
    +
    ...
    +
    ...
    diff --git a/js/angular-sticky.js b/js/angular-sticky.js index 099a74e..8b3b0f2 100755 --- a/js/angular-sticky.js +++ b/js/angular-sticky.js @@ -243,12 +243,13 @@ angular.module('hl.sticky', []) function sticksAtPositionTop(scrolledDistance) { scrolledDistance = scrolledDistance !== undefined ? scrolledDistance : getPageScrolled(); var scrollTop = scrolledDistance + ( Math.max(getScrollerOffset(), 0) + getScrollerScrolled()); - // absoluteOffset = stickyLinePositionTop() - scrollTop; //possible use for absolute position + absoluteOffset = scrollTop - stickyLinePositionTop(); return scrollTop >= stickyLinePositionTop(); } function sticksAtPositionBottom(scrolledDistance) { scrolledDistance = scrolledDistance !== undefined ? scrolledDistance : getPageScrolled(); var scrollBottom = scrolledDistance + window.innerHeight - Math.max(getScrollerBottomOffset(), 0) + getScrollerScrolled(); + absoluteOffset = scrollBottom - stickyLinePositionBottom(); return scrollBottom <= stickyLinePositionBottom(); } function matchesMediaQuery() { @@ -287,23 +288,59 @@ angular.module('hl.sticky', []) if (anchor === 'top') { if (options.useAbsolutePosition) { - stickElementAbsolute(); + if (containerBoundsBottom() <= 0) { + offsetCalc = absoluteOffset; + element.css({ + 'transform': "translateY(" + offsetCalc + "px)" + }); + } } else { - offsetCalc = offsetTop + _stackOffset(anchor) - containerBoundsBottom() + Math.max(getScrollerOffset(), 0); - element.css('top', (offsetCalc) + 'px'); + offsetCalc = offsetTop + _stackOffset(anchor) - containerBoundsBottom() + Math.max(getScrollerOffset(), 0); + element.css('top', (offsetCalc) + 'px'); } - } + } else if (anchor === 'bottom') { + console.log('el',options.offsetBottom); + + var offsetCalcA = offsetBottom + _stackOffset(anchor) - containerBoundsTop() + Math.max(getScrollerBottomOffset(), 0); + console.log('stock:', offsetCalcA); + + var offsetCalcB = absoluteOffset; + console.log('cust:', offsetCalcB); + + console.log('stackOffsetT', _stackOffset('top')); + console.log('stackOffsetB', _stackOffset('bottom')); + console.log('------'); + + + if (options.useAbsolutePosition) { - stickElementAbsolute(); + if (containerBoundsTop() <= 0) { + element.css({ + "transform": "" + }); + var elRect = nativeEl.getBoundingClientRect(); + var containerRect = container.getBoundingClientRect(); + var space = containerRect.top - elRect.top + offsetBottom + _stackOffset(anchor) + Math.max(getScrollerBottomOffset(), 0) + getScrollerScrolled(); + console.log('space', space); + + offsetCalc = space; + // scrolledDistance = scrolledDistance !== undefined ? scrolledDistance : getPageScrolled(); + // var scrollBottom = scrolledDistance + window.innerHeight - Math.max(getScrollerBottomOffset(), 0) + getScrollerScrolled(); + + // offsetCalc = absoluteOffset; + element.css({ + 'transform': "translateY(" + offsetCalc + "px)" + }); + } } else { - offsetCalc = offsetBottom + _stackOffset(anchor) - containerBoundsTop() + Math.max(getScrollerBottomOffset(), 0); + offsetCalc = offsetBottom + _stackOffset(anchor) - containerBoundsTop() + Math.max(getScrollerBottomOffset(), 0); element.css('bottom', (offsetCalc) + 'px'); } - } + } element.css('width', elementWidth() + 'px'); } } @@ -344,34 +381,41 @@ angular.module('hl.sticky', []) function stickElementAbsolute() { - // element.css({ - // 'position': 'absolute', - // 'left': 0, - // 'top': getScrollerScrolled() - // }); + // element.css({ + // 'position': 'absolute', + // 'left': 0, + // 'top': getScrollerScrolled() + // }); } function stickElement() { _isSticking = true; - + var css = {}; element.addClass(stickyClass); - // create placeholder to avoid jump - if (options.usePlaceholder) { - placeholder = placeholder || angular.element('
    '); - placeholder.css('height', elementHeight() + 'px'); - element.after(placeholder); - } + if (options.useAbsolutePosition) { + css = { + 'z-index': stack ? stack.get(id).zIndex - (globalOffset.zIndex || 0) : null, + 'transform': "translateY(" + absoluteOffset + "px)" + } + } else { + // create placeholder to avoid jump + if (options.usePlaceholder) { + placeholder = placeholder || angular.element('
    '); + placeholder.css('height', elementHeight() + 'px'); + element.after(placeholder); + } - var rect = nativeEl.getBoundingClientRect(); - var css = { - 'width': elementWidth() + 'px', - 'position': 'fixed', - 'left': rect.left + 'px', - 'z-index': stack ? stack.get(id).zIndex - (globalOffset.zIndex || 0) : null - }; + var rect = nativeEl.getBoundingClientRect(); + css = { + 'width': elementWidth() + 'px', + 'position': 'fixed', + 'left': rect.left + 'px', + 'z-index': stack ? stack.get(id).zIndex - (globalOffset.zIndex || 0) : null, + }; - css['margin-' + anchor] = 0; + css['margin-' + anchor] = 0; + } element.css(css); } function unstickElement() { @@ -457,7 +501,7 @@ angular.module('hl.sticky', []) return 0; } - // @todo dffgdg + // returns a positive pixel count if it's before the item function containerBoundsTop(scrolledDistance) { if (container === null) { container = getContainer(); @@ -468,7 +512,8 @@ angular.module('hl.sticky', []) var containerBottom = !hasScrollDistance ? containerRect.top - window.innerHeight + elementHeight() : (_getTopOffset(container) + containerRect.height) - scrolledDistance; - return Math.max(0, (containerBottom + offsetTop + offsetBottom) - (_stackOffset(anchor)) + Math.max(getScrollerBottomOffset(), 0) ); + // return Math.max(0, containerBottom - (offsetTop + _stackOffset(anchor))); + return Math.max(0, (containerBottom + offsetTop + _stackOffset(anchor)) - Math.max(getScrollerBottomOffset(), 0) ); } return 0; } @@ -505,45 +550,45 @@ angular.module('hl.sticky', []) } function getPageScrolled() { - return window.pageYOffset || documentEl.scrollTop || bodyEl.scrollTop; - } - - function getScrollerContainer() { - var selector, el = false; - - if (angular.isDefined(options.scrollerContainer)) { - if (angular.isFunction(options.scrollerContainer)) { - - } else if (angular.isString(options.scrollerContainer)) { - selector = options.scrollerContainer; - if (selector.indexOf(".") === -1 && selector.indexOf("#") === -1) { - selector = "#" + selector; - } - el = angular.element(documentEl.querySelector(selector))[0]; - } else { - el = options.scrollerContainer; - } - } - return el; - } + return window.pageYOffset || documentEl.scrollTop || bodyEl.scrollTop; + } - function getScrollerScrolled() { - var el, scrolled = 0, pixels = 0; + function getScrollerContainer() { + var selector, el = false; + + if (angular.isDefined(options.scrollerContainer)) { + if (angular.isFunction(options.scrollerContainer)) { + + } else if (angular.isString(options.scrollerContainer)) { + selector = options.scrollerContainer; + if (selector.indexOf(".") === -1 && selector.indexOf("#") === -1) { + selector = "#" + selector; + } + el = angular.element(documentEl.querySelector(selector))[0]; + } else { + el = options.scrollerContainer; + } + } + return el; + } + + function getScrollerScrolled() { + var el, scrolled = 0, pixels = 0; el = getScrollerContainer(); if (el) { - scrolled = el.scrollTop; - } - return scrolled; - } - - function getScrollerOffset() { + scrolled = el.scrollTop; + } + return scrolled; + } + + function getScrollerOffset() { var el, offset = 0; el = getScrollerContainer(); if (el) { - offset = _getTopOffset(el) - getPageScrolled(); - } + offset = _getTopOffset(el) - getPageScrolled(); + } return offset; } @@ -657,9 +702,9 @@ angular.module('hl.sticky', []) // bind events throttledResize = throttle(resize, $stickyElement.defaults.checkDelay); windowEl.on('resize', throttledResize); - $window.addEventListener('scroll', drawEvent, true); + $window.addEventListener('scroll', drawEvent, true); - unbindViewContentLoaded = $rootScope.$on('$viewContentLoaded', throttledResize); + unbindViewContentLoaded = $rootScope.$on('$viewContentLoaded', throttledResize); unbindIncludeContentLoaded = $rootScope.$on('$includeContentLoaded', throttledResize); throttledResize(); @@ -675,7 +720,7 @@ angular.module('hl.sticky', []) // unbind events windowEl.off('resize', throttledResize); windowEl.off('scroll', drawEvent); - $window.removeEventListener('scroll', drawEvent, true); + $window.removeEventListener('scroll', drawEvent, true); unbindViewContentLoaded(); unbindIncludeContentLoaded(); } @@ -792,7 +837,7 @@ angular.module('hl.sticky', []) restrict: 'A', scope: { container: '@', - scrollerContainer: '@', + scrollerContainer: '@', anchor: '@', stickyClass: '@', mediaQuery: '@', @@ -803,6 +848,7 @@ angular.module('hl.sticky', []) zIndex: '@', event: '&', usePlaceholder: '=?', + useAbsolutePosition: '=?', enable: '=?', alwaysSticky: '=?', options: '=?'