diff --git a/README.md b/README.md index 7ec947d..a117c62 100644 --- a/README.md +++ b/README.md @@ -111,18 +111,18 @@ The class *affixed* will be added to the class once it reaches the bottom edge o Possible values for *affix* are *top*, *bottom*, *left* and *right*. -#### More fluent passage by *cloning* +#### More fluent passage by adding a *placeholder* In default mode the element which should be affixed is just set to *position fixed*. As this will pull the element out of its normal flow, the elements below will be moved up with a jolt. -If you add `{clone: true}` to the affix options the original element will be cloned, -the clone gets inserted after its origin and set to *position fixed*, so that you +If you add `{placeholder: true}` to the affix options the original element will be cloned +and the clone gets inserted after its origin to serve as a placeholder, so that you won't see any flickering effects. -
Affixed!
+
Affixed!
-The default of the option `clone` is `false`. +The default of the option `placeholder` is `false`. ### The pagemenu and pageitems directives The module provides two directives, *pageitems* attribute and ** tag. The *pageitems* tag will parse the included HTML and query for items with a certain class. The menu will then be generated in the DOM where you put the ** tag from that list of items. diff --git a/dist/ngScrollSpy.debug.js b/dist/ngScrollSpy.debug.js index ecc7b71..8eacde3 100755 --- a/dist/ngScrollSpy.debug.js +++ b/dist/ngScrollSpy.debug.js @@ -199,25 +199,25 @@ mod.service('ScrollSpy', function($window) { }); mod.directive('affix', function(ScrollSpy) { var affixCloneFn= function(elem) { - if (!elem.data('$ngScrollSpy.clone')) { - var clone = elem.clone(); - elem.data('$ngScrollSpy.clone', clone); + if (!elem.data('$ngScrollSpy.placeholder')) { + var placeholder = elem.clone(); + elem.data('$ngScrollSpy.placeholder', placeholder); } - return elem.data('$ngScrollSpy.clone'); + return elem.data('$ngScrollSpy.placeholder'); }; var affixFn= function(shouldAffixFn, wasAffixed, affixClass, affixOptions, elem) { var shouldAffix= shouldAffixFn(elem[0].getBoundingClientRect()); if(shouldAffix !== wasAffixed) { if(shouldAffix) { - if(affixOptions.clone) { - // insert cloned element into DOM, so that it replaces the element, - // that will be pulled out of the flow. + if(affixOptions.placeholder) { + // insert cloned element into DOM to serve as a placeholder, + // because the original element (elem) will be pulled out of the flow by getting affixed elem.after(affixCloneFn(elem)); } elem.addClass(affixClass); } else { - if(affixOptions.clone) { + if(affixOptions.placeholder) { // remove clone from DOM again affixCloneFn(elem).detach(); } @@ -233,7 +233,7 @@ mod.directive('affix', function(ScrollSpy) { affixedPos, trigger= false; - affixOptions = angular.extend({offset: 0, clone: false}, affixOptions); + affixOptions = angular.extend({offset: 0, placeholder: false}, affixOptions); if(affixTo === 'top') { scrollHandler= ScrollSpy.onYScroll(function(pos) { diff --git a/dist/ngScrollSpy.js b/dist/ngScrollSpy.js index acfe715..0641e19 100755 --- a/dist/ngScrollSpy.js +++ b/dist/ngScrollSpy.js @@ -199,25 +199,25 @@ mod.service('ScrollSpy', function($window) { }); mod.directive('affix', function(ScrollSpy) { var affixCloneFn= function(elem) { - if (!elem.data('$ngScrollSpy.clone')) { - var clone = elem.clone(); - elem.data('$ngScrollSpy.clone', clone); + if (!elem.data('$ngScrollSpy.placeholder')) { + var placeholder = elem.clone(); + elem.data('$ngScrollSpy.placeholder', placeholder); } - return elem.data('$ngScrollSpy.clone'); + return elem.data('$ngScrollSpy.placeholder'); }; var affixFn= function(shouldAffixFn, wasAffixed, affixClass, affixOptions, elem) { var shouldAffix= shouldAffixFn(elem[0].getBoundingClientRect()); if(shouldAffix !== wasAffixed) { if(shouldAffix) { - if(affixOptions.clone) { - // insert cloned element into DOM, so that it replaces the element, - // that will be pulled out of the flow. + if(affixOptions.placeholder) { + // insert cloned element into DOM to serve as a placeholder, + // because the original element (elem) will be pulled out of the flow by getting affixed elem.after(affixCloneFn(elem)); } elem.addClass(affixClass); } else { - if(affixOptions.clone) { + if(affixOptions.placeholder) { // remove clone from DOM again affixCloneFn(elem).detach(); } @@ -233,7 +233,7 @@ mod.directive('affix', function(ScrollSpy) { affixedPos, trigger= false; - affixOptions = angular.extend({offset: 0, clone: false}, affixOptions); + affixOptions = angular.extend({offset: 0, placeholder: false}, affixOptions); if(affixTo === 'top') { scrollHandler= ScrollSpy.onYScroll(function(pos) { diff --git a/dist/ngScrollSpy.min.js b/dist/ngScrollSpy.min.js index 728b054..b3eed24 100755 --- a/dist/ngScrollSpy.min.js +++ b/dist/ngScrollSpy.min.js @@ -2,4 +2,4 @@ Copyright (c) 2014 Magnús Örn Gylfason Licence: MIT */ -!function(t){"use strict";function n(){return o}var e=t.module("ngScrollSpy",[]);e.service("ScrollSpy",["$window",function(n){var e,o=function(t){var n=(t.innerWidth||document.documentElement.clientWidth||document.body.clientWidth,t.innerHeight||document.documentElement.clientHeight||document.body.clientHeight,void 0!==t.pageXOffset),e="CSS1Compat"===(document.compatMode||""),o=(n?t.pageXOffset:e?document.documentElement.scrollLeft:document.body.scrollLeft,n?t.pageYOffset:e?document.documentElement.scrollTop:document.body.scrollTop,{width:t.innerWidth,height:t.innerHeight,maxWidth:t.document.body.scrollWidth,maxHeight:t.document.body.scrollHeight,posX:t.scrollX||t.pageXOffset||t.document.documentElement.scrollLeft,posY:t.scrollY||t.pageYOffset||t.document.documentElement.scrollTop});return o.posX<0?(o.posX=0,o.overscrollLeft=!0):o.posX+o.width>o.maxWidth&&(o.posX=o.maxWidth-o.width,o.overscrollRight=!0),o.posY<0?(o.posY=0,o.overscrollTop=!0):o.posY+o.height>o.maxHeight&&(o.posY=o.maxHeight-o.height,o.overscrollBottom=!0),o.hasOverscroll=o.overscrollTop||o.overscrollBottom||o.overscrollLeft||o.overscrollRight,o},i=function(n,e){if(!n||!e)return t.extend({isEqual:!1,velocityX:0,velocityY:0},e);var o={posX:e.posX-n.posX,posY:e.posY-n.posY,width:e.width-n.width,height:e.height-n.height,maxWidth:e.maxWidth-n.maxWidth,maxHeight:e.maxHeight-n.maxHeight};return e.width>0&&(o.velocityX=o.posX/e.width),e.height>0&&(o.velocityY=o.posY/e.height),o.isEqual=!(0!==o.posX||0!==o.posY||0!==o.width||0!==o.height||0!==o.maxWidth||0!==o.maxHeight),o},r={},l=function(t){var l=o(n),s=i(e,l);if(!s.isEqual||l.hasOverscroll||t){for(var c in r){var u=r[c].cond;(u(l,s)||t)&&r[c].handler(l,s)}e=l}};t.element(n).on("scroll",l);var s=this,c=0;this.trigger=function(){this.isForced=!0,l(!0),this.isForced=!1},this.addHandler=function(t,n){return r[c]={cond:t,handler:n},c++,c-1},this.removeHandler=function(t){delete r[t]},this.onScroll=function(t){return s.addHandler(function(){return!0},function(n,e){t(n,e)})},this.onXScroll=function(t){return s.addHandler(function(t,n){return 0!==n.posX},function(n,e){t(n.posX,e.posX,n,e)})},this.onYScroll=function(t){return s.addHandler(function(t,n){return 0!==n.posY},function(n,e){t(n.posY,e.posY,n,e)})},this.onOverscrollHorz=function(t){return s.addHandler(function(t,n){return t.overscrollLeft||t.overscrollRight},t)},this.onOverscrollLeft=function(t){return s.addHandler(function(t,n){return t.overscrollLeft},t)},this.onOverscrollRight=function(t){return s.addHandler(function(t,n){return t.overscrollRight},t)},this.onOverscrollVert=function(t){return s.addHandler(function(t,n){return t.overscrollTop||t.overscrollBottom},t)},this.onOverscrollTop=function(t){return s.addHandler(function(t,n){return t.overscrollTop},t)},this.onOverscrollBottom=function(t){return s.addHandler(function(t,n){return t.overscrollBottom},t)}}]),e.directive("affix",["ScrollSpy",function(n){var e,o=function(t){if(!t.data("$ngScrollSpy.clone")){var n=t.clone();t.data("$ngScrollSpy.clone",n)}return t.data("$ngScrollSpy.clone")},i=function(t,n,e,i,r){var l=t(r[0].getBoundingClientRect());l!==n&&(l?(i.clone&&r.after(o(r)),r.addClass(e)):(i.clone&&o(r).detach(),r.removeClass(e)))},r=function(o,r,l,s){var c,u=!1,a=!1,f=!1;l=t.extend({offset:0,clone:!1},l),"top"===o?e=n.onYScroll(function(t){a=u,i(function(n){return u?u=c<=t+l.offset:n.top<=l.offset?(c=n.top=t:e.bottom>=o.height?(c=n.isForced&&0===t?t+e.bottom-o.height-e.height:t+e.bottom-o.height,u=!0):!1},a,r,l,s)})):"left"===o?e=n.onXScroll(function(t){a=u,i(function(n){return u?u=t>=c:n.left<=0?(c=n.left<0?t+n.left:t,u=!0):!1},a,r,l,s)}):"right"===o&&(f=!0,e=n.onXScroll(function(t,e,o){a=u,i(function(e){return u?u=c>=t:e.right>=o.width?(c=n.isForced&&0===t?t+e.right-o.width-e.width:t+e.right-o.width,u=!0):!1},a,r,l,s)})),f&&n.trigger()};return{restrict:"A",scope:{affix:"@",affixClass:"@",affixOptions:"@"},link:function(t,o,i,l){t.affix=t.affix||"top",t.affixClass=t.affixClass||"affix",t.affixOptions=t.affixOptions?t.$eval(t.affixOptions):{},r(t.affix,t.affixClass,t.affixOptions,o),t.$on("destroy",function(){n.removeHandler(e)})}}}]);var o={onRun:null,state:null,store:function(t){for(var n in t)this[n]=t[n];this.state=!0,this.run()},builder:null,setBuilder:function(t){this.builder=t,this.run()},run:function(){this.builder&&this.state&&(this.builder(),this.builder=null,this.state=null,this.onRun&&(this.onRun(),this.onRun=null))}};e.directive("pageitems",["ScrollSpy",function(e){var o=function(o,i,r){if(t.isDefined(o.selector)){o.spyElems=i[0].getElementsByClassName(o.selector),o.spies={},n().onRun=function(){o.spies[o.spyElems[0].id].set()},n().store({topMargin:function(){return 0|o.topmargin},addSpy:function(t){o.spies[t.id]=t},getSpy:function(t){return o.spies[t]},items:function(){return o.spyElems}});var l=o.spyElems,s=0|o.topmargin,c=e.onYScroll(function(t,n,i){for(var r=null,u=o.spies,a=0;a=h?(d.pos=h,null===r&&(r=d),r.pos=i.maxHeight&&(r=u[l[l.length-1].id]),r.set(),o.$on("destroy",function(){e.removeHandler(c)})})}};return{restrict:"A",scope:{selector:"@",topmargin:"@"},link:o}}]),e.directive("pagemenu",["$compile","$location","$anchorScroll",function(t,e,o){var i=function(i,r){for(var l,s=[],c=[],u=function(t){for(var n={link:t.id,text:t.textContent||t.innerText,parent:""},e=t.tagName,o=0;o=0&&e!=s[r];r--);if(0>r)s.push(e),n.push=!0,c.push(l);else for(n.pop=i-1-r;s.length>r+1;)s.pop(),c.pop()}return c.length>0&&(n.parent=c[c.length-1]),l=n.link,n},a=n().items(),f="",d=0;d';else if(h.pop)for(var p=0;p";else 0!==d&&(f+="");f+='
  • ',f+='',f+=h.text,f+=""}f+="
  • ",r.append(t(f)(i)),r.on("click",function(t){var i=t.target.hash.substring(1);e.hash(i),o(),0!==n().topMargin()&&setTimeout(function(){window.scrollTo(window.pageXOffset,window.pageYOffset-n().topMargin())},0)})};return{restrict:"E",replace:!0,template:'',link:function(t,e){n().setBuilder(function(){i(t,e)})}}}]),e.directive("pagemenuspy",["$location","$anchorScroll",function(t,e){return{restrict:"A",link:function(t,e,o){n().addSpy({id:o.pagemenuspy,parent:o.parent,set:function(){e.addClass("active");var t=n().getSpy(this.parent);t&&t.set()},clear:function(){e.removeClass("active")}})}}}])}(angular); \ No newline at end of file +!function(t){"use strict";function e(){return o}var n=t.module("ngScrollSpy",[]);n.service("ScrollSpy",["$window",function(e){var n,o=function(t){var e=(t.innerWidth||document.documentElement.clientWidth||document.body.clientWidth,t.innerHeight||document.documentElement.clientHeight||document.body.clientHeight,void 0!==t.pageXOffset),n="CSS1Compat"===(document.compatMode||""),o=(e?t.pageXOffset:n?document.documentElement.scrollLeft:document.body.scrollLeft,e?t.pageYOffset:n?document.documentElement.scrollTop:document.body.scrollTop,{width:t.innerWidth,height:t.innerHeight,maxWidth:t.document.body.scrollWidth,maxHeight:t.document.body.scrollHeight,posX:t.scrollX||t.pageXOffset||t.document.documentElement.scrollLeft,posY:t.scrollY||t.pageYOffset||t.document.documentElement.scrollTop});return o.posX<0?(o.posX=0,o.overscrollLeft=!0):o.posX+o.width>o.maxWidth&&(o.posX=o.maxWidth-o.width,o.overscrollRight=!0),o.posY<0?(o.posY=0,o.overscrollTop=!0):o.posY+o.height>o.maxHeight&&(o.posY=o.maxHeight-o.height,o.overscrollBottom=!0),o.hasOverscroll=o.overscrollTop||o.overscrollBottom||o.overscrollLeft||o.overscrollRight,o},i=function(e,n){if(!e||!n)return t.extend({isEqual:!1,velocityX:0,velocityY:0},n);var o={posX:n.posX-e.posX,posY:n.posY-e.posY,width:n.width-e.width,height:n.height-e.height,maxWidth:n.maxWidth-e.maxWidth,maxHeight:n.maxHeight-e.maxHeight};return n.width>0&&(o.velocityX=o.posX/n.width),n.height>0&&(o.velocityY=o.posY/n.height),o.isEqual=!(0!==o.posX||0!==o.posY||0!==o.width||0!==o.height||0!==o.maxWidth||0!==o.maxHeight),o},r={},l=function(t){var l=o(e),s=i(n,l);if(!s.isEqual||l.hasOverscroll||t){for(var c in r){var a=r[c].cond;(a(l,s)||t)&&r[c].handler(l,s)}n=l}};t.element(e).on("scroll",l);var s=this,c=0;this.trigger=function(){this.isForced=!0,l(!0),this.isForced=!1},this.addHandler=function(t,e){return r[c]={cond:t,handler:e},c++,c-1},this.removeHandler=function(t){delete r[t]},this.onScroll=function(t){return s.addHandler(function(){return!0},function(e,n){t(e,n)})},this.onXScroll=function(t){return s.addHandler(function(t,e){return 0!==e.posX},function(e,n){t(e.posX,n.posX,e,n)})},this.onYScroll=function(t){return s.addHandler(function(t,e){return 0!==e.posY},function(e,n){t(e.posY,n.posY,e,n)})},this.onOverscrollHorz=function(t){return s.addHandler(function(t,e){return t.overscrollLeft||t.overscrollRight},t)},this.onOverscrollLeft=function(t){return s.addHandler(function(t,e){return t.overscrollLeft},t)},this.onOverscrollRight=function(t){return s.addHandler(function(t,e){return t.overscrollRight},t)},this.onOverscrollVert=function(t){return s.addHandler(function(t,e){return t.overscrollTop||t.overscrollBottom},t)},this.onOverscrollTop=function(t){return s.addHandler(function(t,e){return t.overscrollTop},t)},this.onOverscrollBottom=function(t){return s.addHandler(function(t,e){return t.overscrollBottom},t)}}]),n.directive("affix",["ScrollSpy",function(e){var n,o=function(t){if(!t.data("$ngScrollSpy.placeholder")){var e=t.clone();t.data("$ngScrollSpy.placeholder",e)}return t.data("$ngScrollSpy.placeholder")},i=function(t,e,n,i,r){var l=t(r[0].getBoundingClientRect());l!==e&&(l?(i.placeholder&&r.after(o(r)),r.addClass(n)):(i.placeholder&&o(r).detach(),r.removeClass(n)))},r=function(o,r,l,s){var c,a=!1,u=!1,f=!1;l=t.extend({offset:0,placeholder:!1},l),"top"===o?n=e.onYScroll(function(t){u=a,i(function(e){return a?a=c<=t+l.offset:e.top<=l.offset?(c=e.top=t:n.bottom>=o.height?(c=e.isForced&&0===t?t+n.bottom-o.height-n.height:t+n.bottom-o.height,a=!0):!1},u,r,l,s)})):"left"===o?n=e.onXScroll(function(t){u=a,i(function(e){return a?a=t>=c:e.left<=0?(c=e.left<0?t+e.left:t,a=!0):!1},u,r,l,s)}):"right"===o&&(f=!0,n=e.onXScroll(function(t,n,o){u=a,i(function(n){return a?a=c>=t:n.right>=o.width?(c=e.isForced&&0===t?t+n.right-o.width-n.width:t+n.right-o.width,a=!0):!1},u,r,l,s)})),f&&e.trigger()};return{restrict:"A",scope:{affix:"@",affixClass:"@",affixOptions:"@"},link:function(t,o,i,l){t.affix=t.affix||"top",t.affixClass=t.affixClass||"affix",t.affixOptions=t.affixOptions?t.$eval(t.affixOptions):{},r(t.affix,t.affixClass,t.affixOptions,o),t.$on("destroy",function(){e.removeHandler(n)})}}}]);var o={onRun:null,state:null,store:function(t){for(var e in t)this[e]=t[e];this.state=!0,this.run()},builder:null,setBuilder:function(t){this.builder=t,this.run()},run:function(){this.builder&&this.state&&(this.builder(),this.builder=null,this.state=null,this.onRun&&(this.onRun(),this.onRun=null))}};n.directive("pageitems",["ScrollSpy",function(n){var o=function(o,i,r){if(t.isDefined(o.selector)){o.spyElems=i[0].getElementsByClassName(o.selector),o.spies={},e().onRun=function(){o.spies[o.spyElems[0].id].set()},e().store({topMargin:function(){return 0|o.topmargin},addSpy:function(t){o.spies[t.id]=t},getSpy:function(t){return o.spies[t]},items:function(){return o.spyElems}});var l=o.spyElems,s=0|o.topmargin,c=n.onYScroll(function(t,e,i){for(var r=null,a=o.spies,u=0;u=h?(d.pos=h,null===r&&(r=d),r.pos=i.maxHeight&&(r=a[l[l.length-1].id]),r.set(),o.$on("destroy",function(){n.removeHandler(c)})})}};return{restrict:"A",scope:{selector:"@",topmargin:"@"},link:o}}]),n.directive("pagemenu",["$compile","$location","$anchorScroll",function(t,n,o){var i=function(i,r){for(var l,s=[],c=[],a=function(t){for(var e={link:t.id,text:t.textContent||t.innerText,parent:""},n=t.tagName,o=0;o=0&&n!=s[r];r--);if(0>r)s.push(n),e.push=!0,c.push(l);else for(e.pop=i-1-r;s.length>r+1;)s.pop(),c.pop()}return c.length>0&&(e.parent=c[c.length-1]),l=e.link,e},u=e().items(),f="",d=0;d';else if(h.pop)for(var p=0;p";else 0!==d&&(f+="");f+='
  • ',f+='',f+=h.text,f+=""}f+="
  • ",r.append(t(f)(i)),r.on("click",function(t){var i=t.target.hash.substring(1);n.hash(i),o(),0!==e().topMargin()&&setTimeout(function(){window.scrollTo(window.pageXOffset,window.pageYOffset-e().topMargin())},0)})};return{restrict:"E",replace:!0,template:'',link:function(t,n){e().setBuilder(function(){i(t,n)})}}}]),n.directive("pagemenuspy",["$location","$anchorScroll",function(t,n){return{restrict:"A",link:function(t,n,o){e().addSpy({id:o.pagemenuspy,parent:o.parent,set:function(){n.addClass("active");var t=e().getSpy(this.parent);t&&t.set()},clear:function(){n.removeClass("active")}})}}}])}(angular); \ No newline at end of file diff --git a/src/affix.js b/src/affix.js index 9a1db15..ad8bf37 100644 --- a/src/affix.js +++ b/src/affix.js @@ -1,24 +1,24 @@ mod.directive('affix', function(ScrollSpy) { var affixCloneFn= function(elem) { - if (!elem.data('$ngScrollSpy.clone')) { - var clone = elem.clone(); - elem.data('$ngScrollSpy.clone', clone); + if (!elem.data('$ngScrollSpy.placeholder')) { + var placeholder = elem.clone(); + elem.data('$ngScrollSpy.placeholder', placeholder); } - return elem.data('$ngScrollSpy.clone'); + return elem.data('$ngScrollSpy.placeholder'); }; var affixFn= function(shouldAffixFn, wasAffixed, affixClass, affixOptions, elem) { var shouldAffix= shouldAffixFn(elem[0].getBoundingClientRect()); if(shouldAffix !== wasAffixed) { if(shouldAffix) { - if(affixOptions.clone) { + if(affixOptions.placeholder) { // insert cloned element into DOM to serve as a placeholder, // because the original element (elem) will be pulled out of the flow by getting affixed elem.after(affixCloneFn(elem)); } elem.addClass(affixClass); } else { - if(affixOptions.clone) { + if(affixOptions.placeholder) { // remove clone from DOM again affixCloneFn(elem).detach(); } @@ -34,7 +34,7 @@ mod.directive('affix', function(ScrollSpy) { affixedPos, trigger= false; - affixOptions = angular.extend({offset: 0, clone: false}, affixOptions); + affixOptions = angular.extend({offset: 0, placeholder: false}, affixOptions); if(affixTo === 'top') { scrollHandler= ScrollSpy.onYScroll(function(pos) {