詳細(xì)講解js實現(xiàn)電梯導(dǎo)航的實例
場景
對于某一個頁面內(nèi)容繁多,
如果我們滾動的時間較長,為了增加用戶體驗。
我們需要實現(xiàn)點擊某一個按鈕,然后滾動到對應(yīng)的區(qū)域。
滾動的時候,右側(cè)對應(yīng)的分類實現(xiàn)高亮
其實,這個功能就2個步驟:
1.點擊元素高亮,滾動到對應(yīng)區(qū)域
2.滾動的時候,右側(cè)導(dǎo)航的分類高亮
點擊當(dāng)前元素高亮的實現(xiàn)
1.我們利用事件委托的原理:給被點擊子元素的父元素綁定點擊事件
2.然后移除 li 元素的激活類
3.給當(dāng)前被點擊的子元素添加上激活類
事件委托也稱為事件代理:就是利用事件冒泡,把子元素的事件都綁定到父元素上。
<style>
:root {
--h:931px;
}
*{
padding: 0;
margin: 0;
}
.demo1{
height:var(--h);
background-color: antiquewhite;
}
.demo2{
height:var(--h);
background-color: aqua;
}
.demo3{
height:var(--h);
background-color: blue;
}
.demo4{
height:var(--h);
background-color:chartreuse;
}
.fix-post{
position: fixed;
right: 50px;
bottom: 100px;
width: 200px;
}
li{
height: 40px;
line-height: 40px;
text-align: center;
list-style: none;
border-top: 1px solid #fff;
border-left: 1px solid #fff;
border-right: 1px solid #fff;
}
li:last-child{
border-bottom: 1px solid #fff;
}
.liactive{
background-color: cornsilk;
}
</style>
<body>
<div>
<div class="demo1">1</div>
<div class="demo2">2</div>
<div class="demo3">3</div>
<div class="demo4">4</div>
</div>
<ul class="fix-post" id="nav">
<li class="liactive">1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
</body>
<script>
let domNav= document.getElementById('nav')
let liList=document.querySelectorAll("#nav li")
console.log('liList', liList)
// 給父級元素注冊點擊事件(利用事件委托)
domNav.addEventListener('click',(event)=>{
for(let i=0;i<liList.length;i++){
// 移除所有元素的類名liactive
liList[i].classList.remove('liactive')
}
console.log('event.target', event.target)
// 給當(dāng)前元素添加激活這個類名
event.target.classList.add('liactive');
})
</script>

點擊右側(cè)按鈕,滾動到對應(yīng)區(qū)域
1.給右側(cè)的按鈕添加上索引。
2.點擊索引的時候 * 每個區(qū)域的高度
3.使用window.scrollTo({ })進行滾動
<script>
let domNav= document.getElementById('nav')
let liList=document.querySelectorAll("#nav li")
console.log('liList', liList)
// 給父級元素注冊點擊事件(利用事件委托)
domNav.addEventListener('click',(event)=>{
for(let i=0;i<liList.length;i++){
// 移除所有元素的類名liactive
liList[i].classList.remove('liactive')
// 給右側(cè)的按鈕添加上索引
liList[i]['index']=i
}
console.log('event.target', event.target.index)
// 給當(dāng)前元素添加激活這個類名
event.target.classList.add('liactive');
// 點擊按鈕的時候,滾動到相應(yīng)的區(qū)域
window.scrollTo({
top:event.target.index * 931,
behavior:"smooth" // 平滑的滾動
})
})
</script>

滑動到對應(yīng)區(qū)域右側(cè)按鈕自動高亮
// 實現(xiàn)滾動的時候,右側(cè)區(qū)域自動高亮
window.addEventListener('scroll',()=>{
// 兼容
let top = document.documentElement.scrollTop || document.body.scrollTop
// 獲取當(dāng)前區(qū)域的下標(biāo)
let index = Math.floor(top/931)
console.log('top,index',top,index)
for(let i=0;i<liList.length;i++){
// 移除所有元素的類名liactive
liList[i].classList.remove('liactive')
}
// 給當(dāng)前元素添加激活這個類名
liList[index].classList.add('liactive');
},false)


