天天看點

原生js監聽滾動條_原生js實作模拟滾動條

當頁面中有很多滾動條,它們互相嵌套,很不好看,這時就會模拟滾動條,并給這個滾動條好看的樣式,使得頁面美觀。

模拟滾動條很多時候是去用jquery插件,然後寫幾行代碼就搞定了。不過随着mvvm的快速發展,很多時候都懶得用jquery了,這就是本文的動機,本屌力求用簡單的不依賴jquery隻依賴mvvm(avalon) api的代碼,完成一個簡易的滾動條。

要求:

1.滑鼠滾輪可以讓滾動條工作,界面滾動

2.滑鼠可以拖動滾動條并讓界面滾動

3.頁面resize時,滾動條根據頁面尺寸變化,仍然可以工作

效果:

原生js監聽滾動條_原生js實作模拟滾動條

很顯然,這個元件是基于拖動drag的,本屌又不想重新寫,就隻有改下ui架構的drag了,這裡改的是easy js ui的drag元件。用easy js是因為注釋比較多,代碼簡潔。

本屌把easy js ui的drag元件裡的相應方法換成avalon api裡的方法,删掉prototype裡的方法及備援代碼

define('drag',['avalon-min'],function(avalon){

function getBoundary(container, target) {

var borderTopWidth = 0, borderRightWidth = 0, borderBottomWidth = 0, borderLeftWidth = 0, cOffset = avalon(container)

.offset(), cOffsetTop = cOffset.top, cOffsetLeft = cOffset.left, tOffset = avalon(target)

.offset();

borderTopWidth = parseFloat(avalon.css(container,'borderTopWidth'));

borderRightWidth = parseFloat(avalon.css(container,'borderRightWidth'));

borderBottomWidth = parseFloat(avalon.css(container,'borderBottomWidth'));

borderLeftWidth = parseFloat(avalon.css(container,'borderLeftWidth'));

cOffsetTop = cOffsetTop - tOffset.top + parseFloat(avalon(target).css('top'));

cOffsetLeft = cOffsetLeft - tOffset.left + parseFloat(avalon(target).css('left'));

return {

top : cOffsetTop + borderTopWidth,

right : cOffsetLeft + avalon(container).outerWidth() - avalon(target).outerWidth()

- borderRightWidth,

left : cOffsetLeft + borderLeftWidth,

bottom : cOffsetTop + avalon(container).outerHeight() - avalon(target).outerHeight()

- borderBottomWidth

};

}

var drag = function(target, options) {

var defaults = {

axis:null,

container:null,

handle:null,

ondragmove:null

};

var o =avalon.mix(defaults,options),

doc = target.ownerDocument,

win = doc.defaultView || doc.parentWindow,

originHandle=target,

isIE =!-[1,],

handle = isIE ? target :doc,

container = o.container ?o.container: null,

count = 0,

drag = this,

axis = o.axis,

isMove = false,

boundary, zIndex, originalX, originalY,

clearSelect = 'getSelection' in win ? function(){

win.getSelection().removeAllRanges();

} : function(){

try{

doc.selection.empty();

}

catch( e ){};

},

down = function( e ){

o.isDown = true;

var newTarget = target,

left, top, offset;

o.width = avalon(target).outerWidth();

o.height = avalon(target).outerHeight();

o.handle = handle;

left = avalon(newTarget).css( 'left' );

top = avalon(newTarget).css( 'top' );

offset = avalon(newTarget).offset();

drag.left = left = parseInt( left );

drag.top = top = parseInt( top );

drag.offsetLeft = offset.left;

drag.offsetTop = offset.top;

originalX = e.pageX - left;

originalY = e.pageY - top;

if( (!boundary && container)){

boundary = getBoundary(container, newTarget );

}

if( axis ){

if( axis === 'x' ){

originalY = false;

}

else if( axis === 'y' ){

originalX = false;

}

}

if( isIE ){

handle.setCapture();

}

avalon.bind(handle,'mousemove',move);

avalon.bind(handle,'mouseup',up);

if( isIE ){

avalon.bind(handle,'losecapture',up);

}

e.stopPropagation();

e.preventDefault();

},

move = function( e ){

if( !o.isDown ){

return;

}

count++;

if( count % 2 === 0 ){

return;

}

var currentX = e.pageX,

currentY = e.pageY,

style = target.style,

x, y, left, right, top, bottom;

clearSelect();

isMove = true;

if( originalX ){

x = currentX - originalX;

if( boundary ){

left = boundary.left;

right = boundary.right;

x = x < left ? left :

x > right ? right :

x;

}

drag.left = x;

drag.offsetLeft = currentX - e.offsetX;

style.left = x + 'px';

}

if( originalY ){

y = currentY - originalY;

if( boundary ){

top = boundary.top;

bottom = boundary.bottom;

y = y < top ? top :

y > bottom ? bottom :

y;

}

drag.top = y;

drag.offsetTop = currentY - e.offsetY;

style.top = y + 'px';

}

o.ondragmove.call(this,drag);

e.stopPropagation();

},

up = function( e ){

o.isDown = false;

if( isIE ){

avalon.unbind(handle,'losecapture' );

}

avalon.unbind( handle,'mousemove');

avalon.unbind( handle,'mouseup');

if( isIE ){

handle.releaseCapture();

}

e.stopPropagation();

};

avalon(originHandle).css( 'cursor', 'pointer' );

avalon.bind( originHandle,'mousedown', down );

drag.refresh=function(){

boundary=getBoundary(container,target);

};

};

return drag;

});

