原生js實現自定義滾動條組件
本文實例為大家分享了js實現自定義滾動條組件的具體代碼,供大家參考,具體內容如下
功能需求:1、按照數據結構創建菜單內容,顯示在頁面中;2、點擊菜單后,顯示對應的下級菜單內容,如果整體內容溢出,則出現滾動條;3、滾動條的高度要隨著整體內容高度的改變而改變。4、鼠標拖動滾動條,整體內容要隨著向上滾動。5、當鼠標滾動時,滾動條和整體內容也要相應滾動。
來看一下效果:
默認狀態:
點擊菜單,內容溢出后,出現滾動條;
鼠標拖動滾動條,整體內容隨著向上滾動:
分析:
這個案例中包括折疊菜單和滾動條兩個組件 ,所以可以分開來寫,然后整合到一起。 折疊菜單中要考慮多級菜單出現的情況,使用遞歸來做,數據的結構一定要統一,方便對數據進行處理。 滾動條的創建中,有兩個比例等式,一是滾動條的高度/外層div高度=外層div高度/整體內容高度;二是滾動條的位置/(外層div高度-滾動條高度)=內容的scrollTop/(整體內容的高度-外層div高度) 當點擊折疊菜單后,需要相應地設置滾動條的高度。折疊菜單是在Menu.js文件中,滾動條的設置是在ScrollBar.js文件中,需要進行拋發、監聽事件。 監聽菜單鼠標滾動的事件,當鼠標滾動時,判斷滾輪方向,設置滾動條和內容的 top 值,也需要用到事件的拋發和監聽。下面附上代碼:
html結構,模擬數據,創建外層容器:
<!DOCTYPE html><html lang='en'><head> <meta charset='UTF-8'> <meta name='viewport' content='width=device-width, initial-scale=1.0'> <title>scrollBar</title></head><body> <script type='module'> import Utils from ’./js/Utils.js’; import Menu from ’./js/Menu.js’; import ScrollBar from ’./js/ScrollBar.js’; var arr=[ {name:'A',category:[ {name:'奧迪',category:[ {name:'奧迪A3',href:''}, {name:'奧迪A4L',category:[ {name:'奧迪A4L-1',href:''} ]}, {name:'奧迪Q3',href:''}, {name:'奧迪Q5L',href:''}, {name:'奧迪Q2L',href:''}, {name:'奧迪Q7(進口)',href:''}, {name:'奧迪Q8(進口)',href:''}, {name:'奧迪Q7新能源',href:''}, ]}, {name:'阿爾法-羅密歐',category:[ {name:'Stelvio(進口)',href:''}, {name:'Giulia(進口)',href:''}, ]} ]}, {name:'B',category:[ {name:'奔馳',category:[ {name:'奔馳C級',href:''}, {name:'奔馳E級',href:''}, {name:'奔馳GLA級',href:''}, {name:'奔馳GLC級',href:''}, {name:'奔馳A級',href:''}, {name:'奔馳E級(進口)',href:''}, {name:'奔馳A級(進口)',href:''}, {name:'奔馳B級(進口)',href:''}, {name:'威霆',href:''}, {name:'奔馳V級',href:''}, ]}, {name:'寶馬',category:[ {name:'寶馬5系',href:''}, {name:'寶馬1系',href:''}, {name:'寶馬X1',href:''}, {name:'寶馬X5(進口)',href:''}, {name:'寶馬X6(進口)',href:''}, ]}, {name:'本田',category:[ {name:'競瑞',href:''}, {name:'思域',href:''}, {name:'本田CR-V',href:''}, {name:'本田XR-V',href:''}, {name:'本田UR-V',href:''}, {name:'艾力紳',href:''}, {name:'享域',href:''}, {name:'INSPIRE',href:''}, {name:'凌派',href:''}, {name:'雅閣',href:''}, {name:'繽智',href:''}, ]}, {name:'別克',category:[ {name:'凱越',href:''}, {name:'英朗',href:''}, {name:'威朗',href:''}, {name:'閱朗',href:''}, {name:'君威',href:''}, {name:'君越',href:''}, {name:'昂科拉',href:''}, {name:'昂科威',href:''}, {name:'別克GL8',href:''}, {name:'別克GL6',href:''}, {name:'VELITE',href:''}, ]} ]} ] var container; init(); function init(){ createMenu(arr); createScrollBar(); } function createMenu(arr){ //創建菜單 let menu=new Menu(arr); //創建最外層容器 container=Utils.createE('div',{ width:'235px', height:'360px', border:'1px solid #ccc', position:'relative', overflow:'hidden' }) menu.appendTo(container); Utils.appendTo(container,'body') } function createScrollBar(){ //創建滾動條 let scrollBar=new ScrollBar(container); scrollBar.appendTo(container); } </script></body></html>
Menu.js文件,根據數據創建折疊菜單內容:
import Utils from ’./Utils.js’;export default class Menu{ static SET_BAR_HEIGHT='set_bar_height'; static MOUSE_WHEEL_EVENT='mouse_wheel_event'; constructor(_list){ this.elem=this.createElem(_list); } createElem(_list){ if(this.elem) return this.elem; //創建最外層ul容器 let ul=Utils.createE('ul',{ listStyle:'none', padding:'0px', margin:'0px', width:'235px', height:'360px', color:'#333', fontSize:'14px', userSelect: 'none', position:'absolute' }); //創建li列表 this.createMenu(_list,ul); //ul監聽點擊事件 ul.addEventListener('click',e=>this.clickHandler(e)); //ul監聽滾輪事件,火狐使用DOMMouseScroll,其它瀏覽器使用mousewheel ul.addEventListener('mousewheel',e=>this.mouseWheelHandler(e)); ul.addEventListener('DOMMouseScroll',e=>this.mouseWheelHandler(e)); return ul; } appendTo(parent){ Utils.appendTo(this.elem,parent); } //創建一級菜單 createMenu(_list,parent){ for(let i=0;i<_list.length;i++){ let li=Utils.createE('li',{ background:'#f5f5f5', borderTop:'1px solid #ddd', lineHeight:'32px', },{ data:1,//控制一級菜單不能點擊折疊 }) let span=Utils.createE('span',{ marginLeft:'14px', fontSize:'18px' },{ textContent:_list[i].name }) Utils.appendTo(span,li); Utils.appendTo(li,parent); //創建子菜單,第三個參數控制子菜單是否顯示 this.createSubMenu(_list[i].category,li,0); } } //創建子菜單 createSubMenu(_subList,_parent,_index){ //如果沒有子菜單,則跳出 if(_subList.length===0) return; let subUl=Utils.createE('ul',{ listStyle:'none', background:'#fff', padding:'0px', margin:'0px', fontSize:'14px', display:_index===0? 'block' : 'none' }) for(let i=0;i<_subList.length;i++){ let subLi=Utils.createE('li',{ paddingLeft:'40px', position:'relative', cursor:'pointer' }) if(!_subList[i].category){ //如果當前菜單沒有子菜單,則創建a標簽,進行跳轉 let subA=Utils.createE('a',{ color:'#333', textDecoration:'none', width:'100%', display:'inline-block' },{ textContent:_subList[i].name, href:_subList[i].href || 'javascript:void(0)', target:_subList[i].href ? '_blank' : '_self' }) Utils.appendTo(subA,subLi); }else{ //如果當前菜單有子菜單,創建span標簽 let subSpan=Utils.createE('span',{ position:'absolute', left:'20px', top:'8px', border: '1px solid #ccc', display: 'inline-block', width: '10px', height: '10px', lineHeight:'8px' },{ textContent:_subList[i].category.length>0? '+' : '-' }) subLi.textContent=_subList[i].name; Utils.appendTo(subSpan,subLi); } Utils.appendTo(subLi,subUl); //如果當前菜單沒有子菜單,則跳過下面的執行 if(!_subList[i].category) continue; //將當前菜單的子菜單作為參數,進行遞歸 this.createSubMenu(_subList[i].category,subLi,1); } Utils.appendTo(subUl,_parent); } clickHandler(e){ //如果當前點擊的不是li標簽或者span,直接跳出 if(e.target.nodeName!=='LI' && e.target.nodeName!=='SPAN') return; let targ; if(e.target.nodeName==='SPAN') targ=e.target.parentElement; else targ=e.target; //如果當前點擊Li下面沒有子菜單,直接跳出 if(targ.children.length<=1) return; //如果當前點擊的是一級菜單,直接跳出 if(targ.data===1) return; //控制當前點擊的Li下的ul顯示隱藏 if(!targ.bool) targ.lastElementChild.style.display='block'; else targ.lastElementChild.style.display='none'; targ.bool=!targ.bool; //改變span標簽的內容 this.changeSpan(targ); //拋發事件,改變滾動條的高度 var evt=new Event(Menu.SET_BAR_HEIGHT); document.dispatchEvent(evt) } changeSpan(elem){ if(elem.lastElementChild.style.display==='block'){ elem.firstElementChild.textContent='-'; }else{ elem.firstElementChild.textContent='+'; } } mouseWheelHandler(e){ //阻止事件冒泡 e.stopPropagation(); //火狐瀏覽器判斷e.detail,e.detail<0時,表示滾輪往下,頁面往上 let tag=e.detail,wheelDir; //其他瀏覽器判斷e.deltaY,e.deltaY<0時,表示滾輪往下,頁面往上 if(tag===0) tag=e.deltaY; if(tag>0){ //滾輪往下滾動,頁面往上走 wheelDir='down'; }else{ wheelDir='up'; } //拋發事件,將滾輪方向傳遞過去 let evt=new Event(Menu.MOUSE_WHEEL_EVENT); evt.wheelDirection=wheelDir; this.elem.dispatchEvent(evt); }}
ScrollBar.js文件,創建滾動條,對滾動條進行操作:
import Utils from ’./Utils.js’;import Menu from ’./Menu.js’;export default class ScrollBar { bar; conHeight; menuHeight; wheelSpeed=6; barTop=0; static SET_BAR_HEIGHT='set_bar_height'; constructor(parent) { this.container = parent; this.menuUl=this.container.firstElementChild; this.elem = this.createElem(); //偵聽菜單的點擊事件,動態改變滾動條的高度 document.addEventListener(ScrollBar.SET_BAR_HEIGHT,()=>this.setBarHeight()); //ul菜單偵聽滾輪事件 this.menuUl.addEventListener(Menu.MOUSE_WHEEL_EVENT,e=>this.mouseWheelHandler(e)); } createElem() { if (this.elem) return this.elem; //創建滾動條的外層容器 let div = Utils.createE('div', { width: '8px', height: '100%', position: 'absolute', right: '0px', top: '0px', }) this.createBar(div); return div; } appendTo(parent) { Utils.appendTo(this.elem,parent); } createBar(_parent) { if(this.bar) return this.bar; //創建滾動條 this.bar = Utils.createE('div', { width: '100%', position: 'absolute', left: '0px', top: '0px', borderRadius: '10px', backgroundColor: 'rgba(255,0,0,.5)' }) //設置滾動條hover狀態的樣式 this.bar.addEventListener('mouseenter',e=>this.setMouseStateHandler(e)); this.bar.addEventListener('mouseleave',e=>this.setMouseStateHandler(e)); //設置滾動條的高度 this.setBarHeight(); //偵聽鼠標拖動事件 this.mouseHand = e => this.mouseHandler(e); this.bar.addEventListener('mousedown', this.mouseHand); Utils.appendTo(this.bar, _parent); } setBarHeight() { //外層父容器的高度 this.conHeight = this.container.clientHeight; //實際內容的高度 this.menuHeight = this.container.firstElementChild.scrollHeight; //如果實際內容的高度小于父容器的高度,滾動條隱藏 if (this.conHeight >= this.menuHeight) this.bar.style.display = 'none'; else this.bar.style.display = 'block'; //計算滾動條的高度 let h = Math.floor(this.conHeight / this.menuHeight * this.conHeight); this.bar.style.height = h + 'px'; } setMouseStateHandler(e){ //設置滾動條hover狀態的樣式 if(e.type==='mouseenter'){ this.bar.style.backgroundColor='rgba(255,0,0,1)'; }else{ this.bar.style.backgroundColor='rgba(255,0,0,.5)'; } } mouseHandler(e) { switch (e.type) { case 'mousedown': e.preventDefault(); this.y = e.offsetY; document.addEventListener('mousemove', this.mouseHand); document.addEventListener('mouseup', this.mouseHand); break; case 'mousemove': //注意:getBoundingClientRect()返回的結果中,width height 都是包含border的 var rect = this.container.getBoundingClientRect(); this.barTop = e.clientY - rect.y - this.y; //滾動條移動 this.barMove(); break; case 'mouseup': document.removeEventListener('mousemove', this.mouseHand); document.removeEventListener('mouseup', this.mouseHand); break; } } mouseWheelHandler(e){ //滾輪事件 if(e.wheelDirection==='down'){ //滾動往下,菜單內容往上 this.barTop+=this.wheelSpeed; }else{ this.barTop-=this.wheelSpeed; } //滾動條移動 this.barMove(); } barMove(){ if (this.barTop < 0) this.barTop = 0; if (this.barTop > this.conHeight - this.bar.offsetHeight) this.barTop = this.conHeight - this.bar.offsetHeight; this.bar.style.top = this.barTop + 'px'; //菜單內容滾動 this.menuMove(); } menuMove(){ //計算內容的滾動高度 let menuTop=this.barTop/(this.conHeight-this.bar.offsetHeight)*(this.menuHeight-this.conHeight); this.menuUl.style.top=-menuTop+'px'; }}
Utils.js文件,是一個工具包:
export default class Utils{ static createE(elem,style,prep){ elem=document.createElement(elem); if(style) for(let prop in style) elem.style[prop]=style[prop]; if(prep) for(let prop in prep) elem[prop]=prep[prop]; return elem; } static appendTo(elem,parent){ if (parent.constructor === String) parent = document.querySelector(parent); parent.appendChild(elem); } static randomNum(min,max){ return Math.floor(Math.random*(max-min)+min); } static randomColor(alpha){ alpha=alpha||Math.random().toFixed(1); if(isNaN(alpha)) alpha=1; if(alpha>1) alpha=1; if(alpha<0) alpha=0; let col='rgba('; for(let i=0;i<3;i++){ col+=Utils.randomNum(0,256)+','; } col+=alpha+')'; return col; } static insertCss(select,styles){ if(document.styleSheets.length===0){ let styleS=Utils.createE('style'); Utils.appendTo(styleS,document.head); } let styleSheet=document.styleSheets[document.styleSheets.length-1]; let str=select+'{'; for(var prop in styles){ str+=prop.replace(/[A-Z]/g,function(item){ return '-'+item.toLocaleLowerCase(); })+':'+styles[prop]+';'; } str+='}' styleSheet.insertRule(str,styleSheet.cssRules.length); } static getIdElem(elem,obj){ if(elem.id) obj[elem.id]=elem; if(elem.children.length===0) return obj; for(let i=0;i<elem.children.length;i++){ Utils.getIdElem(elem.children[i],obj); } }}
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持好吧啦網。
相關文章:
