diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c0875c..5df32f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,17 @@ # CHANGELOG jquery.simple-scroll-follow + +## v3.1.0 +#### Add option `upper_side` and `lower_side` +If you use fixed menu such as [Bootstrap .navbar-fixed-top](http://getbootstrap.com/components/#navbar-fixed-top), the options below will help you. + +```javascript +$('#foo').simpleScrollFollow({ + upper_side: '#menu-fixed-top', + lower_side: '#menu-fixed-bottom' +}); +``` + +- - - ## v3.0.0 #### Option "instance" is deleted. In v3.x, simpleScrollFollow() always returns jQuery object, so jQuery method chaining always works. diff --git a/README.md b/README.md index 95e683a..3c15547 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,8 @@ $('#foo').simpleScrollFollow(); |[limit_elem](http://sutara79.github.io/jquery.simple-scroll-follow/#limit_elem)|Object, string|`$('body')`|Lower limit of target element.| |[min_width](http://sutara79.github.io/jquery.simple-scroll-follow/#min_width)|number|`0`|When windows width is less narrow than this, this plugin stops.| |[enabled](http://sutara79.github.io/jquery.simple-scroll-follow/#enabled)|boolean|`true`|If it is `false`, this plugin stops.| +|[upper_side](http://sutara79.github.io/jquery.simple-scroll-follow/#fixed-elem)|string|`null`|Upper side of target element.| +|[lower_side](http://sutara79.github.io/jquery.simple-scroll-follow/#fixed-elem)|string|`null`|Lower side of target element.| ## Public Method diff --git a/dist/jquery.simple-scroll-follow.js b/dist/jquery.simple-scroll-follow.js index 3abf4cc..bb15724 100644 --- a/dist/jquery.simple-scroll-follow.js +++ b/dist/jquery.simple-scroll-follow.js @@ -1,6 +1,6 @@ /** * @file jQuery Plugin: jquery.simple-scroll-follow - * @version 3.0.1 + * @version 3.1.0 * @author Yuusaku Miyazaki * @license MIT License */ @@ -146,7 +146,9 @@ $.extend($.simpleScrollFollow.prototype, /** @lends external:jQuery.simpleScroll this.option = $.extend({ enabled: true, limit_elem: $('body'), - min_width: 0 + min_width: 0, + upper_side: null, + lower_side: null }, option); if (typeof this.option.limit_elem == 'string') { this.option.limit_elem = $(this.option.limit_elem); @@ -161,8 +163,16 @@ $.extend($.simpleScrollFollow.prototype, /** @lends external:jQuery.simpleScroll * @return {number} - 算出されたoffset_bottom */ _calcOffsetBottom: function(elem) { - return $(elem).offset().top + - $(elem).height() + + return $(elem).offset().top + this._calcElemHeight(elem); + }, + + /** + * @private + * @arg {Object} elem - Target element to calc height + * @return {number} - the height which includes border-width and padding + */ + _calcElemHeight: function(elem) { + return $(elem).height() + Number($(elem).css('border-top-width' ).replace(/px$/, '')) + Number($(elem).css('border-bottom-width').replace(/px$/, '')) + Number($(elem).css('padding-top' ).replace(/px$/, '')) + @@ -188,8 +198,8 @@ $.extend($.simpleScrollFollow.prototype, /** @lends external:jQuery.simpleScroll // 画面の上端、下端を取得 var win = { - scroll_top: $(window).scrollTop(), - scroll_bottom: $(window).scrollTop() + $(window).height() + scroll_top: this._getUpperSide(), + scroll_bottom: this._getLowerSide() }; // 追尾要素の "現在の" 上端、下端を取得 @@ -209,6 +219,36 @@ $.extend($.simpleScrollFollow.prototype, /** @lends external:jQuery.simpleScroll return true; }, + /** + * @private + * @returns {number} Upper side of window + */ + _getUpperSide: function() { + var winScrollTop = $(window).scrollTop(); + if (this.option.upper_side) { + var upperLimitBottom = this._calcOffsetBottom(this.option.upper_side); + if (winScrollTop < upperLimitBottom) { + winScrollTop = upperLimitBottom; + } + } + return winScrollTop; + }, + + /** + * @private + * @returns {number} Lower side of window + */ + _getLowerSide: function() { + var winScrollBottom = $(window).scrollTop() + $(window).height(); + if (this.option.lower_side) { + var lowerLimitTop = $(this.option.lower_side).offset().top; + if (winScrollBottom > lowerLimitTop) { + winScrollBottom = lowerLimitTop; + } + } + return winScrollBottom; + }, + /** * @private * @desc イベントハンドラ: 画面スクロール @@ -311,7 +351,7 @@ $.extend($.simpleScrollFollow.prototype, /** @lends external:jQuery.simpleScroll $(this.follow.elem) .css({ position: 'fixed', - top: 0, + top: this._getPositionToStickToWindow(this.option.upper_side), bottom: 'auto', left: this.follow.offset_left, right: 'auto' @@ -328,13 +368,26 @@ $.extend($.simpleScrollFollow.prototype, /** @lends external:jQuery.simpleScroll .css({ position: 'fixed', top: 'auto', - bottom: 0, + bottom: this._getPositionToStickToWindow(this.option.lower_side), left: this.follow.offset_left, right: 'auto' }) .width(this.follow.width); }, + /** + * @private + * @arg {object} limit - this.option.upper_side or this.option.lower_side + * @returns {number} position-top/bottom to stick to window + */ + _getPositionToStickToWindow: function(limit) { + if (limit) { + return this._calcElemHeight(limit); + } else { + return 0; + } + }, + /** * @private * @desc イベントハンドラ: 画面リサイズ diff --git a/dist/jquery.simple-scroll-follow.min.js b/dist/jquery.simple-scroll-follow.min.js index e2e7f66..b8cb06e 100644 --- a/dist/jquery.simple-scroll-follow.min.js +++ b/dist/jquery.simple-scroll-follow.min.js @@ -1,7 +1,7 @@ /** * @file jquery.simple-scroll-follow - * @version 3.0.1 + * @version 3.1.0 * @author Yuusaku Miyazaki * @license MIT */ -!function(a){"object"==typeof module&&"object"==typeof module.exports?a(require("jquery"),window,document):a(jQuery,window,document)}(function(a,b,c,d){a.fn.simpleScrollFollow=function(b){var c=Array.prototype.slice.call(arguments,1);return this.each(function(){a.fn.simpleScrollFollow.processEach(this,b,c)})},a.fn.simpleScrollFollow.processEach=function(b,c,d){var e=a(b).data("simple-scroll-follow");return e&&c in e&&"_"!=c.charAt(0)?(e[c].apply(e,d),"call public method"):"object"!=typeof c&&c?(console.error('Sub-method "'+c+'" does not exist on $.simpleScrollFollow'),"error"):(a(b).data("simple-scroll-follow",new a.simpleScrollFollow(b,c)),"init plugin")},a.simpleScrollFollow=function(c,d){this._setOption(d),this._setFollow(c);var e=this;a(b).scroll(function(){e._handleScroll.call(e)}),this.timer=!1,a(b).resize(function(){e._handleResize.call(e)})},a.extend(a.simpleScrollFollow.prototype,{setEnabled:function(a){return a===d||a?this.option.enabled=!0:(this._moveDefaultPosition(),this.option.enabled=!1)},_moveDefaultPosition:function(){a(this.follow.elem).css({position:"",top:"",bottom:"",left:"",right:""}).width("")},_setFollow:function(b){return this.follow={elem:b,width:a(b).width(),offset_top:a(b).offset().top,offset_bottom:this._calcOffsetBottom(b),offset_left:a(b).offset().left,position_top:"auto"==a(b).css("top")?0:Number(a(b).css("top").replace(/px$/,""))}},_setOption:function(b){return this.option=a.extend({enabled:!0,limit_elem:a("body"),min_width:0},b),"string"==typeof this.option.limit_elem&&(this.option.limit_elem=a(this.option.limit_elem)),this.option},_calcOffsetBottom:function(b){return a(b).offset().top+a(b).height()+Number(a(b).css("border-top-width").replace(/px$/,""))+Number(a(b).css("border-bottom-width").replace(/px$/,""))+Number(a(b).css("padding-top").replace(/px$/,""))+Number(a(b).css("padding-bottom").replace(/px$/,""))},_handleScroll:function(){if(!this.option.enabled)return!1;if(a(b).width()c.offset_bottom?(this._move2(b,c),2):a.scroll_bottom-a.scroll_top>b.offset_bottom-b.offset_top?c.offset_bottom-a.scroll_topc.offset_bottom?(this._move2(b,c),5):a.scroll_bottom-this.follow.offset_top>b.offset_bottom-b.offset_top?(this._move4(),6):(this._move1(),7)},_move1:function(){a(this.follow.elem).css({position:"absolute",top:"",bottom:"",left:"",right:""}).width(this.follow.width)},_move2:function(b,c){a(this.follow.elem).css({position:"absolute",top:c.offset_bottom-this.follow.offset_top-(b.offset_bottom-b.offset_top)+this.follow.position_top,bottom:"auto",left:"",right:""}).width(this.follow.width)},_move3:function(){a(this.follow.elem).css({position:"fixed",top:0,bottom:"auto",left:this.follow.offset_left,right:"auto"}).width(this.follow.width)},_move4:function(){a(this.follow.elem).css({position:"fixed",top:"auto",bottom:0,left:this.follow.offset_left,right:"auto"}).width(this.follow.width)},_handleResize:function(){!1!==this.timer&&clearTimeout(this.timer);var c=this;return this.timer=setTimeout(function(){c._moveDefaultPosition.call(c),c._setFollow.call(c,c.follow.elem),a(b).trigger("scroll")},200)}})}); \ No newline at end of file +!function(a){"object"==typeof module&&"object"==typeof module.exports?a(require("jquery"),window,document):a(jQuery,window,document)}(function(a,b,c,d){a.fn.simpleScrollFollow=function(b){var c=Array.prototype.slice.call(arguments,1);return this.each(function(){a.fn.simpleScrollFollow.processEach(this,b,c)})},a.fn.simpleScrollFollow.processEach=function(b,c,d){var e=a(b).data("simple-scroll-follow");return e&&c in e&&"_"!=c.charAt(0)?(e[c].apply(e,d),"call public method"):"object"!=typeof c&&c?(console.error('Sub-method "'+c+'" does not exist on $.simpleScrollFollow'),"error"):(a(b).data("simple-scroll-follow",new a.simpleScrollFollow(b,c)),"init plugin")},a.simpleScrollFollow=function(c,d){this._setOption(d),this._setFollow(c);var e=this;a(b).scroll(function(){e._handleScroll.call(e)}),this.timer=!1,a(b).resize(function(){e._handleResize.call(e)})},a.extend(a.simpleScrollFollow.prototype,{setEnabled:function(a){return a===d||a?this.option.enabled=!0:(this._moveDefaultPosition(),this.option.enabled=!1)},_moveDefaultPosition:function(){a(this.follow.elem).css({position:"",top:"",bottom:"",left:"",right:""}).width("")},_setFollow:function(b){return this.follow={elem:b,width:a(b).width(),offset_top:a(b).offset().top,offset_bottom:this._calcOffsetBottom(b),offset_left:a(b).offset().left,position_top:"auto"==a(b).css("top")?0:Number(a(b).css("top").replace(/px$/,""))}},_setOption:function(b){return this.option=a.extend({enabled:!0,limit_elem:a("body"),min_width:0,upper_side:null,lower_side:null},b),"string"==typeof this.option.limit_elem&&(this.option.limit_elem=a(this.option.limit_elem)),this.option},_calcOffsetBottom:function(b){return a(b).offset().top+this._calcElemHeight(b)},_calcElemHeight:function(b){return a(b).height()+Number(a(b).css("border-top-width").replace(/px$/,""))+Number(a(b).css("border-bottom-width").replace(/px$/,""))+Number(a(b).css("padding-top").replace(/px$/,""))+Number(a(b).css("padding-bottom").replace(/px$/,""))},_handleScroll:function(){if(!this.option.enabled)return!1;if(a(b).width()d&&(c=d)}return c},_handleScrollMain:function(a,b,c){return a.scroll_topc.offset_bottom?(this._move2(b,c),2):a.scroll_bottom-a.scroll_top>b.offset_bottom-b.offset_top?c.offset_bottom-a.scroll_topc.offset_bottom?(this._move2(b,c),5):a.scroll_bottom-this.follow.offset_top>b.offset_bottom-b.offset_top?(this._move4(),6):(this._move1(),7)},_move1:function(){a(this.follow.elem).css({position:"absolute",top:"",bottom:"",left:"",right:""}).width(this.follow.width)},_move2:function(b,c){a(this.follow.elem).css({position:"absolute",top:c.offset_bottom-this.follow.offset_top-(b.offset_bottom-b.offset_top)+this.follow.position_top,bottom:"auto",left:"",right:""}).width(this.follow.width)},_move3:function(){a(this.follow.elem).css({position:"fixed",top:this._getPositionToStickToWindow(this.option.upper_side),bottom:"auto",left:this.follow.offset_left,right:"auto"}).width(this.follow.width)},_move4:function(){a(this.follow.elem).css({position:"fixed",top:"auto",bottom:this._getPositionToStickToWindow(this.option.lower_side),left:this.follow.offset_left,right:"auto"}).width(this.follow.width)},_getPositionToStickToWindow:function(a){return a?this._calcElemHeight(a):0},_handleResize:function(){!1!==this.timer&&clearTimeout(this.timer);var c=this;return this.timer=setTimeout(function(){c._moveDefaultPosition.call(c),c._setFollow.call(c,c.follow.elem),a(b).trigger("scroll")},200)}})}); \ No newline at end of file diff --git a/index.html b/index.html index 146db14..a959e45 100644 --- a/index.html +++ b/index.html @@ -60,6 +60,10 @@

