js模拟滚动条(不依赖jquery)

转载请注明: TheViper http://www.cnblogs.com/TheViper

当页面中有很多滚动条,它们相互嵌套,很不好看,这时就会模拟滚动条,并给这个滚动条好看的样式,使得页面美观。

模拟滚动条很多时候是去用jquery插件,然后写几行代码就搞定了。不过随着mvvm的快速发展,很多时候都懒得用jquery了,这就是本文的动机,本屌力求用简单的不依赖jquery只依赖mvvm(avalon) api的代码,完成一个简易的滚动条。

要求:

1.鼠标滚轮可以让滚动条工作,界面滚动

2.鼠标可以拖动滚动条并让界面滚动

3.页面resize时,滚动条根据页面尺寸变化,仍然可以工作

效果:

很显然,这个组件是基于拖动drag的,本屌又不想重新写,就只有改下ui框架的drag了,这里改的是easy js ui的drag组件。用easy js是因为注释比较多,代码简洁。

本屌把easy js ui的drag组件里的相应方法换成avalon api里的方法,删掉prototype里的方法及冗余代码

  1 define(‘drag‘,[‘avalon-min‘],function(avalon){
  2     function getBoundary(container, target) {
  3         var borderTopWidth = 0, borderRightWidth = 0, borderBottomWidth = 0, borderLeftWidth = 0, cOffset = avalon(container)
  4         .offset(), cOffsetTop = cOffset.top, cOffsetLeft = cOffset.left, tOffset = avalon(target)
  5         .offset();
  6         borderTopWidth = parseFloat(avalon.css(container,‘borderTopWidth‘));
  7         borderRightWidth = parseFloat(avalon.css(container,‘borderRightWidth‘));
  8         borderBottomWidth = parseFloat(avalon.css(container,‘borderBottomWidth‘));
  9         borderLeftWidth = parseFloat(avalon.css(container,‘borderLeftWidth‘));
 10         cOffsetTop = cOffsetTop - tOffset.top + parseFloat(avalon(target).css(‘top‘));
 11         cOffsetLeft = cOffsetLeft - tOffset.left + parseFloat(avalon(target).css(‘left‘));
 12         return {
 13             top : cOffsetTop + borderTopWidth,
 14             right : cOffsetLeft + avalon(container).outerWidth() - avalon(target).outerWidth()
 15             - borderRightWidth,
 16             left : cOffsetLeft + borderLeftWidth,
 17             bottom : cOffsetTop + avalon(container).outerHeight() - avalon(target).outerHeight()
 18             - borderBottomWidth
 19         };
 20     }
 21     var drag = function(target, options) {
 22         var defaults = {
 23             axis:null,
 24             container:null,
 25             handle:null,
 26             ondragmove:null
 27         };
 28         var o =avalon.mix(defaults,options),
 29         doc = target.ownerDocument,
 30         win = doc.defaultView || doc.parentWindow,
 31         originHandle=target,
 32         isIE =!-[1,],
 33         handle = isIE ? target :doc,
 34         container = o.container ?o.container: null,
 35         count = 0,
 36         drag = this,
 37         axis = o.axis,
 38         isMove = false,
 39         boundary, zIndex, originalX, originalY,
 40         clearSelect = ‘getSelection‘ in win ? function(){
 41             win.getSelection().removeAllRanges();
 42         } : function(){
 43             try{
 44                 doc.selection.empty();
 45             }
 46             catch( e ){};
 47         },
 48         down = function( e ){
 49             o.isDown = true;
 50             var newTarget = target,
 51             left, top, offset;
 52             o.width = avalon(target).outerWidth();
 53             o.height = avalon(target).outerHeight();
 54             o.handle = handle;
 55             left = avalon(newTarget).css( ‘left‘ );
 56             top = avalon(newTarget).css( ‘top‘ );
 57             offset = avalon(newTarget).offset();
 58             drag.left = left = parseInt( left );
 59             drag.top = top = parseInt( top );
 60             drag.offsetLeft = offset.left;
 61             drag.offsetTop = offset.top;
 62             originalX = e.pageX - left;
 63             originalY = e.pageY - top;
 64             if( (!boundary && container)){
 65                 boundary = getBoundary(container, newTarget );
 66             }
 67             if( axis ){
 68                 if( axis === ‘x‘ ){
 69                     originalY = false;
 70                 }
 71                 else if( axis === ‘y‘ ){
 72                     originalX = false;
 73                 }
 74             }
 75             if( isIE ){
 76                 handle.setCapture();
 77             }
 78             avalon.bind(handle,‘mousemove‘,move);
 79             avalon.bind(handle,‘mouseup‘,up);
 80             if( isIE ){
 81                 avalon.bind(handle,‘losecapture‘,up);
 82             }
 83             e.stopPropagation();
 84             e.preventDefault();
 85         },
 86         move = function( e ){
 87             if( !o.isDown ){
 88                 return;
 89             }
 90             count++;
 91             if( count % 2 === 0 ){
 92                 return;
 93             }
 94             var currentX = e.pageX,
 95             currentY = e.pageY,
 96             style = target.style,
 97             x, y, left, right, top, bottom;
 98             clearSelect();
 99             isMove = true;
100             if( originalX ){
101                 x = currentX - originalX;
102                 if( boundary ){
103                     left = boundary.left;
104                     right = boundary.right;
105                     x = x < left ? left :
106                     x > right ? right :
107                     x;
108                 }
109                 drag.left = x;
110                 drag.offsetLeft = currentX - e.offsetX;
111                 style.left = x + ‘px‘;
112             }
113             if( originalY ){
114                 y = currentY - originalY;
115                 if( boundary ){
116                     top = boundary.top;
117                     bottom = boundary.bottom;
118                     y = y < top ? top :
119                     y > bottom ? bottom :
120                     y;
121                 }
122                 drag.top = y;
123                 drag.offsetTop = currentY - e.offsetY;
124                 style.top = y + ‘px‘;
125             }
126             o.ondragmove.call(this,drag);
127             e.stopPropagation();
128         },
129         up = function( e ){
130             o.isDown = false;
131             if( isIE ){
132                 avalon.unbind(handle,‘losecapture‘ );
133             }
134             avalon.unbind( handle,‘mousemove‘);
135             avalon.unbind( handle,‘mouseup‘);
136             if( isIE ){
137                 handle.releaseCapture();
138             }
139             e.stopPropagation();
140         };
141         avalon(originHandle).css( ‘cursor‘, ‘pointer‘ );
142         avalon.bind( originHandle,‘mousedown‘, down );
143         drag.refresh=function(){
144             boundary=getBoundary(container,target);
145         };
146     };
147     return drag;
148 });

