<div> <ul data-target="#main-container"> <li role="presentation"><a href="#">Home</a></li> <li role="presentation"><a href="#">Profile</a></li> <li role="presentation"><a href="#">Messages</a></li> </ul> </div> <div> <div> ... </div> ... </div>
固定在底部的html结构:
<div> <div> ... </div> ... </div> <div> <ul data-target="#main-container"> <li role="presentation"><a href="#">Home</a></li> <li role="presentation"><a href="#">Profile</a></li> <li role="presentation"><a href="#">Messages</a></li> </ul> </div>
以上#main-container就是我们的target元素,#sticky就是我们的sticky元素,还需要注意两点:
a. 顺序问题,两种结构中,target元素与sticky的父元素顺序位置是反的;
b. sticky元素外面必须包裹一层元素,而且还得给这一层元素设置height属性:
.sticky-wrapper { margin-bottom: 10px; height: 52px; }
这是因为当sticky元素被固定的时候,它会脱离普通文档流,所以要利用它的父元素把sticky元素的高度在普通文档流中撑起来,以免在固定效果出现的时候,target元素的内容出现跳动的情况。
2)固定效果
让一个元素固定在浏览器的某个位置,当然是通过position: fixed来弄,所以可以用两个css类来实现固定在顶部和固定在底部的效果:
.sticky--in-top,.sticky--in-bottom { position: fixed; z-index: 1000; } .sticky--in-top { top: 0; } .sticky--in-bottom { bottom: 0; }
当我们判断元素需要被固定在顶部的时候,就给它添加.sticky--in-top的css类;当我们判断元素需要被固定在底部的时候,就给它添加.sticky--in-bottom的css类。
3)滚动回调
控制sticy元素固定的逻辑显然要写在window的scroll事件回调中(有了前面对实现思路以及判断条件的说明,相信理解下面这段代码应该会很容易):
固定在顶部的回调逻辑:
$(window).scroll(function() {
var rect = $target[0].getBoundingClientRect();
if (rect.top < 0 && (rect.bottom - stickyHeight) > 0) {
!$elem.hasClass('sticky--in-top') && $elem.addClass('sticky--in-top').css('width', stickyWidth + 'px');
} else {
$elem.hasClass('sticky--in-top') && $elem.removeClass('sticky--in-top').css('width', 'auto');
}
});
其中:$target是target元素的jq对象,$elem是sticky元素的jq对象,stickyHeight是sticky元素的高度,stickyWidth是sticky元素的宽度。由于sticky元素固定时,脱离原来的文档流,需要设置宽度才能显示跟固定前一样的宽度。
固定在底部的回调逻辑:
$(window).scroll(function() {
var rect = $target[0].getBoundingClientRect(),
docClientWidth = document.documentElement.clientHeight;
if (rect.bottom > docClientWidth && (rect.top + stickyHeight) < docClientWidth) {
!$elem.hasClass('sticky--in-bottom') && $elem.addClass('sticky--in-bottom').css('width', stickyWidth + 'px');
} else {
$elem.hasClass('sticky--in-bottom') && $elem.removeClass('sticky--in-bottom').css('width', 'auto');
}
});
这里是为了把回调逻辑说的更清楚才把代码分成两份,最后给的实现会把这两个代码合并成一份:)
4)函数节流
函数节流通常应用于window的scroll事件,resize事件以及普通元素的mousemove事件,因为这些事件由于鼠标或滚轮操作很频繁,会导致回调连续触发,如果回调里面含有DOM操作,这种连续调用就会影响页面的性能,所以很有必要控制这类回调的执行次数,函数节流就是做这个的,我这里提供了一个很简单的函数节流实现:
function throttle(func, wait) {
var timer = null;
return function() {
var self = this,
args = arguments;
if (timer) clearTimeout(timer);
timer = setTimeout(function() {
return typeof func === 'function' && func.apply(self, args);
}, wait);
}
}
这个函数可以控制func所指定的函数,执行的间隔指定为wait指定的毫秒数,利用它,我们可以把前面的滚动回调改动一下,比如固定在顶部的情况改成:
$(window).scroll(throttle(function() { var rect = $target[0].getBoundingClientRect(), docClientWidth = document.documentElement.clientHeight; if (rect.bottom > docClientWidth && (rect.top + stickyHeight) < docClientWidth) { !$elem.hasClass('sticky--in-bottom') && $elem.addClass('sticky--in-bottom').css('width', stickyWidth + 'px'); } else { $elem.hasClass('sticky--in-bottom') && $elem.removeClass('sticky--in-bottom').css('width', 'auto'); } }, 50);