最近看到寫 “錨點自動跟隨滾動定位”的方法,大都是基於JQ,或者是第三方。
所以,進行給出使用原生JS的寫法。
什麼都不說了,直接上代碼(使用模塊模式方式):
<!DOCTYPE html>
<html>
<head>
<title></title>
<style>
div.section_maodian {height: 500px;width: 100%;}
ul,li {list-style: none;}
ul {position: fixed; right:10px;width: 100px;}
li {height: 20px;background: burlywood;padding: 10px;cursor: pointer;}
li.active {background-color: brown;}
li.active {color: #fff};
</style>
</head>
<body>
<div class="section_maodian">秒殺專區</div>
<div class="section_maodian">買一贈一</div>
<div class="section_maodian">品牌齊聚</div>
<script>
const ScrollAnchorModule = function() {
let scrollH = document.documentElement.scrollTop||document.body.scrollTop;
//scroll更新
const updateNav = (secItems,navItems) => {
if(getScrollTop()===0)
navon(0,navItems);
if(getScrollTop()+getWindowHeight()===getScrollHeight())
navon(secItems.length-1,navItems);
else {
secItems.forEach((item,index)=>{
if(getScrollTop()>=item.offsetTop){
navon(index,navItems);
}
});
}
}
//定位
const toScroll = (index,secItems) => {
//計算位置
let scrollY = secItems[index].offsetTop;
window.scrollTo(0,scrollY);
}
//當前錨點
const navon = (index,navItems) => {
navItems.forEach((item)=>{
item.classList = "";
});
navItems[index].classList = "active";
}
//防抖
const debounce = (fn,wait) => {
let timeout = null;
return function() {
if(timeout!==null) clearTimeout(timeout);
timeout = setTimeout(fn,wait);
}
}
//節流
const throttle = (fn,delay) => {
let prev = Date.now();//記錄上一次觸發回調的時間
return function() {
let context = this;//保留調用時的this上下文
let args = arugments;//保留調用時傳入的參數
let now = Date.now();//記錄本次觸發回調的時間
if(now-prev >=delay) {//判斷上次觸發回調的時間和本次觸發回調的時間差十分小於時間間隔的閾值
fn.apply(context,args);//大於設定的閾值,則執行回調
prev = Date.now();
}
}
}
//滾動條在Y軸上的滾動距離
const getScrollTop = () => {
let scrollTop = 0, bodyScrollTop = 0, documentScrollTop = 0;
if(document.body){
bodyScrollTop = document.body.scrollTop;
}
if(document.documentElement){
documentScrollTop = document.documentElement.scrollTop;
}
scrollTop = (bodyScrollTop - documentScrollTop > 0) ? bodyScrollTop : documentScrollTop;
return scrollTop;
}
//文檔的總高度
const getScrollHeight = () =>{
let scrollHeight = 0, bodyScrollHeight = 0, documentScrollHeight = 0;
if(document.body){
bodyScrollHeight = document.body.scrollHeight;
}
if(document.documentElement){
documentScrollHeight = document.documentElement.scrollHeight;
}
scrollHeight = (bodyScrollHeight - documentScrollHeight > 0) ? bodyScrollHeight : documentScrollHeight;
return scrollHeight;
}
//瀏覽器視口(窗口)的高度
const getWindowHeight = () =>{
let windowHeight = 0;
if(document.compatMode == "CSS1Compat"){
windowHeight = document.documentElement.clientHeight;
}else{
windowHeight = document.body.clientHeight;
}
return windowHeight;
}
return {//模塊返回接口
init: (options) => {
let html = `<ul class="sideMaodian">`+options.navMenu.map((item,index)=>{
return `<li class="nav_maodian ">${item}</li>`;
}).join('')+`</ul>`;
document.body.insertAdjacentHTML('afterbegin',html);
let navItems = document.querySelectorAll(options.navClass);
let secItems = document.querySelectorAll(options.secClass);
let sideMaodian = document.querySelector('.sideMaodian');
if(scrollH===0)
navon(0,navItems);
//利用事件冒泡機制,在點擊的li元素的祖先元素ul標籤上註冊click事件
sideMaodian.addEventListener('click',event=>{
let item = event.target;
let lists = Array.from(sideMaodian.querySelectorAll('li'));
let index = lists.indexOf(item); //li 索引
if(event.target.tagName.toLowerCase() === 'li'){
navon(index,navItems);
toScroll(index,secItems);
}
},false);
//註冊scroll事件
window.addEventListener('scroll',event=>{
event.stopPropagation();
debounce(updateNav(secItems,navItems),1000);
},false);
}
}
}();
var options = {
navClass:'.nav_maodian',
secClass:'.section_maodian',
navMenu:['秒殺專區','買一贈一','品牌齊聚']
}
ScrollAnchorModule.init(options);
</script>
</body>
</html>