• 售前

  • 售后

热门帖子
入门百科

原生js实现自界说滚动条组件

[复制链接]
123457441 显示全部楼层 发表于 2021-10-25 19:25:38 |阅读模式 打印 上一主题 下一主题
本文实例为大家分享了js实现自界说滚动条组件的具体代码,供大家参考,具体内容如下
功能需求:

1、按照数据布局创建菜单内容,显示在页面中;
2、点击菜单后,显示对应的下级菜单内容,假如团体内容溢出,则出现滚动条;
3、滚动条的高度要随着团体内容高度的改变而改变。
4、鼠标拖动滚动条,团体内容要随着向上滚动。
5、当鼠标滚动时,滚动条和团体内容也要相应滚动。

来看一下效果:
默认状态:

点击菜单,内容溢出后,出现滚动条;

鼠标拖动滚动条,团体内容随着向上滚动:

分析:
       
  • 这个案例中包罗折叠菜单和滚动条两个组件 ,所以可以分开来写,然后整合到一起。   
  • 折叠菜单中要思量多级菜单出现的情况,使用递归来做,数据的布局一定要同一,方便对数据举行处理惩罚。   
  • 滚动条的创建中,有两个比例等式,一是滚动条的高度/外层div高度=外层div高度/团体内容高度;二是滚动条的位置/(外层div高度-滚动条高度)=内容的scrollTop/(团体内容的高度-外层div高度)   
  • 当点击折叠菜单后,必要相应地设置滚动条的高度。折叠菜单是在Menu.js文件中,滚动条的设置是在ScrollBar.js文件中,必要举行抛发、监听变乱。   
  • 监听菜单鼠标滚动的变乱,当鼠标滚动时,判断滚轮方向,设置滚动条和内容的 top 值,也必要用到变乱的抛发和监听。
下面附上代码:
html布局,模仿数据,创建外层容器:
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>scrollBar</title>
  7. </head>
  8. <body>
  9. <script type="module">
  10. import Utils from './js/Utils.js';
  11. import Menu from './js/Menu.js';
  12. import ScrollBar from './js/ScrollBar.js';
  13. var arr=[
  14.   {name:"A",category:[
  15.   {name:"奥迪",category:[
  16.    {name:"奥迪A3",href:""},
  17.    {name:"奥迪A4L",category:[
  18.    {name:"奥迪A4L-1",href:""}
  19.    ]},
  20.    {name:"奥迪Q3",href:""},
  21.    {name:"奥迪Q5L",href:""},
  22.    {name:"奥迪Q2L",href:""},
  23.    {name:"奥迪Q7(进口)",href:""},
  24.    {name:"奥迪Q8(进口)",href:""},
  25.    {name:"奥迪Q7新能源",href:""},
  26.   ]},
  27.   {name:"阿尔法-罗密欧",category:[
  28.    {name:"Stelvio(进口)",href:""},
  29.    {name:"Giulia(进口)",href:""},
  30.   ]}
  31.   ]},
  32.   {name:"B",category:[
  33.   {name:"奔驰",category:[
  34.    {name:"奔驰C级",href:""},
  35.    {name:"奔驰E级",href:""},
  36.    {name:"奔驰GLA级",href:""},
  37.    {name:"奔驰GLC级",href:""},
  38.    {name:"奔驰A级",href:""},
  39.    {name:"奔驰E级(进口)",href:""},
  40.    {name:"奔驰A级(进口)",href:""},
  41.    {name:"奔驰B级(进口)",href:""},
  42.    {name:"威霆",href:""},
  43.    {name:"奔驰V级",href:""},
  44.   ]},
  45.   {name:"宝马",category:[
  46.    {name:"宝马5系",href:""},
  47.    {name:"宝马1系",href:""},
  48.    {name:"宝马X1",href:""},
  49.    {name:"宝马X5(进口)",href:""},
  50.    {name:"宝马X6(进口)",href:""},
  51.   ]},
  52.   {name:"本田",category:[
  53.    {name:"竞瑞",href:""},
  54.    {name:"思域",href:""},
  55.    {name:"本田CR-V",href:""},
  56.    {name:"本田XR-V",href:""},
  57.    {name:"本田UR-V",href:""},
  58.    {name:"艾力绅",href:""},
  59.    {name:"享域",href:""},
  60.    {name:"INSPIRE",href:""},
  61.    {name:"凌派",href:""},
  62.    {name:"雅阁",href:""},
  63.    {name:"缤智",href:""},
  64.   ]},
  65.   {name:"别克",category:[
  66.    {name:"凯越",href:""},
  67.    {name:"英朗",href:""},
  68.    {name:"威朗",href:""},
  69.    {name:"阅朗",href:""},
  70.    {name:"君威",href:""},
  71.    {name:"君越",href:""},
  72.    {name:"昂科拉",href:""},
  73.    {name:"昂科威",href:""},
  74.    {name:"别克GL8",href:""},
  75.    {name:"别克GL6",href:""},
  76.    {name:"VELITE",href:""},
  77.   ]}
  78.   ]}
  79. ]
  80. var container;
  81. init();
  82. function init(){
  83.   createMenu(arr);  
  84.   createScrollBar();
  85. }
  86.   function createMenu(arr){
  87.   //创建菜单
  88.   let menu=new Menu(arr);
  89.   //创建最外层容器
  90.   container=Utils.createE("div",{
  91.   width:"235px",
  92.   height:"360px",
  93.   border:"1px solid #ccc",
  94.   position:"relative",
  95.   overflow:"hidden"
  96.   })
  97.   menu.appendTo(container);
  98.   Utils.appendTo(container,"body")
  99. }
  100. function createScrollBar(){
  101.   //创建滚动条
  102.   let scrollBar=new ScrollBar(container);
  103.   scrollBar.appendTo(container);
  104. }
  105. </script>
  106. </body>
  107. </html>
