51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

分享一个原生Javascript应用:页面滚动导航且导航吸顶切换功能

500.jpg

转眼间,2022快过一半啦,今天迎来了6月的第一天,也是儿童节,也祝福天下的大小朋友们节日快乐!

今天小编继续给大家分享点东西,关于我们页面中导航的一些定位已经自动切换功能。废话不多说,往下看吧。

预览

先来看张图:

image.png

这是我们刚打开页面进来的默认效果,当我们往下拉到导航的时候,会变化成如下图显示效果:

image.png

导航直接吸顶了,并且固定,我们继续往下滑动后,会根据导航对应的内容,导航的标签进行状态切换。是不是很好玩?接下来看下代码如何实现吧!

首先需要布局下页面,直接放代码。

HTML

<div class="right-section">
    <div class="top-section">Web前端之家<br />www.jiangweishan.com</div>
    <div class="nav-bar-wrap">
      <ul class="nav-bar" onclick="selectNav()">
        <li data-content="content1" class="active">Web前端之家</li>
        <li data-content="content2" class="">Web前端前端教程</li>
        <li data-content="content3" class="">React教程</li>
      </ul>
    </div>
    <div class="nav-content-container">
      <div class="content content1">欢迎来到Web前端之家,我们的网址是https://www.jiangweishan.com</div>
      <div class="content content2">Web前端前端教程</div>
      <div class="content content3">React教程</div>
    </div>
</div>

CSS

html {
  height: 100%;
  overflow: hidden;
}
body {
  margin: 0;
  padding-top: 0; 
  height: 100%;
  font-size: 0;
  overflow: auto;
  background-color: #f5f6fa;
}