"enabled" for disabling this plugin by default "enabled"で追尾の有効・無効を設定する + + Fixed element + 固定要素に隠れないようにする + Call public method from outside パブリックメソッドを外部から実行する @@ -231,7 +235,6 @@

Sample -

JavaScript
 $('#enabled').simpleScrollFollow({
   enabled: true
@@ -243,6 +246,35 @@ 
JavaScript
+
+
+

+ Fixed element + 固定要素に隠れないようにする +

+
+
+ +

Since: v3.1.0

+

+ If you use fixed menu such as Bootstrap .navbar-fixed-top, the options below will help you. +

+

+ Bootstrap .navbar-fixed-topのような固定メニューに隠れないようにするため、下記のオプションを指定してください。 +

+
+$('aside').simpleScrollFollow({
+  upper_side: '#menu-fixed-top',
+  lower_side: '#menu-fixed-bottom'
+});
+
+
+
+

@@ -265,7 +297,6 @@

第1引数にメソッド名を文字列で指定します。
第2引数以降はそのメソッドの引数になります。

-

JavaScript
 // apply plugin firstly
 $('aside').simpleScrollFollow();
diff --git a/package.json b/package.json
index c4dc65a..07e82a9 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "jquery.simple-scroll-follow",
   "description": "jQuery plugin to move the element according to the scrolling window.",
-  "version": "3.0.1",
+  "version": "3.1.0",
   "license": "MIT",
   "author": "Yuusaku Miyazaki ",
   "homepage": "https://github.com/sutara79/jquery.simple-scroll-follow",
diff --git a/sample/fixed-elem/index.html b/sample/fixed-elem/index.html
new file mode 100644
index 0000000..fa76741
--- /dev/null
+++ b/sample/fixed-elem/index.html
@@ -0,0 +1,132 @@
+
+
+
+
+
+Bootstrap navbar: jquery.simple-scroll-follow
+
+
+
+
+
+
+

Bootstrap navbar

+
+
+
+

article

+

+ width: 200px;
+ position: static;
+ top: 0px;
+ left: 0px; +

+

1

+

2

+

3

+

4

+

5

+

6

+

7

+

8

+

9

+

10

+

11

+

12

+

13

+

14

+

15

+

16

+

17

+

18

+

19

+

20

+

21

+

22

+

23

+

24

+

25

+

26

+

27

+

28

+

29

+

30

+

31

+

32

+

33

+

34

+

35

+

36

+

37

+

38

+

39

+

40

+
+ +
+ + + + + + + + + \ No newline at end of file diff --git a/sample/fixed-elem/sample.css b/sample/fixed-elem/sample.css new file mode 100644 index 0000000..1b4f7b8 --- /dev/null +++ b/sample/fixed-elem/sample.css @@ -0,0 +1,37 @@ +@charset "utf-8"; + +body { + margin: 0; + padding: 0; + background: url(null) fixed; + padding-top: 50px; /* Bootstrap .navbar-fixed-top */ +} + +header { + padding: 16px; + background: #bcd; + border-bottom: 1px solid #999; +} + +#contents { + position: relative; +} + +article { + box-sizing: border-box; + width: 200px; + padding: 16px; + background: #bcd; + position: static; +} + +aside { + box-sizing: border-box; + width: 200px; + padding: 16px; + background: #fbb; + position: absolute; + top: 0px; + left: 200px; + border: 2px solid #f77; +} \ No newline at end of file diff --git a/sample/fixed-elem/sample.js b/sample/fixed-elem/sample.js new file mode 100644 index 0000000..025fa4a --- /dev/null +++ b/sample/fixed-elem/sample.js @@ -0,0 +1,6 @@ +jQuery(document).ready(function($) { + $('aside').simpleScrollFollow({ + upper_side: '#menu-fixed-top', + lower_side: '#menu-fixed-bottom' + }); +}); \ No newline at end of file diff --git a/test/index.html b/test/index.html index 224f630..1afac8d 100644 --- a/test/index.html +++ b/test/index.html @@ -20,8 +20,12 @@ + + + + diff --git a/test/jQuery.simpleScrollFollow/_calcElemHeight.js b/test/jQuery.simpleScrollFollow/_calcElemHeight.js new file mode 100644 index 0000000..36e0e04 --- /dev/null +++ b/test/jQuery.simpleScrollFollow/_calcElemHeight.js @@ -0,0 +1,31 @@ +/** + * @file Unit Testing by QUnit 1.x -- $.simpleScrollFollow._calcElemHeight() + */ +jQuery(document).ready(function($) { + + module('$.simpleScrollFollow._calcElemHeight', { + setup: function() { + this.target = $('
') + .height(50) + .css({ + 'padding-top': 4, + 'padding-bottom': 6, + 'border': '1px solid #f00', + 'border-top-width': 1, + 'border-bottom-width': 3 + }) + .appendTo('body'); + }, + teardown: function() { + this.target.remove(); + delete this.obj; + $(window).off('scroll resize'); + } + }); + + test('Height should be 64', 1, function() { + var h = $.simpleScrollFollow.prototype._calcElemHeight(this.target); + ok(h == 64); + }); + +}); \ No newline at end of file diff --git a/test/jQuery.simpleScrollFollow/_getLowerSide.js b/test/jQuery.simpleScrollFollow/_getLowerSide.js new file mode 100644 index 0000000..4660660 --- /dev/null +++ b/test/jQuery.simpleScrollFollow/_getLowerSide.js @@ -0,0 +1,41 @@ +/** + * @file Unit Testing by QUnit 1.x -- $.simpleScrollFollow._getLowerSide() + */ +$(function() { + + module('$.simpleScrollFollow._getLowerSide', { + setup: function() { + this.target = $('
').appendTo('body'); + }, + teardown: function() { + this.target.remove(); + delete this.obj; + $(window).off('scroll resize'); + } + }); + + test('should equal to lower limit', 1, function() { + var lower_side = $('
') + .css({ + position: 'absolute', + bottom: 0 + }) + .appendTo('body'); + + this.obj = new $.simpleScrollFollow(this.target, { + lower_side: lower_side + }); + + var lowerLimitTop = $(lower_side).offset().top; + var returns = this.obj._getLowerSide(); + strictEqual(returns, lowerLimitTop); + }); + + test('should equal to scrollBottom of window', 1, function() { + var winScrollBottom = $(window).scrollTop() + $(window).height(); + this.obj = new $.simpleScrollFollow(this.target); + var returns = this.obj._getLowerSide(); + strictEqual(returns, winScrollBottom); + }); + +}); \ No newline at end of file diff --git a/test/jQuery.simpleScrollFollow/_getPositionToStickToWindow.js b/test/jQuery.simpleScrollFollow/_getPositionToStickToWindow.js new file mode 100644 index 0000000..04d5ee1 --- /dev/null +++ b/test/jQuery.simpleScrollFollow/_getPositionToStickToWindow.js @@ -0,0 +1,36 @@ +/** + * @file Unit Testing by QUnit 1.x -- $.simpleScrollFollow._getPositionToStickToWindow() + */ +$(function() { + + module('$.simpleScrollFollow._getPositionToStickToWindow', { + setup: function() { + this.target = $('
') + .height(50) + .css({ + 'padding-top': 4, + 'padding-bottom': 6, + 'border': '1px solid #f00', + 'border-top-width': 1, + 'border-bottom-width': 3 + }) + .appendTo('body'); + }, + teardown: function() { + this.target.remove(); + delete this.obj; + $(window).off('scroll resize'); + } + }); + + test('Returns should be 64', 1, function() { + var h = $.simpleScrollFollow.prototype._getPositionToStickToWindow(this.target); + ok(h === 64); + }); + + test('Returns should be 0', 1, function() { + var h = $.simpleScrollFollow.prototype._getPositionToStickToWindow(null); + ok(h === 0); + }); + +}); \ No newline at end of file diff --git a/test/jQuery.simpleScrollFollow/_getUpperSide.js b/test/jQuery.simpleScrollFollow/_getUpperSide.js new file mode 100644 index 0000000..a5095f6 --- /dev/null +++ b/test/jQuery.simpleScrollFollow/_getUpperSide.js @@ -0,0 +1,32 @@ +/** + * @file Unit Testing by QUnit 1.x -- $.simpleScrollFollow._getUpperSide() + */ +$(function() { + + module('$.simpleScrollFollow._getUpperSide', { + setup: function() { + this.target = $('
').appendTo('body'); + }, + teardown: function() { + this.target.remove(); + delete this.obj; + $(window).off('scroll resize'); + } + }); + + test('should be above 0', 1, function() { + this.upper_side = $('
').appendTo('body'); + this.obj = new $.simpleScrollFollow(this.target, { + upper_side: this.upper_side + }); + var returns = this.obj._getUpperSide(); + ok(returns > 0); + }); + + test('should be 0', 1, function() { + this.obj = new $.simpleScrollFollow(this.target); + var returns = this.obj._getUpperSide(); + ok(returns === 0); + }); + +}); \ No newline at end of file diff --git a/test/jQuery.simpleScrollFollow/_handleResize.js b/test/jQuery.simpleScrollFollow/_handleResize.js index 96efa2c..620b9af 100644 --- a/test/jQuery.simpleScrollFollow/_handleResize.js +++ b/test/jQuery.simpleScrollFollow/_handleResize.js @@ -7,7 +7,7 @@ jQuery(document).ready(function($) { setup: function() { $('
').appendTo('body'); this.target = $('#target'); - this.obj = new $.simpleScrollFollow(target); + this.obj = new $.simpleScrollFollow(this.target); }, teardown: function() { this.target.remove(); diff --git a/test/jQuery.simpleScrollFollow/_handleScroll.js b/test/jQuery.simpleScrollFollow/_handleScroll.js index d7ae01f..66f9965 100644 --- a/test/jQuery.simpleScrollFollow/_handleScroll.js +++ b/test/jQuery.simpleScrollFollow/_handleScroll.js @@ -16,20 +16,20 @@ jQuery(document).ready(function($) { } }); - test('() option.enabled = false', 1, function() { + test('should be false when option.enabled = false', 1, function() { this.obj.option.enabled = false; strictEqual(this.obj._handleScroll(), false); }); - test('() option.min_width = 1000', 1, function() { + test('should be false when option.min_width = 8000', 1, function() { // node_modules/phantomjs/lib/phantom/examples/colorwheel.coffee // node_modules/phantomjs/lib/phantom/examples/colorwheel.js // viewportSize = { width: 400, height : 400 } - this.obj.option.min_width = 1000; + this.obj.option.min_width = 8000; strictEqual(this.obj._handleScroll(), false); }); - test('()', 1, function() { + test('should be true when no options', 1, function() { strictEqual(this.obj._handleScroll(), true); });