复制代码
Menu.js文件,根据数据创建折叠菜单内容:
  1. import Utils from './Utils.js';
  2. export default class Menu{
  3. static SET_BAR_HEIGHT="set_bar_height";
  4. static MOUSE_WHEEL_EVENT="mouse_wheel_event";
  5. constructor(_list){
  6. this.elem=this.createElem(_list);
  7. }
  8. createElem(_list){
  9. if(this.elem) return this.elem;
  10. //创建最外层ul容器
  11. let ul=Utils.createE("ul",{
  12.   listStyle:"none",
  13.   padding:"0px",
  14.   margin:"0px",
  15.   width:"235px",
  16.   height:"360px",
  17.   color:"#333",
  18.   fontSize:"14px",
  19.   userSelect: "none",
  20.   position:"absolute"
  21. });
  22. //创建li列表
  23. this.createMenu(_list,ul);
  24. //ul监听点击事件
  25. ul.addEventListener("click",e=>this.clickHandler(e));
  26. //ul监听滚轮事件,火狐使用DOMMouseScroll,其它浏览器使用mousewheel
  27. ul.addEventListener("mousewheel",e=>this.mouseWheelHandler(e));
  28. ul.addEventListener("DOMMouseScroll",e=>this.mouseWheelHandler(e));
  29. return ul;
  30. }
  31. appendTo(parent){
  32. Utils.appendTo(this.elem,parent);
  33. }
  34. //创建一级菜单
  35. createMenu(_list,parent){
  36. for(let i=0;i<_list.length;i++){
  37.   let li=Utils.createE("li",{
  38.   background:"#f5f5f5",
  39.   borderTop:"1px solid #ddd",
  40.   lineHeight:"32px",
  41.   },{
  42.   data:1,//控制一级菜单不能点击折叠
  43.   })
  44.   let span=Utils.createE("span",{
  45.   marginLeft:"14px",
  46.   fontSize:"18px"
  47.   },{
  48.   textContent:_list[i].name
  49.   })
  50.   Utils.appendTo(span,li);
  51.   Utils.appendTo(li,parent);
  52.   //创建子菜单,第三个参数控制子菜单是否显示
  53.   this.createSubMenu(_list[i].category,li,0);
  54. }
  55. }
  56. //创建子菜单
  57. createSubMenu(_subList,_parent,_index){
  58. //如果没有子菜单,则跳出
  59. if(_subList.length===0) return;
  60. let subUl=Utils.createE("ul",{
  61.   listStyle:"none",
  62.   background:"#fff",
  63.   padding:"0px",
  64.   margin:"0px",
  65.   fontSize:"14px",
  66.   display:_index===0? "block" : "none"
  67. })
  68. for(let i=0;i<_subList.length;i++){
  69.   let subLi=Utils.createE("li",{
  70.   paddingLeft:"40px",
  71.   position:"relative",
  72.   cursor:"pointer"
  73.   })
  74.   if(!_subList[i].category){
  75.   //如果当前菜单没有子菜单,则创建a标签,进行跳转
  76.   let subA=Utils.createE("a",{
  77.    color:"#333",
  78.    textDecoration:"none",
  79.    width:"100%",
  80.    display:"inline-block"
  81.   },{
  82.    textContent:_subList[i].name,
  83.    href:_subList[i].href || "javascript:void(0)",
  84.    target:_subList[i].href ? "_blank" : "_self"
  85.   })
  86.   Utils.appendTo(subA,subLi);
  87.   }else{
  88.   //如果当前菜单有子菜单,创建span标签
  89.   let subSpan=Utils.createE("span",{
  90.    position:"absolute",
  91.    left:"20px",
  92.    top:"8px",
  93.    border: "1px solid #ccc",
  94.    display: "inline-block",
  95.    width: "10px",
  96.    height: "10px",
  97.    lineHeight:"8px"
  98.   },{
  99.    textContent:_subList[i].category.length>0? "+" : "-"
  100.   })
  101.   subLi.textContent=_subList[i].name;
  102.   Utils.appendTo(subSpan,subLi);
  103.   }
  104.   Utils.appendTo(subLi,subUl);
  105.   //如果当前菜单没有子菜单,则跳过下面的执行
  106.   if(!_subList[i].category) continue;
  107.   //将当前菜单的子菜单作为参数,进行递归
  108.   this.createSubMenu(_subList[i].category,subLi,1);
  109. }
  110. Utils.appendTo(subUl,_parent);
  111. }
  112. clickHandler(e){
  113. //如果当前点击的不是li标签或者span,直接跳出
  114. if(e.target.nodeName!=="LI" && e.target.nodeName!=="SPAN") return;
  115. let targ;
  116. if(e.target.nodeName==="SPAN") targ=e.target.parentElement;
  117. else targ=e.target;
  118. //如果当前点击Li下面没有子菜单,直接跳出
  119. if(targ.children.length<=1) return;
  120. //如果当前点击的是一级菜单,直接跳出
  121. if(targ.data===1) return;
  122. //控制当前点击的Li下的ul显示隐藏
  123. if(!targ.bool) targ.lastElementChild.style.display="block";
  124. else targ.lastElementChild.style.display="none";
  125. targ.bool=!targ.bool;
  126. //改变span标签的内容
  127. this.changeSpan(targ);
  128. //抛发事件,改变滚动条的高度
  129. var evt=new Event(Menu.SET_BAR_HEIGHT);
  130. document.dispatchEvent(evt)
  131. }
  132. changeSpan(elem){
  133. if(elem.lastElementChild.style.display==="block"){
  134.   elem.firstElementChild.textContent="-";
  135. }else{
  136.   elem.firstElementChild.textContent="+";
  137. }
  138. }
  139. mouseWheelHandler(e){
  140. //阻止事件冒泡
  141. e.stopPropagation();
  142. //火狐浏览器判断e.detail,e.detail<0时,表示滚轮往下,页面往上
  143. let tag=e.detail,wheelDir;
  144. //其他浏览器判断e.deltaY,e.deltaY<0时,表示滚轮往下,页面往上
  145. if(tag===0) tag=e.deltaY;
  146. if(tag>0){
  147.   //滚轮往下滚动,页面往上走
  148.   wheelDir="down";
  149. }else{
  150.   wheelDir="up";
  151. }
  152. //抛发事件,将滚轮方向传递过去
  153. let evt=new Event(Menu.MOUSE_WHEEL_EVENT);
  154. evt.wheelDirection=wheelDir;
  155. this.elem.dispatchEvent(evt);
  156. }
  157. }