發(fā)現(xiàn)2個問題
問題1:
出現(xiàn)這個問題的原因是:距離頂部的高度僅僅超過第一層區(qū)域的一點,
這個時候頁面顯示絕大部分區(qū)域是第二層的,
但是右側(cè)按鈕顯示的是當(dāng)前是第1層。
怎么解決這個問題?我們給當(dāng)前區(qū)域手動新增一個高度。
這個高度一般為 3/10
問題2:
我們每次滾動的時候,都在移除元素激活類,然后新增。
這樣不太好,沒有必須要。
我們需要判斷一下
優(yōu)化代碼[每次滾動的時候都在移除元素的激活類]
// 實現(xiàn)滾動的時候,右側(cè)區(qū)域自動高亮
let index=0
window.addEventListener('scroll',()=>{
console.log(222)
// 兼容
let top = (document.documentElement.scrollTop || document.body.scrollTop) + 279 //手動新增一個值
// 如果索引不變,則不取新增或者移除類名
if( Math.floor(top/931) != index){
// 獲取當(dāng)前區(qū)域的下標(biāo)
let index = Math.floor(top/931)
for(let i=0;i<liList.length;i++){
// 移除所有元素的類名liactive
liList[i].classList.remove('liactive')
}
// 給當(dāng)前元素添加激活這個類名
liList[index].classList.add('liactive');
}
},false)
scroll 事件不滾動也會觸發(fā)
我們每次刷新頁面的時候,滾動事件都會被觸發(fā)。
因為:刷新的時候可視區(qū)域距離頂部有距離。所以滾動事件會被觸發(fā);【現(xiàn)象】
這樣就會導(dǎo)致初始化(可視區(qū)域距離頂部有距離)刷新頁面的時候。
右側(cè)的指示燈會切換2次(第一次html上寫激活類,第二次是由于有距離觸發(fā)了滾動事件)。
這樣不太好。我們需要優(yōu)化一下:
刪除html上的激活類。如果距離為0



處理右側(cè)的指示燈在距離頂部有距離的時候,快速切換了2次
let index
let topValue = document.documentElement.scrollTop || document.body.scrollTop
// 距離為0.顯示第一個指示燈
if(topValue==0){
liList[0].classList.add('liactive');
}

scroll 事件特別說明
在 iOS UIWebViews 中,
滾動進行時不會觸發(fā) scroll 事件;
只有當(dāng)滾動結(jié)束后事件才會被觸發(fā)。
參見 Bootstrap issue #16202。Safari 和 WKWebViews 則沒有這個問題。
ps:刷新的時候可視區(qū)域距離頂部有距離。滾動事件也會被觸發(fā);不一定滾動才會觸發(fā)
每個區(qū)域固定高度實現(xiàn)導(dǎo)航【全部代碼】
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
:root {
--h:931px;
}
*{
padding: 0;
margin: 0;
}
.demo1{
height:var(--h);
background-color: antiquewhite;
}
.demo2{
height:var(--h);
background-color: aqua;
}
.demo3{
height:var(--h);
background-color: blue;
}
.demo4{
height:var(--h);
background-color:chartreuse;
}
.fix-post{
position: fixed;
right: 50px;
bottom: 100px;
width: 200px;
}
li{
height: 40px;
line-height: 40px;
text-align: center;
list-style: none;
border-top: 1px solid #fff;
border-left: 1px solid #fff;
border-right: 1px solid #fff;
}
li:last-child{
border-bottom: 1px solid #fff;
}
.liactive{
background-color: cornsilk;
}
</style>
</head>
<body>
<div>
<div class="demo1">1111</div>
<div class="demo2">2</div>
<div class="demo3">3</div>
<div class="demo4">4</div>
</div>
<ul class="fix-post" id="nav">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
</body>
<script>
let domNav= document.getElementById('nav')
let liList=document.querySelectorAll("#nav li")
console.log('liList', liList)
// 給父級元素注冊點擊事件(利用事件委托)
domNav.addEventListener('click',(event)=>{
for(let i=0;i<liList.length;i++){
// 移除所有元素的類名liactive
liList[i].classList.remove('liactive')
// 給右側(cè)的按鈕添加上索引
liList[i]['index']=i
}
console.log('event.target', event.target.index)
// 給當(dāng)前元素添加激活這個類名
event.target.classList.add('liactive');
// 點擊按鈕的時候,滾動到相應(yīng)的區(qū)域
window.scrollTo({
top:event.target.index * 931,
behavior:"smooth" // 平滑的滾動
})
})
let index
let topValue = document.documentElement.scrollTop || document.body.scrollTop
// 離為0.顯示第一個指示燈
if(topValue==0){
liList[0].classList.add('liactive');
}
// 實現(xiàn)滾動的時候,右側(cè)區(qū)域自動高亮
window.addEventListener('scroll',()=>{
console.log('scroll-觸發(fā)')
// 兼容
let top = (document.documentElement.scrollTop || document.body.scrollTop) + 279 //手動新增一個值
// 如果索引不變,則不取新增或者移除類名
if( Math.floor(top/931) != index){
// 獲取當(dāng)前區(qū)域的下標(biāo)
index = Math.floor(top/931)
for(let i=0;i<liList.length;i++){
// 移除所有元素的類名liactive
liList[i].classList.remove('liactive')
}
// 給當(dāng)前元素添加激活這個類名
liList[index].classList.add('liactive');
}
},false)
</script>
</html>
每個區(qū)域高度不一致怎么滾動到對應(yīng)的區(qū)域
雖然我們的滾動可以正確顯示右側(cè)的高亮。
點擊右側(cè)區(qū)域也可以顯示到對應(yīng)的區(qū)域。
但是我們每個區(qū)域的高度是一致的。
在有些情況,每個區(qū)域的高度不一致,
怎么滾動到對應(yīng)的區(qū)域,這個問題怎么處理呢?
我們可以判斷當(dāng)前區(qū)域在哪個區(qū)間。

