本文實例為大家分享了js實現自定義滾動條組件的具體代碼,供大家參考,具體內容如下
功能需求:
1、按照數據結構創建菜單內容,顯示在頁面中;
2、點擊菜單后,顯示對應的下級菜單內容,如果整體內容溢出,則出現滾動條;
3、滾動條的高度要隨著整體內容高度的改變而改變。
4、鼠標拖動滾動條,整體內容要隨著向上滾動。
5、當鼠標滾動時,滾動條和整體內容也要相應滾動。
來看一下效果:
默認狀態:
點擊菜單,內容溢出后,出現滾動條;
鼠標拖動滾動條,整體內容隨著向上滾動:
分析:
- 這個案例中包括折疊菜單和滾動條兩個組件 ,所以可以分開來寫,然后整合到一起。
- 折疊菜單中要考慮多級菜單出現的情況,使用遞歸來做,數據的結構一定要統一,方便對數據進行處理。
- 滾動條的創建中,有兩個比例等式,一是滾動條的高度/外層div高度=外層div高度/整體內容高度;二是滾動條的位置/(外層div高度-滾動條高度)=內容的scrollTop/(整體內容的高度-外層div高度)
- 當點擊折疊菜單后,需要相應地設置滾動條的高度。折疊菜單是在Menu.js文件中,滾動條的設置是在ScrollBar.js文件中,需要進行拋發、監聽事件。
- 監聽菜單鼠標滾動的事件,當鼠標滾動時,判斷滾輪方向,設置滾動條和內容的 top 值,也需要用到事件的拋發和監聽。
下面附上代碼:
html結構,模擬數據,創建外層容器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
<!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文件,根據數據創建折疊菜單內容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
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文件,創建滾動條,對滾動條進行操作:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
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文件,是一個工具包:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
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); } } } |
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:https://blog.csdn.net/charissa2017/article/details/104098404