复制代码
ScrollBar.js文件,创建滚动条,对滚动条举行操纵:
  1. import Utils from './Utils.js';
  2. import Menu from './Menu.js';
  3. export default class ScrollBar {
  4. bar;
  5. conHeight;
  6. menuHeight;
  7. wheelSpeed=6;
  8. barTop=0;
  9. static SET_BAR_HEIGHT="set_bar_height";
  10. constructor(parent) {
  11. this.container = parent;
  12. this.menuUl=this.container.firstElementChild;
  13. this.elem = this.createElem();
  14. //侦听菜单的点击事件,动态改变滚动条的高度
  15. document.addEventListener(ScrollBar.SET_BAR_HEIGHT,()=>this.setBarHeight());
  16. //ul菜单侦听滚轮事件
  17. this.menuUl.addEventListener(Menu.MOUSE_WHEEL_EVENT,e=>this.mouseWheelHandler(e));
  18. }
  19. createElem() {
  20. if (this.elem) return this.elem;
  21. //创建滚动条的外层容器
  22. let div = Utils.createE("div", {
  23.   width: "8px",
  24.   height: "100%",
  25.   position: "absolute",
  26.   right: "0px",
  27.   top: "0px",
  28. })
  29. this.createBar(div);
  30. return div;
  31. }
  32. appendTo(parent) {
  33. Utils.appendTo(this.elem,parent);
  34. }
  35. createBar(_parent) {
  36. if(this.bar) return this.bar;
  37. //创建滚动条
  38. this.bar = Utils.createE("div", {
  39.   width: "100%",
  40.   position: "absolute",
  41.   left: "0px",
  42.   top: "0px",
  43.   borderRadius: "10px",
  44.   backgroundColor: "rgba(255,0,0,.5)"
  45. })
  46. //设置滚动条hover状态的样式
  47. this.bar.addEventListener("mouseenter",e=>this.setMouseStateHandler(e));
  48. this.bar.addEventListener("mouseleave",e=>this.setMouseStateHandler(e));
  49. //设置滚动条的高度
  50. this.setBarHeight();
  51. //侦听鼠标拖动事件
  52. this.mouseHand = e => this.mouseHandler(e);
  53. this.bar.addEventListener("mousedown", this.mouseHand);
  54. Utils.appendTo(this.bar, _parent);
  55. }
  56. setBarHeight() {
  57. //外层父容器的高度
  58. this.conHeight = this.container.clientHeight;
  59. //实际内容的高度
  60. this.menuHeight = this.container.firstElementChild.scrollHeight;
  61. //如果实际内容的高度小于父容器的高度,滚动条隐藏
  62. if (this.conHeight >= this.menuHeight) this.bar.style.display = "none";
  63. else this.bar.style.display = "block";
  64. //计算滚动条的高度
  65. let h = Math.floor(this.conHeight / this.menuHeight * this.conHeight);
  66. this.bar.style.height = h + "px";
  67. }
  68. setMouseStateHandler(e){
  69. //设置滚动条hover状态的样式
  70. if(e.type==="mouseenter"){
  71.   this.bar.style.backgroundColor="rgba(255,0,0,1)";
  72. }else{
  73.   this.bar.style.backgroundColor="rgba(255,0,0,.5)";
  74. }
  75. }
  76. mouseHandler(e) {
  77. switch (e.type) {
  78.   case "mousedown":
  79.   e.preventDefault();
  80.   this.y = e.offsetY;
  81.   document.addEventListener("mousemove", this.mouseHand);
  82.   document.addEventListener("mouseup", this.mouseHand);
  83.   break;
  84.   case "mousemove":
  85.   //注意:getBoundingClientRect()返回的结果中,width height 都是包含border的
  86.   var rect = this.container.getBoundingClientRect();
  87.   this.barTop = e.clientY - rect.y - this.y;
  88.   //滚动条移动
  89.   this.barMove();
  90.   break;
  91.   case "mouseup":
  92.   document.removeEventListener("mousemove", this.mouseHand);
  93.   document.removeEventListener("mouseup", this.mouseHand);
  94.   break;
  95. }
  96. }
  97. mouseWheelHandler(e){
  98. //滚轮事件
  99. if(e.wheelDirection==="down"){
  100.   //滚动往下,菜单内容往上
  101.   this.barTop+=this.wheelSpeed;
  102. }else{
  103.   this.barTop-=this.wheelSpeed;
  104. }
  105. //滚动条移动
  106. this.barMove();
  107. }
  108. barMove(){
  109. if (this.barTop < 0) this.barTop = 0;
  110. if (this.barTop > this.conHeight - this.bar.offsetHeight) this.barTop = this.conHeight - this.bar.offsetHeight;
  111. this.bar.style.top = this.barTop + "px";
  112. //菜单内容滚动
  113. this.menuMove();
  114. }
  115. menuMove(){
  116. //计算内容的滚动高度
  117. let menuTop=this.barTop/(this.conHeight-this.bar.offsetHeight)*(this.menuHeight-this.conHeight);
  118. this.menuUl.style.top=-menuTop+"px";
  119. }
  120. }