另外在最后暴露的drag上加了一个refresh()方法,作用是在resize时,需要更新滚动条可以拖动的范围。这个方法在scrollbar的更新视图中会用到。

        drag.refresh=function(){
            boundary=getBoundary(container,target);
        }; 

还有在滚动条拖动过程move中,添加一个钩子,允许从外面添加一个监听函数,拖动时会触发监听函数,并传入drag参数。

o.ondragmove.call(this,drag);

然后是scrollbar.js

 1 define(‘scrollbar‘,[‘avalon-min‘,‘drag‘],function(avalon,drag){
 2     function scrollbar(wrap,scrollbar,height_per_scroll){//容器,滚动条,每次滚轮移动的距离
 3         this.scroll_height=0;//滚动条高度
 4         this.dragger=null;//drag组件实例
 5         wrap.scrollTop=0;
 6         var self=this;
 7         function ondragmove(drag){//drag组件拖动时的监听函数,更新容器视图
 8             wrap.scrollTop=(parseFloat(scrollbar.style.top)-avalon(wrap).offset().top)*
 9             (wrap.scrollHeight -wrap.clientHeight)/(wrap.clientHeight-self.scroll_height);
10         };
11         function setScrollPosition(o) {//更新滚动条位置
12             scrollbar.style.top =o.scrollTop*wrap.clientHeight/wrap.scrollHeight+avalon(wrap).offset().top+ ‘px‘;
13         }
14         function inti_events(){
15             avalon.bind(wrap,‘mousewheel‘,function(e){
16                 if(e.wheelDelta < 0)
17                    wrap.scrollTop+=height_per_scroll;
18                 else
19                    wrap.scrollTop-=height_per_scroll;
20                 setScrollPosition(wrap);
21                 e.preventDefault();
22             });
23             self.dragger=new drag(scrollbar,{container:wrap,axis:‘y‘,ondragmove:ondragmove});
24             window.onresize=function(){
25                 self.refresh_views();
26                 self.dragger.refresh();
27             };
28         }
29         this.refresh_views=function(){//更新组件所有部分视图,并暴露供外部调用
30             //容器高度这里设置成浏览器可视部分-容器垂直方向位置,没有考虑容器有border,padding,margin.可根据相应场景修改
31             wrap.style.height=document.documentElement.clientHeight-avalon(wrap).offset().top+‘px‘;
32             self.scroll_height=wrap.clientHeight*wrap.clientHeight/wrap.scrollHeight;
33             //容器高度等于滚动条高度,隐藏滚动条
34             if(self.scroll_height==wrap.clientHeight)
35                 scrollbar.style.display=‘none‘;
36             else
37                 scrollbar.style.display=‘block‘;
38             scrollbar.style.height=self.scroll_height+‘px‘;
39             setScrollPosition(wrap);
40         }
41         function init(){
42             self.refresh_views();
43             inti_events();
44         }
45         init();
46     }
47     return scrollbar;
48 });

