任務需求:由於不同瀏覽器對滾動條的樣式解析存在差異,為統一樣式風格,增加整體美觀程度,需要實現自定義滾動條。
第一種方案:CSS設置滾動條樣式。(目前只有Chrome瀏覽器支持,火狐不支持)
樣式說明:
CSS ::-webkit-scrollbar { /* 1 */ } ::-webkit-scrollbar-button { /* 2 */ } ::-webkit-scrollbar-track { /* 3 */ } ::-webkit-scrollbar-track-piece { /* 4 */ } ::-webkit-scrollbar-thumb { /* 5 */ } ::-webkit-scrollbar-corner { /* 6 */ } ::-webkit-resizer { /* 7 */ }
::-webkit-scrollbar 滾動條整體部分,其中的屬性有width,height,background,border(就和一個塊級元素一樣)等。
::-webkit-scrollbar-button 滾動條兩端的按鈕。可以用display:none讓其不顯示,也可以添加背景圖片,顏色改變顯示效果。
::-webkit-scrollbar-track 外層軌道。可以用display:none讓其不顯示,也可以添加背景圖片,顏色改變顯示效果。
::-webkit-scrollbar-track-piece 內層軌道,滾動條中間部分(除去)。
::-webkit-scrollbar-thumb 滾動條里面可以拖動的那部分
::-webkit-scrollbar-corner 邊角
::-webkit-resizer 定義右下角拖動塊的樣式
第二種方案:由於CSS設置滾動條只有谷歌瀏覽器支持,所以引入jquery第三方擴展插件nicescroll.js並對其進行封裝成AngularJs指令使其更方便使用。
1、 引入外部文件
<script src="js/jquery.js"></script>
<script src="js/jquery.nicescroll.min.js"></script>
nicescroll是基於jquery的擴展,此處需要先引入jquery再引入nicescroll。
封裝的ng-scroll指令代碼如下:
angular.module('ng.Scroll', []) .provider('$scroll', function(){ var $$options = { cursorcolor: "#008fd4",//改變滾動條顏色,使用16進制顏色值 cursoropacitymax: 0.2, //當滾動條是隱藏狀態時改變透明度, 值范圍 1 到 0 cursorwidth: "4px", //滾動條的寬度,單位:便素 cursorborder: "0", // CSS方式定義滾動條邊框 cursorborderradius: "2px",//滾動條圓角(像素) autohidemode: false //隱藏滾動條的方式 }; //調用scrollProvider為整個項目配置統一的滾動條風格 this.setOptions = function(options) { angular.extend($$options, options); }; this.$get = function() { return { getOptions:function(){ //返回配置對象的一個拷貝 var defaultOptions={}; angular.copy($$options,defaultOptions); return defaultOptions; } }; } }) .directive('ngScroll', ['$scroll', function ($scroll) { return { restrict: 'A', link: _link }; function _link(scope, iElement, iAttrs) { var scrollOptions = scope.$eval(iAttrs.scrollOption);//解析scroll特性配置對象 var defaultOptions = $scroll.getOptions();//獲取統一風格的配置對象 var niceOptions = angular.extend(defaultOptions, scrollOptions); var niceScroll = $(iElement).niceScroll(niceOptions); //調用第三方插件nicescroll方法對dom元素實現滾動條效果 scope.$on('$destroy', function () { // 注冊'$destroy'事件來刪除任何易於內存泄漏的代碼。 if (angular.isDefined(niceScroll)) { niceScroll.remove() } }) } }]);
另外注意:nicescroll是把滾動條DOM節點全加在父容器上,當在同一頁面中使用多個nicescroll滾動條時,滾動容器的滾動條時會影響內部的滾動條位置,要及時隱藏用完的nicescroll對象,
加載時,需要先show,再resize。
由於這個原因,使用多滾動條需要先隱藏滾動條的顯示,還是滿足不了任務的需求,所以有了第三種方案。
第三種方案:引入另一個擴展插件perfect-scrollbar並對其進行封裝成AngularJs指令使其更方便使用。
此插件有JS和Jquery雙版本,我更喜歡引入原生的js版,性能好也不必依賴jquery庫,插件的使用介紹直接看官方文檔 https://github.com/noraesae/perfect-scrollbar
在結合自己項目控件使用時帶來了另一個難題,perfect-scrollbar能解決多滾動條場景的BUG,但引入了另一個BUG,比如表格Table中使用滾動條,在表格數據還沒請求成功時,元素的scrollHeight高度為0,導致
perfect-scrollbar在初始化滾動條時,無法得到元素的具體高度scrollHeight,從而滾動條的高度為0,結果一開始滾動條是不顯示的,鼠標在表格上滾動一下滾動條就出來了。
那么問題來了,如何解決Ajax請求的數據還未加載時,內容的高度還不確定的情況怎么設置滾動條呢?
1,最原始的辦法就是把初始化滾動條的操作放在Ajax請求的回調函數中,但是每個與數據有關的地方需要滾動條顯示時也是得都要進行此處理,使用不方便,而且Ajax請求一般都封裝成單獨的數據服務模塊,滾動條邏輯和數據服務扯上關系了這有點尷尬,同時也增加了模塊間的耦合性。
2,可以使用事件監聽,監聽內容區域DOM元素的resize事件,當內容改變時就能自動增加元素的高度,就可以在監聽函數中處理滾動條的更新。
當調整瀏覽器窗口的大小時,發生 resize 事件。也就是說,resize 事件觸發在window對象上,window.addEventListener("resize",fn);
有個大神寫了個方法可以在div上監聽resize事件,直接就可以 $('div').resize(fucntion(){ .. }); 詳情請看我另一篇文章的記錄:http://www.cnblogs.com/weboey/p/6014966.html
3,最佳解決辦法。HTML5的新特性MutationObserver,它的作用是監視DOM變動。當DOM對象樹發生任何改變時,MutationObserver會得到通知。有關MutationObserver的詳細介紹和使用本文就不詳細介紹 了,(HTML5此接口很強大,值得大家去學習了解)以下是我利用MutationObserver來解決內容元素的變動更新滾動條高度的代碼,並封裝成angularjs指令,直接以屬性的形式使用。
define(['perfect-scrollbar','css!ng.Scroll'], function(perfectScroll) { angular.module('ng.Scroll', []) .directive('ngScroll', [function () { return { restrict: 'A', link: _link }; function _link(scope, iElement) { var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; var hlazyresize=null; var container = iElement[0]; iElement.css({position: 'relative',overflow: 'hidden'}); //滾動條容器必要的樣式 perfectScroll.initialize(container); //初始化滾動條 perfectScroll.lazyResize=function(){ //DOM元素內容不確定是否加載完,滾動條進行延時加載處理 if (hlazyresize) clearTimeout(hlazyresize); hlazyresize = setTimeout(function(){ perfectScroll.update(container); },200); }; perfectScroll.lazyResize(); if(!!MutationObserver) { //觀察配置對象,觀察所有子節點(包括子節點和子節點的子節點) var observerOption={ 'childList': true, 'subtree': true }; //觀察DOM樹變動,更新滾動條 perfectScroll.observer=new MutationObserver( function(mutations){ perfectScroll.lazyResize(); } ).observe(container, observerOption); //觀察滾動條注銷,取消觀察 perfectScroll.observerRemover = new MutationObserver( function(mutations) { mutations.forEach(function(mo) { if (mo.removedNodes.length > 0) { for (var dom in mo.removedNodes) { if (!!perfectScroll && (mo.removedNodes[dom] == container)) { perfectScroll.observer.disconnect(); perfectScroll.destroy(container); } } } }); }).observe(container.parentNode, observerOption); } scope.$on('$destroy', function () { // 注冊'$destroy'事件來刪除任何易於內存泄漏的代碼。 if (angular.isDefined(container)) { perfectScroll.destroy(container); !!hlazyresize&&clearTimeout(hlazyresize); } }); } }]); });
總結
工作中都會遇到一些大大小小問題,與大家分享一下任務的完成過程。鑒於自己的技術也菜,如果有更好的辦法或者編碼不嚴謹的地方歡迎大家糾正,請留在評論里供大家學習,一起共勉,謝謝。