另外在最後暴露的drag上加了一個refresh()方法,作用是在resize時,需要更新滾動條可以拖動的範圍。這個方法在scrollbar的更新視圖中會用到。

drag.refresh=function(){

boundary=getBoundary(container,target);

};

還有在滾動條拖動過程move中,添加一個鈎子,允許從外面添加一個監聽函數,拖動時會觸發監聽函數,并傳入drag參數。

o.ondragmove.call(this,drag);

然後是scrollbar.js

define('scrollbar',['avalon-min','drag'],function(avalon,drag){

function scrollbar(wrap,scrollbar,height_per_scroll){//容器,滾動條,每次滾輪移動的距離

this.scroll_height=0;//滾動條高度

this.dragger=null;//drag元件執行個體

wrap.scrollTop=0;

//容器的位置要減去浏覽器最外面的預設滾動條垂直方向位置

var self=this,wrap_top=avalon(wrap).offset().top-avalon(document).scrollTop();

function ondragmove(drag){//drag元件拖動時的監聽函數,更新容器視圖

wrap.scrollTop=(parseFloat(scrollbar.style.top)-wrap_top)*

(wrap.scrollHeight -wrap.clientHeight)/(wrap.clientHeight-self.scroll_height);

};

function setScrollPosition(o) {//更新滾動條位置

scrollbar.style.top =o.scrollTop*wrap.clientHeight/wrap.scrollHeight+wrap_top+ 'px';

}

function inti_events(){

avalon.bind(wrap,'mousewheel',function(e){

if(e.wheelDelta < 0)

wrap.scrollTop+=height_per_scroll;

else

wrap.scrollTop-=height_per_scroll;

setScrollPosition(wrap);

e.preventDefault();

});

self.dragger=new drag(scrollbar,{container:wrap,axis:'y',ondragmove:ondragmove});

window.οnresize=function(){

self.refresh_views();

self.dragger.refresh();

};

}

this.refresh_views=function(){//更新元件所有部分視圖,并暴露供外部調用

//容器高度這裡設定成浏覽器可視部分-容器垂直方向位置,沒有考慮容器有border,padding,margin.可根據相應場景修改

wrap.style.height=document.documentElement.clientHeight-wrap_top+'px';

self.scroll_height=wrap.clientHeight*wrap.clientHeight/wrap.scrollHeight;

//容器高度等于滾動條高度,隐藏滾動條

if(self.scroll_height==wrap.clientHeight)

scrollbar.style.display='none';

else

scrollbar.style.display='block';

scrollbar.style.height=self.scroll_height+'px';

setScrollPosition(wrap);

}

function init(){

self.refresh_views();

inti_events();

}

init();

}

return scrollbar;

});

可以看到,在resize時,調用了drag元件的refresh方法,更新滾動條可以拖動的範圍。這裡暴露了refresh_views()方法,以應對外部需要手動更新視圖的情況。比如,聊天分組的折疊和展開。

原生js監聽滾動條_原生js實作模拟滾動條

這樣就完成了簡易滾動條。代碼很簡單,如果出問題需要fix bug或定制的話,也很容易。

以上所述上就是本文的全部内容了,希望大家能夠喜歡。