詳細講解js實現(xiàn)電梯導航的實例
場景
對于某一個頁面內(nèi)容繁多,
如果我們滾動的時間較長,為了增加用戶體驗。
我們需要實現(xiàn)點擊某一個按鈕,然后滾動到對應(yīng)的區(qū)域。
滾動的時候,右側(cè)對應(yīng)的分類實現(xiàn)高亮
其實,這個功能就2個步驟:
1.點擊元素高亮,滾動到對應(yīng)區(qū)域
2.滾動的時候,右側(cè)導航的分類高亮
點擊當前元素高亮的實現(xiàn)
1.我們利用事件委托的原理:給被點擊子元素的父元素綁定點擊事件
2.然后移除 li 元素的激活類
3.給當前被點擊的子元素添加上激活類
事件委托也稱為事件代理:就是利用事件冒泡,把子元素的事件都綁定到父元素上。
<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) // 給當前元素添加激活這個類名 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) // 給當前元素添加激活這個類名 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 // 獲取當前區(qū)域的下標 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') } // 給當前元素添加激活這個類名 liList[index].classList.add('liactive'); },false)
發(fā)現(xiàn)2個問題
問題1:
出現(xiàn)這個問題的原因是:距離頂部的高度僅僅超過第一層區(qū)域的一點,
這個時候頁面顯示絕大部分區(qū)域是第二層的,
但是右側(cè)按鈕顯示的是當前是第1層。
怎么解決這個問題?我們給當前區(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){ // 獲取當前區(qū)域的下標 let index = Math.floor(top/931) for(let i=0;i<liList.length;i++){ // 移除所有元素的類名liactive liList[i].classList.remove('liactive') } // 給當前元素添加激活這個類名 liList[index].classList.add('liactive'); } },false)
scroll 事件不滾動也會觸發(fā)
我們每次刷新頁面的時候,滾動事件都會被觸發(fā)。
因為:刷新的時候可視區(qū)域距離頂部有距離。所以滾動事件會被觸發(fā);【現(xiàn)象】
這樣就會導致初始化(可視區(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 事件;
只有當滾動結(jié)束后事件才會被觸發(fā)。
參見 Bootstrap issue #16202。Safari 和 WKWebViews 則沒有這個問題。
ps:刷新的時候可視區(qū)域距離頂部有距離。滾動事件也會被觸發(fā);不一定滾動才會觸發(fā)
每個區(qū)域固定高度實現(xiàn)導航【全部代碼】
<!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) // 給當前元素添加激活這個類名 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){ // 獲取當前區(qū)域的下標 index = Math.floor(top/931) for(let i=0;i<liList.length;i++){ // 移除所有元素的類名liactive liList[i].classList.remove('liactive') } // 給當前元素添加激活這個類名 liList[index].classList.add('liactive'); } },false) </script> </html>
每個區(qū)域高度不一致怎么滾動到對應(yīng)的區(qū)域
雖然我們的滾動可以正確顯示右側(cè)的高亮。
點擊右側(cè)區(qū)域也可以顯示到對應(yīng)的區(qū)域。
但是我們每個區(qū)域的高度是一致的。
在有些情況,每個區(qū)域的高度不一致,
怎么滾動到對應(yīng)的區(qū)域,這個問題怎么處理呢?
我們可以判斷當前區(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) // 獲取當前這個點擊元素的距離頂部的距離 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; // 判斷當前內(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; // 判斷當前內(nèi)容區(qū)塊是否在可視范圍內(nèi) if (top >= offsetTop && top < offsetTop + offsetHeight) { }
這個判斷是不準確的。容易出問題。
比如說:某一個區(qū)域的高度大于屏幕的可用區(qū)域并且下一個區(qū)域小于上一個區(qū)域的高度。
就可能出現(xiàn)問題。
下一樓層無法正確高亮(在滾動的時候)區(qū)域與高亮區(qū)域不匹配
全部代碼:每個區(qū)域高度不確定導航
<!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) // 獲取當前這個點擊元素的距離頂部的距離 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; // 判斷當前內(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>
以上就是詳細講解js實現(xiàn)電梯導航的實例的詳細內(nèi)容,更多關(guān)于js實現(xiàn)電梯導航的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Bootstrap CSS組件之分頁(pagination)和翻頁(pager)
這篇文章主要介為大家詳細紹了Bootstrap CSS組件之分頁和翻頁的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-12-12用JavaScript實現(xiàn)一個代碼簡潔、邏輯不復雜的多級樹
這篇文章主要介紹了用JavaScript實現(xiàn)一個代碼簡潔、邏輯不復雜的多級樹,需要的朋友可以參考下2014-05-05javascript Three.js創(chuàng)建文字初體驗
這篇文章主要為大家介紹了Three.js創(chuàng)建文字初體驗,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2021-11-11