可以看到,在resize时,调用了drag组件的refresh方法,更新滚动条可以拖动的范围。这里暴露了refresh_views()方法,以应对外部需要手动更新视图的情况。比如,聊天分组的折叠和展开。

最后附上下载

如果您觉得本文的内容对您有所帮助,您可以打赏我: 

时间: 2024-11-05 00:41:48

js模拟滚动条(不依赖jquery)的相关文章

原生js模拟滚动条

滚动条的基本交互有两个,鼠标拖拽滚动条和滚轮滚动. 滚动条涉及到的dom元素:1.主体区域(obj,box2与box4的父元素),鼠标滚动的触发主体,包含内容和滚动条,宽高自定.2.滚动条(box1),宽自定,高按内容区比例计算.3.滚动区域(box2,box1的父节点),高与内容可视区的高相同,宽自定.4.内容(box3,滚动的内容主体).5.内容(box4,box3的父元素,有限宽高,内容可视区). 实现的基本原理就是以上元素绝对定位,通过鼠标的交互事件,来完成相关dom的top值,已达到模

js模拟滚动条

1 <!DOCTYPE html> 2 <html> 3 <head> 4 5 <title></title> 6 <style type="text/css"> 7 * { margin: 0px; padding: 0px; } 8 .scroll-boxall { width: 312px; height: 500px; overflow: hidden; zoom: 1; border: 1px solid

js 模拟滚动条

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-

原生JS模拟JQuery封装Ajax

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" con

jQuery.noConflict()解决imgBox.js依赖jquery版本问题

jQuery提供两种点击图片放大效果出处 在使用imgbox.js是出现的jquery版本不兼容问题,之后了解到jQuery.noConflict()的用法 jQuery.noConflict()的存在只有一个目的:它允许你在同一个页面加载多个jQuery实例,尤其是不同版本的jQuery. //引用 <script src="~/Scripts/ssn/jquery.min.js"></script> <script type="text/ja

单篇文章JS模拟分页

废话部分 前两天做了一个前台分页插件,支持ajax读取数据绑定前台 和 url带页码参数跳转两种方式.于是稍加改动,做了一个单篇文章js模拟分页的代码,为什么说是模拟分页呢?因为在服务器响应HTML请求的时候,就已经把全文回传给客户端了,只是我们通过js的方式,把全文隐藏,每次翻页至显示出我们需要的那一部分,而不是真正的按需要去发出HTML请求.所以,在做这个插件的时候去掉了ajax请求的功能及其附带参数,去掉了pageSize参数(恒等于1).这里就不讨论具体的技术细节了和上一篇的分页计算原理

纯js模拟 radio和checkbox控件

代码待优化,功能实现了,不兼容ie8以上, 相同name的radio可以实现切换的操作, 分享代码,共同学习进步 <!doctype html> <html> <head> <meta charset="utf-8"> <title></title> <style> .radiobox, .checkbox { width: 10px; height: 10px; padding: 2px; borde

js模拟jq获取id

js模拟jq获取id: (jquery)需要自己添加 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>js模拟jq的点击效果</title> <style> * {margin: 0; padding: 0;} #btn {display:block;height: 30px; width:

js获取滚动条距离浏览器顶部,底部的高度,兼容ie和firefox

做web开发经常会碰到需要获取浏览器的滚动条与顶部和底部的距离,然后做相应的处理动作.下面作者就如何通过js来获取浏览器滚动条距离浏览器顶部和底部的高度做一下分享,这个是同时兼容ie和firefox的. 获取窗口可视范围的高度 function getClientHeight(){ var clientHeight=0; if(document.body.clientHeight&&document.documentElement.clientHeight){ var clientHeig