zoukankan      html  css  js  c++  java
  • JavaScript实现竖向滚动条的一种思路

    设计目标:希望复刻浏览器原生竖向滚动条的功能,并且能做一些个性化配置

    测试页面:

     1 <!DOCTYPE html>
     2 <html lang="en">
     3 <head>
     4     <meta charset="UTF-8">
     5     <title>Title</title>
     6     <script src="MyScrolly.js"></script>
     7 </head>
     8 <body>
     9 <div id="div_allbase" style=" 800px;height: 600px;background-color: beige;overflow-y: hidden">
    10     <div id="div_outer" style=" 700px;height: 500px;background-color: cornflowerblue;overflow-y: auto">
    11         <div id="div_inner1" style=" 600px;height: 300px;background-color: darkseagreen">
    12 111111111111111111111111111111111111111111111111111111111111111
    13         </div>
    14         <div id="div_inner2" style=" 600px;height: 300px;margin-top: 50px;background-color: darkseagreen"">
    15 222222222222222222222222222222222222222222222222222222222222222
    16         </div>
    17         <div id="div_inner3" style=" 600px;height: 300px;margin-top: 50px;background-color: darkseagreen"">
    18 333333333333333333333333333333333333333333333333333333333333333
    19         </div>
    20     </div>
    21 </div>
    22 </body>
    23 <script>
    24     var myScroll=new MyScrolly(document.getElementById("div_outer")//要添加滚动条的元素
    25         ,{parentelem:document.getElementById("div_allbase")})//配置参数
    26     myScroll.func_count(myScroll);//在innerHTML发生变化或onresize之后重新计算dragbar的长度
        //使用这种方式可以为页面中的多个元素配置不同的滚动条样式
    27 </script> 28 </html>

    代码实现:

      1 function MyScrolly(elem,obj_p)
      2 {
      3     if(elem)
      4     {
      5         obj_p=obj_p||{};
      6         this.elem=elem;
      7         this.func_render=obj_p.func_render||MyScrolly.defaultRender;//滚动条的dom结构
      8         this.func_count=obj_p.func_count||MyScrolly.countChildren;//在页面发生变化时滚动条的变化方式
      9         this.func_render(elem,this);
     10         this.parentelem=obj_p.parentelem||elem;//支持拖拽、释放、禁止选择等动作的外围元素范围,默认设置为document可能效果更好
     11         //this.clientY0=this.elem.clientY;//没有用到
     12         this.last_clientY=-1;
     13         //elem.onload
     14         var that=this;
     15         //使用页面观察器观察dom变化!!<-兼容性如何??<-html5,并且会导致this被替换为MutationObserver对象,并且不好控制调用条件
     16         // var MutationObserver=window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver;
     17         // //var mo=new MutationObserver(that.countChildren);
     18         // var mo=new MutationObserver(function(records){
     19         //     that.func_count(that);
     20         // });
     21         // this.mo=mo;
     22         // var option={
     23         //     childList:true,
     24         //     subtree:true,
     25         // }
     26         //mo.observe(this.elem,option);
     27 
     28         this.div2.onpointerdown=function (event) {//按下scrollbar
     29             that.picked=true;
     30             that.last_clientY=event.clientY;//取相对定位的参考点
     31             that.parentelem.onselectstart=function(event){//防止在上下滑动时选中div中的文本
     32                 event.returnValue=false;
     33                 return false;
     34             }
     35         }
     36         this.parentelem.onpointerup=function (event) {
     37             that.picked=false;
     38             that.parentelem.onselectstart=null;
     39             //that.parentelem.onmousewheel=null;
     40         }
     41         this.parentelem.onpointerleave=function (event) {
     42             that.picked=false;
     43             that.parentelem.onselectstart=null;
     44             that.parentelem.onmousewheel=null;
     45         }
     46         this.parentelem.onpointermove=function (event) {//拖动效果在div_allbase范围内均有效
     47             if(that.picked==true&&(that.last_clientY>=0))
     48             {
     49                 //event.preventDefault();//阻止默认的行为发生
     50 
     51                 var int_clientY=event.clientY-that.last_clientY;//因为比较难定位elem的初始位置(也许elem自身会发生移动或变形),这里使用相对变化量
     52                 that.last_clientY=event.clientY;
     53                 var top_div2=parseInt(that.div2.style.top);//滑块上端到滑轨上端的距离,关于div2等属性的含义见defaultRender方法
     54                 var int1=top_div2+int_clientY;
     55                 if((that.outer_height-that.height_scrollbar)<int1)
     56                 {//如果过于靠下
     57                     int1=that.outer_height-that.height_scrollbar
     58                 }
     59                 if(int1<0)
     60                 {//如果过于靠上
     61                     int1=0
     62                 }
     63 
     64                 // if((that.outer_height-that.height_scrollbar)>=(top_div2+int_clientY)&&((top_div2+int_clientY)>=0))
     65                 // {
     66                     that.div2.style.top=int1+"px";//移动滑块
     67                     var int2=(int1)/(that.outer_height/that.inner_height)
     68                     that.elem.scrollTop=int2;//滚动元素内容
     69                     that.div1.style.top=int2+"px";//移动滑轨
     70                     //console.log(that.elem.scrollTop);
     71                // }
     72 
     73 
     74             }
     75         }
     76         //this.parentelem.onclick=function(event){
     77         this.parentelem.onmouseenter=function(event){//鼠标滚轮,这里没有兼容火狐
     78             that.parentelem.onmousewheel=function(event){
     79                 if(that.last_clientY<0)
     80                 {
     81                     that.last_clientY=0;
     82                 }
     83                 if((that.last_clientY>=0))
     84                 {
     85                     var int_clientY=-event.wheelDelta;
     86                     var top_div2=parseInt(that.div2.style.top);
     87                     var int1=top_div2+int_clientY;
     88                     if((that.outer_height-that.height_scrollbar)<int1)
     89                     {
     90                         int1=that.outer_height-that.height_scrollbar
     91                     }
     92                     if(int1<0)
     93                     {
     94                         int1=0
     95                     }
     96 
     97                         that.div2.style.top=int1+"px";
     98                         var int2=(int1)/(that.outer_height/that.inner_height)
     99                         that.elem.scrollTop=int2;
    100                         that.div1.style.top=int2+"px";
    101                         //console.log(that.elem.scrollTop);
    102 
    103                 }
    104             }
    105         }
    106 
    107     }
    108     else {
    109         return false;
    110     }
    111 
    112 
    113 }
    114 //MyScrolly.prototype
    115 //计算容器内部组件的实际高度,并就此调整滚动条显示效果
    116 //MyScrolly.prototype.countChildren=function(records){
    117 MyScrolly.countChildren=function(that){
    118     //this=that;
    119     var arr=that.elem.childNodes;//如果使用MutationObserver,则这里的this是MutationObserver对象!!
    120     var len=arr.length;
    121     var sum_height=0;
    122     // for(var i=0;i<len;i++)//假设除了滚动条之外的所有元素都是纵向排列的!!《-这里需要递归排列!!??
    123     // {累加元素内部children的高度
    124     //     var obj=arr[i];
    125     //     if(obj.className!="div_myscroll1")
    126     //     {
    127     //         var int=obj.offsetHeight;
    128     //         if(int)//有些textnode的高度可能是undefined!!
    129     //         {
    130     //             sum_height+=int;
    131     //         }
    132     //
    133     //     }
    134     // }
    135     //考虑到margin,换一种测量思路
    136     for(var i=len-1;i>0;i--)
    137     {
    138         var obj=arr[i];
    139         if(obj.className!="div_myscroll1")
    140         {
    141             var int=obj.offsetHeight;
    142             if(int)//有些textnode的高度可能是undefined!!
    143             {
    144                 sum_height+=int;
    145                 sum_height+=obj.offsetTop;
    146                 break;
    147             }
    148 
    149         }
    150     }
    151     that.inner_height=sum_height;//元素内容高度
    152     that.outer_height=that.elem.offsetHeight;//元素本身高度
    153     console.log("重新测量高度"+that.outer_height+"/"+that.inner_height);
    154     that.div2.style.top="0px";//滑块复位
    155     that.elem.scrollTop=0;
    156     that.clientY0=0;
    157     that.picked=false;
    158     that.last_clientY=-1;//这里还应该加上取消监听的代码
    159     if(that.inner_height<=that.outer_height)//如果不需要显示滚动条
    160     {
    161         that.div1.style.display="none";
    162     }
    163     else {
    164         that.div1.style.display="block";
    165         var int=that.outer_height*(that.outer_height/that.inner_height);
    166         that.height_scrollbar=int;
    167         that.div2.style.height=int+"px";
    168     }
    169 }
    170 //默认的滚动条样式,也可以在这里使用图片等自定义样式
    171 MyScrolly.defaultRender=function(elem,that)
    172 {
    173     elem.style.position="relative";
    174     elem.style.overflowX="hidden";
    175     elem.style.overflowY="hidden";//取消浏览器自带的滚动条
    176     var div1=document.createElement("div");//滑轨
    177     div1.className="div_myscroll1";
    178     div1.style.width="10px";
    179     div1.style.backgroundColor="rgb(245,245,245)";
    180     div1.style.position="absolute";
    181     div1.style.right="0px";
    182     div1.style.top="0px";
    183     div1.style.height="100%";
    184     div1.style.zIndex=elem.style.zIndex+10;
    185     div1.style.display="none";
    186     that.div1=div1;
    187     var div2=document.createElement("div");//滑块
    188     div2.className="div_myscroll2";
    189     div2.style.width="10px";
    190     div2.style.backgroundColor="rgb(226,226,226)";
    191     div2.style.position="absolute";
    192     div2.style.right="0px";
    193     div2.style.top="0px";
    194     //div1.style.height="100%";
    195     div2.style.zIndex=elem.style.zIndex+20;
    196     that.div2=div2;
    197     div1.appendChild(div2);
    198     elem.appendChild(div1);
    199 }

     20201113补充,在实际使用中发现三个问题:

    一、如果容器中包含img标签,则需要等待所有img标签加载完毕后计算内容高度,否则img标签高度只会表示为24px(Chrome下的默认加载图标高度),建议为每个img标签设置onload和onerror监听,搭配记录总img数量的计数器确保所有img标签加载完毕后计算内容高度。(为标签设置innerHTML后即可用getElement计算img标签个数)

    二、如果将滑轨和容器内容放在同一层次,则需要注意在用innerHTML=""清空容器内容时也会将滚动条一起清理掉,需要在内容填充完毕后重绘滚动条。特别的,如果容器中的标签是否换行受到滑轨宽度影响,则计算内容offsetHeight值时会丢失换行增加的部分,最终导致算出的滚动长度小于真实内容长度,故此建议将变化的内容放在一个尺寸随内容变化的内部容器中,然后让这个内部容器与滑轨平级。

    三、在实际使用时发现将parentElem设为document时,虽然事件能够触发响应,但并没有实现预期的滚动效果,替换为外围其他标签后正常,时间有限没有深入研究。

  • 相关阅读:
    SSH、SCP和SFTP 解析(转)
    SQL Server数据库partition by 与ROW_NUMBER()函数使用详解 (转载)
    Git版本控制与工作流详解(转)
    IQueryable,IEnumberable,.AsEnumerable() 和 .AsQueryable() (转载)
    ASP.NET 中Http处理流程与 HttpModule,HttpHandler学习之初步认知
    xml Node 是否存在
    MVC-前台调用后台action 传递upload file 参数问题
    ResXResourceWriter 与ResourceWriter
    "= ="与 equals 的区别 摘录
    jpg文件格式分析
  • 原文地址:https://www.cnblogs.com/ljzc002/p/13954404.html
Copyright © 2011-2022 走看看