美化浏览器滚动条效果
任务需求:由于不同浏览器对滚动条的样式解析存在差异,为统一样式风格,增加整体美观程度,需要实现自定义滚动条。
第一种方案:CSS设置滚动条样式。(目前只有Chrome浏览器支持,火狐不支持)
样式说明:
CSS ::-webkit-scrollbar { /**/ } ::-webkit-scrollbar-button { /**/ } ::-webkit-scrollbar-track { /**/ } ::-webkit-scrollbar-track-piece { /**/ } ::-webkit-scrollbar-thumb { /**/ } ::-webkit-scrollbar-corner { /**/ } ::-webkit-resizer { /**/ }
::-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); } }); } }]); });
总结
工作中都会遇到一些大大小小问题,与大家分享一下任务的完成过程。鉴于自己的技术也菜,如果有更好的办法或者编码不严谨的地方欢迎大家纠正,请留在评论里供大家学习,一起共勉,谢谢。