首页前端开发JavaScript原生js实现自定义滚动条组件

原生js实现自定义滚动条组件

时间2024-01-31 16:14:03发布访客分类JavaScript浏览468
导读:收集整理的这篇文章主要介绍了原生js实现自定义滚动条组件,觉得挺不错的,现在分享给大家,也给大家做个参考。 本文实例为大家分享了js实现自定义滚动条组件的具体代码,供大家参考,具体内容如...
收集整理的这篇文章主要介绍了原生js实现自定义滚动条组件,觉得挺不错的,现在分享给大家,也给大家做个参考。

本文实例为大家分享了js实现自定义滚动条组件的具体代码,供大家参考,具体内容如下

功能需求:

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

来看一下效果:

默认状态:

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

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

@H_777_29@分析:

  • 这个案例中包括折叠菜单和滚动条两个组件 ,所以可以分开来写,然后整合到一起。
  • 折叠菜单中要考虑多级菜单出现的情况,使用递归来做,数据的结构一定要统一,方便对数据进行处理。
  • 滚动条的创建中,有两个比例等式,一是滚动条的高度/外层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.detail0时,表示滚轮往下,页面往上 let tag=e.detail,wheelDir;
     //其他浏览器判断e.deltaY,e.deltaY0时,表示滚轮往下,页面往上 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(alpha0) alpha=0;
     let col="rgba(";
     for(let i=0;
    i3;
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;
    ielem.children.length;
i++){
      Utils.getIdElem(elem.children[i],obj);
 }
 }
}
    

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

您可能感兴趣的文章:
  • 原生js实现自定义滚动条
  • js实现滚动条自动滚动
  • Vue.js桌面端自定义滚动条组件之美化滚动条VScroll
  • JS自定义滚动条效果
  • JavaScript实现简易聊天对话框(加滚动条)
  • js滚轮事件 js自定义滚动条的实现
  • JS实现滚动条触底加载更多
  • 使用js实现一个简单的滚动条过程解析
  • layer.js open 隐藏滚动条的例子
  • vue 纯js监听滚动条到底部的实例讲解
  • JavaScript 获取滚动条位置并将页面滑动到锚点

声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!

js

若转载请注明出处: 原生js实现自定义滚动条组件
本文地址: https://pptw.com/jishu/594186.html
C语言中continue的作用是什么 c语言冒泡排序怎样实现从大到小

游客 回复需填写必要信息