复制代码
Utils.js文件,是一个工具包:
  1. export default class Utils{
  2. static createE(elem,style,prep){
  3. elem=document.createElement(elem);
  4. if(style) for(let prop in style) elem.style[prop]=style[prop];
  5. if(prep) for(let prop in prep) elem[prop]=prep[prop];
  6. return elem;
  7. }
  8. static appendTo(elem,parent){
  9. if (parent.constructor === String) parent = document.querySelector(parent);
  10. parent.appendChild(elem);
  11. }
  12. static randomNum(min,max){
  13. return Math.floor(Math.random*(max-min)+min);
  14. }
  15. static randomColor(alpha){
  16. alpha=alpha||Math.random().toFixed(1);
  17. if(isNaN(alpha)) alpha=1;
  18. if(alpha>1) alpha=1;
  19. if(alpha<0) alpha=0;
  20. let col="rgba(";
  21. for(let i=0;i<3;i++){
  22.   col+=Utils.randomNum(0,256)+",";
  23. }
  24. col+=alpha+")";
  25. return col;
  26. }
  27. static insertCss(select,styles){
  28. if(document.styleSheets.length===0){
  29.   let styleS=Utils.createE("style");
  30.   Utils.appendTo(styleS,document.head);
  31. }
  32. let styleSheet=document.styleSheets[document.styleSheets.length-1];
  33. let str=select+"{";
  34. for(var prop in styles){
  35.   str+=prop.replace(/[A-Z]/g,function(item){
  36.   return "-"+item.toLocaleLowerCase();
  37.   })+":"+styles[prop]+";";
  38. }
  39. str+="}"
  40. styleSheet.insertRule(str,styleSheet.cssRules.length);
  41. }
  42. static getIdElem(elem,obj){
  43. if(elem.id) obj[elem.id]=elem;
  44. if(elem.children.length===0) return obj;
  45. for(let i=0;i<elem.children.length;i++){
  46.   Utils.getIdElem(elem.children[i],obj);
  47. }
  48. }
  49. }
复制代码
以上就是本文的全部内容,盼望对大家的学习有所资助,也盼望大家多多支持脚本之家。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

帖子地址: 

回复

使用道具 举报

分享
推广
火星云矿 | 预约S19Pro,享500抵1000!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

草根技术分享(草根吧)是全球知名中文IT技术交流平台,创建于2021年,包含原创博客、精品问答、职业培训、技术社区、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区。
  • 官方手机版

  • 微信公众号

  • 商务合作