场景:在HTML页面中,我们经常会遇到页面内容过多后,通过设置浮动的导航条快速跳转到页面指定的位置,此时我们会想到使用HTML提供的锚链接。但锚链接有一个弊端,就是当导航条在顶部时,点击锚链接跳转后的内容会从页面顶部开始,那么导航条就会遮挡部分元素。同时,还要实现页面滚动时的导航条联动效果,此时就需要应用js来实现。下面先直接上代码。
html代码
<div class="box"></div>
<div class="navigator_box">
<div class="navigator_nav">
<ul>
<li id="oneBar" class="active" barclass="info_one">信息一</li>
<li id="twoBar" barclass="info_two">信息二</li>
<li id="threeBar" barclass="info_three">信息三</li>
<li id="foreBar" barclass="info_fore">信息四</li>
</ul>
</div>
</div>
<div class="info_one">
<div>信息一</div>
</div>
<div class="info_two">
<div>信息二</div>
</div>
<div class="info_three">
<div>信息三</div>
</div>
<div class="info_fore">
<div>信息四</div>
</div>
CSS代码
.box{height: 355px;border: 1px solid #000000;}
.navigator_box{position: relative;height: 70px;}
.navigator_box .navigator_nav{width: 100%;}
.navigator_box .navigator_nav ul{overflow: hidden;margin: 0;padding: 0;background-color: #DADADA;}
.navigator_box .navigator_nav li{list-style-type: none;float: left;height: 70px;line-height: 70px;width: 150px;text-align: center;cursor: pointer;}
.navigator_box .navigator_nav li.active{background-color: greenyellow;color: #fff;}
.info_one{height: 300px;border: 1px solid blue;}
.info_two{height: 1000px;border: 1px solid red;margin-top: 20px;}
.info_three{height: 500px;border: 1px solid #ADFF2F;margin-top: 20px;}
.info_fore{height: 500px;border: 1px solid #F4A460;margin-top: 20px;}
.nav_fixed{position: fixed;top: 0;}
页面中使用了几个空的div元素,通过定义高度来撑开页面,并设置了border来区分。下面是预览
css中特别需要注意的地方已用红色标出,他是为要fixed的元素’.navigator_nav’设置的一个盒子,用于在此元素浮动后占据此元素原本的位置,避免元素浮动后页面高度减少带来的问题。当然,也可以在设置元素浮动的js动态的为浮动元素的下一个元素添加marging-top属性,但能减少js代码就要尽量减少不是。下面是重点的js代码,使用了jQuery框架。
js代码
jQuery(document).ready(function(){
var ul_top,info_one,info_two,info_three,info_fore;
initScroll();
function initScroll() {
// 导航条距离顶部高度
ul_top = $('.navigator_box').offset().top;
// 信息一距离顶部高度
info_one = $('.info_one').offset().top;
// 信息二距离顶部高度
info_two = $('.info_two').offset().top;
// 信息三距离顶部高度
info_three = $('.info_three').offset().top;
// 信息四距离顶部高度
info_fore = $('.info_fore').offset().top;
// 添加滚动监听
window.onscroll = scrollEvent
};
/**
* 滚动监听
*/
function scrollEvent() {
if(document.documentElement && document.documentElement.scrollTop) {//若要适配ie
targetTop = document.documentElement.scrollTop;
} else if(document.body) {
targetTop = document.body.scrollTop;
}
if (targetTop >= ul_top){
$('.navigator_nav').addClass('nav_fixed');
}else{
$('.navigator_nav').removeClass('nav_fixed');
}
// 此处代码实现导航条的联动样式
// 70是导航条的高度,讲道理应该用js来获取,可是我懒啊,同时也有可能并不是导航条的高度,
// 这与你的页面布局有很大关系,如果你理解了这里的代码的原理,你就可以随心所欲的调节他的大小来实现自己的功能
if(targetTop >= (info_one - 70) && targetTop < info_two - 70){
$('.navigator_nav').find('li.active').removeClass("active");
$("#oneBar").addClass("active");
} else if(targetTop >= info_two - 70 && targetTop < info_three - 70){
$('.navigator_nav').find('li.active').removeClass("active");
$("#twoBar").addClass("active");
} else if(targetTop >= info_three - 70 && targetTop < info_fore - 70){
$('.navigator_nav').find('li.active').removeClass("active");
$("#threeBar").addClass("active");
} else if(targetTop >= info_fore - 70){
$('.navigator_nav').find('li.active').removeClass("active");
$("#foreBar").addClass("active");
}
};
// 点击导航条后滑动到相应的位置
$(".navigator_nav").on('click','li',function(){
var barclass = $(this).attr('barclass');
var top = document.getElementsByClassName(barclass)[0].offsetTop - 70;
console.log(top)
$("html,body").animate({
scrollTop: top
}, 500);
});
});
效果图
代码的重点在于获取屏幕向上滚动的距离以及页面中每一块所处的位置的高度范围。因为我的页面布局可能较为简单,所以就没有那么复杂的计算,比如offsetTop就直接获取到了元素距离页面顶部的高度,但如果页面元素较为复杂,那么代码中多处都需要计算改动。说到这里,就应该要注意一下offsetTop获取的是元素顶部距离其父元素到高度,同时jQuery的写法是obj.offset().top。
代码中有一个地方写了IE8的兼容模式,其实有这个还是不够的,若要适配IE8,不仅要在页面中引入IE8的jQuery版本,同时还要重写js的getElementByClassName方法,此处只贴如何重写此方法,而具体的实现代码就不在赘述。毕竟,谁™还要去用沙雕一样的IE8,可这世界从来就不缺沙雕是不是(一个刚写完适配IE8项目的程序猿的心声)。
getElementsByClassName : function(classname){
var obj;
var reg = new RegExp("^.*"+classname+".*$");
if(document.getElementsByClassName){
obj = document.getElementsByClassName(classname)[0];
}else{
var divs = document.getElementsByTagName("div");//获取所有的div标签
for(var i = 0; i < divs.length; i++ ){//遍历,挑出所有满足情况的div
if(divs[i].className.match(reg)){
obj = divs[i];
break;
}
}
}
return obj;
}
最后,说一下容易忽略的地方,若如上的内容一、二、三、四中的内容是通过jquery动态获取的,那么调用initScroll方法初始化各个元素距离顶部的距离就应该在元素内容填充完毕后再调用,否则其获取的仍然是填充内容之前的高度,并且如果之后还会再次动态改变内容,就需要再次调用来初始化高度。如果你填充的内容里面有图片,不要着急,一定要等到图片加载完毕后再调用,否则就算你是在填充的内容之后调用的方法,任然无法获取到正确的距离顶部的距离。以下是我的解决方案:
// 假如信息二里面有动态加载的图片
var imgEle = $('.info_two img');
if(imgEle.length > 0){
$('.info_two img').load(function(){
initScroll();
});
}else{
initScroll();
}