<body>
<div id="cont">
<div class="demo1">1111</div>
<div class="demo2">2</div>
<div class="demo3">3</div>
<div class="demo4">4</div>
</div>
<ul class="fix-post" id="nav">
<li id="demo1">1</li>
<li id="demo2">2</li>
<li id="demo3">3</li>
<li id="demo4">4</li>
</ul>
</body>
<script>
let contDivList= document.querySelectorAll('#cont div')
let liList=document.querySelectorAll("#nav li")
liList.forEach(link =>{
// 給每個元素注冊點擊事件
link.addEventListener('click',(event)=>{
// 獲取被點擊元素的類名
let currentClickElement= event.target.getAttribute('id')
// 獲取對應(yīng)的區(qū)域元素dom
let currentTargetTop= document.querySelector('.' + currentClickElement)
// 獲取當(dāng)前這個點擊元素的距離頂部的距離
let eleTop= currentTargetTop.offsetTop
// 點擊按鈕的時候,滾動到相應(yīng)的區(qū)域
window.scrollTo({
top:eleTop,
behavior:"smooth" // 平滑的滾動
})
})
})
// 實現(xiàn)滾動的時候,右側(cè)區(qū)域自動高亮
window.addEventListener('scroll',()=>{
let top = window.scrollTop || document.documentElement.scrollTop || document.body.scrollTop;
console.log('top', top)
contDivList.forEach(element => {
// 獲取每個元素距離頂部的距離
const offsetTop = element.offsetTop;
// 獲取每個元素的高度
const offsetHeight = element.offsetHeight;
// 判斷當(dāng)前內(nèi)容區(qū)塊是否在可視范圍內(nèi)
if (top >= offsetTop && top < offsetTop + offsetHeight) {
liList.forEach(function (link) {
if (link.getAttribute('id') === element.getAttribute('class')) {
link.classList.add('liactive');
} else {
link.classList.remove('liactive');
}
});
}
});
},false)
</script>

咋們這種判斷方式有沒有問題?
有的。
const offsetTop = element.offsetTop;
console.log('offsetTop', offsetTop)
// 獲取每個元素的高度
const offsetHeight = element.offsetHeight;
// 判斷當(dāng)前內(nèi)容區(qū)塊是否在可視范圍內(nèi)
if (top >= offsetTop && top < offsetTop + offsetHeight) {
}
這個判斷是不準(zhǔn)確的。容易出問題。
比如說:某一個區(qū)域的高度大于屏幕的可用區(qū)域并且下一個區(qū)域小于上一個區(qū)域的高度。
就可能出現(xiàn)問題。
下一樓層無法正確高亮(在滾動的時候)區(qū)域與高亮區(qū)域不匹配