.right-section {   position: relative; /* 这是重点 */   display: inline-block;   margin-left: 24px;   width: 100%;   font-size: 14px;   vertical-align: top; } .top-section {   background: #fff;   padding: 120px 20px;   text-align: center;   line-height: 1.6;   font-size: 26px;   font-weight: bold; } .nav-bar-wrap {   margin: 16px 0;   height: 55px; } .nav-bar {   margin: 0;   padding: 0;   height: 100%;   border-radius: 5px;   background: #fff;   box-shadow: 0 1px 8px 4px rgba(0,0,0,.02); } .nav-bar li {   display: inline-block;   padding: 16px;   cursor: pointer; } .nav-bar li.active {   color: #387af6;   border-bottom: 2px solid #387af6; } .content{   padding: 20px; } .content1 {   height: 300px;   background: #fff; } .content2 {   margin: 16px 0;   height: 500px;   background: #fff; } .content3 {   height: 1000px;   background: #fff; }

Javascript

Javascript代码是核心,当然了,如果您对于基础JS代码熟悉的话,对于下面的JS代码就不会感到棘手。

<script>
var navBar = document.querySelector('.nav-bar');
var menu = document.querySelectorAll('.nav-bar li');
var scrollContainer = document.querySelector('body');
var needFixed = true;
// 这是body的paddingTop,因为导航栏的offsetParent不是body,因为要减去body的paddingTop才能用来跟导航栏的offsetTop做比较
var extraFixed = -24;
// 55 - 24:导航栏高度 - body的paddingTop
// 在不吸顶的情况下,导航指定的内容只要滚动到body顶部就算到了该内容了的导航了,即滚动了【内容的offsetTop + body的paddingTop】的距离
// 但是吸顶之后,只要滚动到吸顶导航栏底部就算到了指定导航内容了,所以相当于只要滚动【内容的offsetTop + body的paddingTop - 吸顶导航栏的高度】的距离就会到达临界值
// 转换成公式来理解,c代表导航内容的offsetTop,s代表滚动的距离,body的paddingTop为24,吸顶导航栏高度为55。只要滚动距离大于等于上面说的临界值,即肯定到达了对应导航。
// 因此公式为: s >= c + 24 - 55, 即 s + 31 >= c 时,到达条件成立,因此滚动容器的scrollTop都要加上31,才是拿来判断的值
var extraScroll = 31;
var offsetTops = {};
var isSupportSticky = validateSticky();
calcTop(true);
scrollContainer.addEventListener('scroll', handleScroll);
window.addEventListener('resize', hanldeResize);

/**  * 计算页面的各个offsetTop  */ function calcTop(recalNav) {   recalNav && (offsetTops.navBar = navBar.offsetTop);   var contents = ['content1', 'content2', 'content3'];   for (var j = 0; j < contents.length; j++) {     offsetTops[contents[j]] = document.querySelector('.' + contents[j]).offsetTop;   } }

/**  * 选择标题跳到对应内容  */ function selectNav(e) {   var ev = e || event;   var target = ev.target || ev.srcElement; // 兼容IE   this.resetNavSelect();   target.className = 'active';   scrollContainer.scrollTop = offsetTops[target.getAttribute('data-content')] - extraScroll; }

/**  * 重置导航栏激活样式  */ function resetNavSelect() {   for (var i = 0; i < menu.length; i++) {     menu[i].className = '';   } }

/**  * 检查浏览器是否有支持的sticky值,没有返回false,有就添加sticky相关css,实现吸顶  */ function validateSticky () {   var supportStickyValue = valiateCssValue('position', 'sticky');   if (supportStickyValue) {     var navBarWrap = document.querySelector('.nav-bar-wrap');     navBarWrap.style.position = supportStickyValue;     navBarWrap.style.top = '0';     return true;   }   return false; }

/**  * 监听滚动事件,实现吸顶和滚动导航  */ function handleScroll() {   var top = scrollContainer.scrollTop;   var fixedBaseTop = top + extraScroll; // 这是吸顶之后用来做衡量的scrollTop   var menuLength = menu.length;   if (needFixed && !isSupportSticky) {     // 这是控制导航栏吸顶 - 吸顶     if ((top + extraFixed) >= offsetTops.navBar) {       navBar.style.position = 'fixed';       navBar.style.top = 0;       navBar.style.left = '124px';       navBar.style.width = '300px';       navBar.style.height = '55px';     }     // 这是控制导航栏吸顶 - 取消吸顶     if ((top + extraFixed) < offsetTops.navBar) {       navBar.style.position = 'static';       navBar.style.width = '100%';       navBar.style.height = '100%';     }   }   resetNavSelect();   // 滚动条到达底部就选中最后一个导航   if (top + scrollContainer.clientHeight >= scrollContainer.scrollHeight) {     menu[menuLength - 1].className = 'active';     return;   }   // 以下都为依据滚动自动选择对应导航   for (var i = 0; i < menuLength - 1; i++) {     if (fixedBaseTop >= offsetTops['content' + (i + 1)] && fixedBaseTop < offsetTops['content' + (i + 2)]) {       menu[i].className = 'active';       return;     }   }   if (fixedBaseTop >= offsetTops['content' + (menuLength - 1)]) {     menu[menuLength - 1].className = 'active';     return;   }   menu[0].className = 'active'; }

/**  * 监听resize事件,变化时重新计算offsetTop  */ function hanldeResize() {   calcTop(true); }

/**  * 检查浏览器是否支持某个css属性值  */ function valiateCssValue (key, value) {   var prefix = ['-o-', '-ms-', '-moz-', '-webkit-', ''];   var prefixValue = [];   for (var i = 0; i < prefix.length; i++) {     prefixValue.push(prefix[i] + value)   }   var element = document.createElement('div');   var eleStyle = element.style;   for (var j = 0; j < prefixValue.length; j++) {     eleStyle[key] = prefixValue[j];   }   return eleStyle[key]; }

</script>

上面的代码,是进行封装过的,所以算比较规范了。大家可以拿去直接用。

完整DEMO:

<html><head>
  <meta charset="utf-8">
  <title>滚动导航并且吸顶功能 | Web前端之家www.jiangweishan.com</title>
<style id="jsbin-css">
html {
  height: 100%;
  overflow: hidden;
}
body {
  margin: 0;
  padding-top: 0; /* 这是重点 */
  height: 100%;
  font-size: 0;
  overflow: auto;
  background-color: #f5f6fa;
}

.right-section {   position: relative; /* 这是重点 */   display: inline-block;   margin-left: 24px;   width: 100%;   font-size: 14px;   vertical-align: top; } .top-section {   background: #fff;   padding: 120px 20px;   text-align: center;   line-height: 1.6;   font-size: 26px;   font-weight: bold; } .nav-bar-wrap {   margin: 16px 0;   height: 55px; } .nav-bar {   margin: 0;   padding: 0;   height: 100%;   border-radius: 5px;   background: #fff;   box-shadow: 0 1px 8px 4px rgba(0,0,0,.02); } .nav-bar li {   display: inline-block;   padding: 16px;   cursor: pointer; } .nav-bar li.active {   color: #387af6;   border-bottom: 2px solid #387af6; } .content{   padding: 20px; } .content1 {   height: 300px;   background: #fff; } .content2 {   margin: 16px 0;   height: 500px;   background: #fff; } .content3 {   height: 1000px;   background: #fff;         } </style> <body>   <div class="right-section">     <div class="top-section">Web前端之家<br />www.jiangweishan.com</div>     <div class="nav-bar-wrap">       <ul class="nav-bar" onclick="selectNav()">         <li data-content="content1" class="active">Web前端之家</li>         <li data-content="content2" class="">Web前端前端教程</li>         <li data-content="content3" class="">React教程</li>       </ul>     </div>     <div class="nav-content-container">       <div class="content content1">欢迎来到Web前端之家,我们的网址是https://www.jiangweishan.com</div>       <div class="content content2">Web前端前端教程</div>       <div class="content content3">React教程</div>     </div>   </div> <script>     var navBar = document.querySelector('.nav-bar'); var menu = document.querySelectorAll('.nav-bar li'); var scrollContainer = document.querySelector('body'); var needFixed = true; // 这是body的paddingTop,因为导航栏的offsetParent不是body,因为要减去body的paddingTop才能用来跟导航栏的offsetTop做比较 var extraFixed = -24; // 55 - 24:导航栏高度 - body的paddingTop // 在不吸顶的情况下,导航指定的内容只要滚动到body顶部就算到了该内容了的导航了,即滚动了【内容的offsetTop + body的paddingTop】的距离 // 但是吸顶之后,只要滚动到吸顶导航栏底部就算到了指定导航内容了,所以相当于只要滚动【内容的offsetTop + body的paddingTop - 吸顶导航栏的高度】的距离就会到达临界值 // 转换成公式来理解,c代表导航内容的offsetTop,s代表滚动的距离,body的paddingTop为24,吸顶导航栏高度为55。只要滚动距离大于等于上面说的临界值,即肯定到达了对应导航。 // 因此公式为: s >= c + 24 - 55, 即 s + 31 >= c 时,到达条件成立,因此滚动容器的scrollTop都要加上31,才是拿来判断的值 var extraScroll = 31; var offsetTops = {}; var isSupportSticky = validateSticky(); calcTop(true); scrollContainer.addEventListener('scroll', handleScroll); window.addEventListener('resize', hanldeResize);

/**  * 计算页面的各个offsetTop  */ function calcTop(recalNav) {   recalNav && (offsetTops.navBar = navBar.offsetTop);   var contents = ['content1', 'content2', 'content3'];   for (var j = 0; j < contents.length; j++) {     offsetTops[contents[j]] = document.querySelector('.' + contents[j]).offsetTop;   } }

/**  * 选择标题跳到对应内容  */ function selectNav(e) {   var ev = e || event;   var target = ev.target || ev.srcElement; // 兼容IE   this.resetNavSelect();   target.className = 'active';   scrollContainer.scrollTop = offsetTops[target.getAttribute('data-content')] - extraScroll; }

/**  * 重置导航栏激活样式  */ function resetNavSelect() {   for (var i = 0; i < menu.length; i++) {     menu[i].className = '';   } }

/**  * 检查浏览器是否有支持的sticky值,没有返回false,有就添加sticky相关css,实现吸顶  */ function validateSticky () {   var supportStickyValue = valiateCssValue('position', 'sticky');   if (supportStickyValue) {     var navBarWrap = document.querySelector('.nav-bar-wrap');     navBarWrap.style.position = supportStickyValue;     navBarWrap.style.top = '0';     return true;   }   return false; }

/**  * 监听滚动事件,实现吸顶和滚动导航  */ function handleScroll() {   var top = scrollContainer.scrollTop;   var fixedBaseTop = top + extraScroll; // 这是吸顶之后用来做衡量的scrollTop   var menuLength = menu.length;   if (needFixed && !isSupportSticky) {     // 这是控制导航栏吸顶 - 吸顶     if ((top + extraFixed) >= offsetTops.navBar) {       navBar.style.position = 'fixed';       navBar.style.top = 0;       navBar.style.left = '124px';       navBar.style.width = '300px';       navBar.style.height = '55px';     }     // 这是控制导航栏吸顶 - 取消吸顶     if ((top + extraFixed) < offsetTops.navBar) {       navBar.style.position = 'static';       navBar.style.width = '100%';       navBar.style.height = '100%';     }   }   resetNavSelect();   // 滚动条到达底部就选中最后一个导航   if (top + scrollContainer.clientHeight >= scrollContainer.scrollHeight) {     menu[menuLength - 1].className = 'active';     return;   }   // 以下都为依据滚动自动选择对应导航   for (var i = 0; i < menuLength - 1; i++) {     if (fixedBaseTop >= offsetTops['content' + (i + 1)] && fixedBaseTop < offsetTops['content' + (i + 2)]) {       menu[i].className = 'active';       return;     }   }   if (fixedBaseTop >= offsetTops['content' + (menuLength - 1)]) {     menu[menuLength - 1].className = 'active';     return;   }   menu[0].className = 'active'; }

/**  * 监听resize事件,变化时重新计算offsetTop  */ function hanldeResize() {   calcTop(true); }

/**  * 检查浏览器是否支持某个css属性值  */ function valiateCssValue (key, value) {   var prefix = ['-o-', '-ms-', '-moz-', '-webkit-', ''];   var prefixValue = [];   for (var i = 0; i < prefix.length; i++) {     prefixValue.push(prefix[i] + value)   }   var element = document.createElement('div');   var eleStyle = element.style;   for (var j = 0; j < prefixValue.length; j++) {     eleStyle[key] = prefixValue[j];   }   return eleStyle[key]; }

</script>

</body> </html>

说明:

因为这里只是一个简单的DEMO,如果大家在应用的时候出现问题,也可以对应的去调整下。

总结

关于这种导航功能的应用,还有很多方法,比如用插件等。我们用框架(vuereact)也可以轻松实现,只不过写法不同,但是核心方法一样。

赞(2)
未经允许不得转载:工具盒子 » 分享一个原生Javascript应用:页面滚动导航且导航吸顶切换功能