全部代碼:每個區(qū)域高度不確定導(dǎo)航
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
:root {
--h:931px;
}
*{
padding: 0;
margin: 0;
}
.demo1{
height: 800px;
background-color: antiquewhite;
}
.demo2{
height: 450px;
background-color: aqua;
}
.demo3{
height: 1200px;
background-color: blue;
}
.demo4{
height: 660px;
background-color:chartreuse;
}
.demo5{
height: 1000px;
background-color:rgb(33, 58, 7);
}
.fix-post{
position: fixed;
right: 50px;
bottom: 100px;
width: 200px;
}
li{
height: 40px;
line-height: 40px;
text-align: center;
list-style: none;
border-top: 1px solid #fff;
border-left: 1px solid #fff;
border-right: 1px solid #fff;
}
li:last-child{
border-bottom: 1px solid #fff;
}
.liactive{
background-color: cornsilk;
}
</style>
</head>
<body>
<div id="cont">
<div class="demo1">1111</div>
<div class="demo2">2</div>
<div class="demo3">3</div>
<div class="demo4">4</div>
<div class="demo5">5</div>
</div>
<ul class="fix-post" id="nav">
<li id="demo1">1</li>
<li id="demo2">2</li>
<li id="demo3">3</li>
<li id="demo4">4</li>
<li id="demo5">5</li>
</ul>
</body>
<script>
let contDivList= document.querySelectorAll('#cont div')
let liList=document.querySelectorAll("#nav li")
liList.forEach(link =>{
// 給每個元素注冊點擊事件
link.addEventListener('click',(event)=>{
// 獲取被點擊元素的類名
let currentClickElement= event.target.getAttribute('id')
// 獲取對應(yīng)的區(qū)域元素dom
let currentTargetTop= document.querySelector('.' + currentClickElement)
// 獲取當(dāng)前這個點擊元素的距離頂部的距離
let eleTop= currentTargetTop.offsetTop
// 點擊按鈕的時候,滾動到相應(yīng)的區(qū)域
window.scrollTo({
top:eleTop,
behavior:"smooth" // 平滑的滾動
})
})
})
// 實現(xiàn)滾動的時候,右側(cè)區(qū)域自動高亮
window.addEventListener('scroll',()=>{
let top = window.scrollTop || document.documentElement.scrollTop || document.body.scrollTop;
console.log('top', top)
contDivList.forEach(element => {
// 獲取每個元素距離頂部的距離
const offsetTop = element.offsetTop;
console.log('offsetTop', offsetTop)
// 獲取每個元素的高度
const offsetHeight = element.offsetHeight;
// 判斷當(dāng)前內(nèi)容區(qū)塊是否在可視范圍內(nèi)
if (top >= offsetTop && top < offsetTop + offsetHeight) {
liList.forEach(function (link) {
if (link.getAttribute('id') === element.getAttribute('class')) {
link.classList.add('liactive');
} else {
link.classList.remove('liactive');
}
});
}
});
},false)
</script>
</html>
以上就是詳細(xì)講解js實現(xiàn)電梯導(dǎo)航的實例的詳細(xì)內(nèi)容,更多關(guān)于js實現(xiàn)電梯導(dǎo)航的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
js+canvas實現(xiàn)繪制正方形并插入文字效果(居中顯示)
canvas是一個可以讓我們使用腳本繪圖的標(biāo)簽,它提供了一系列完整的屬性和方法,下面這篇文章主要給大家介紹了js+canvas實現(xiàn)繪制正方形并插入文字居中顯示效果的相關(guān)資料,需要的朋友可以參考下2023-11-11
asp.net+js 實現(xiàn)無刷新上傳解析csv文件的代碼
無刷新上傳解析csv文件的實現(xiàn)代碼,需要的朋友可以參考下。2010-05-05
php顯示當(dāng)前文件所在的文件以及文件夾所有文件以樹形展開
本文為大家介紹喜愛php如何顯示當(dāng)前文件所在的文件以及文件夾所有文件并以樹形展開,感興趣的朋友可以參考下2013-12-12
原生javascript實現(xiàn)DIV拖拽并計算重復(fù)面積
這篇文章主要介紹了使用原生javascript實現(xiàn)DIV拖拽并計算重復(fù)面積的方法及示例代碼分享,效果十分漂亮,需要的朋友可以參考下2015-01-01

