主流浏览器默认为html元素提供的滚动条不美观,而且前端开发人员想对其通过css进行统一样式的美化也是不可实现的。比如ie可以通过样式来实现简单的美化、Webkit内核浏览器可以控制滚动条的显示效果,firefox则不允许用户为滚动条定义样式。但是对于追求友好的用户体验的前端开发人员,是不会被这些浏览器的不一致行为所阻止的。我们可以自己通过标准的html元素模拟来实现自定义的滚动条。
这里是自己在工作不太忙的时候写出来了一个用户可以自定义的滚动条jscroll,以下简称jscroll。jscroll默认只提供一种滚动条样式,部分样式来自google webstore ,其中有部分css3样式主要用于实现圆角,阴影效果。为实现跨浏览器情况下滚动条显示效果的一致,对于ie6, 7, 8不支持css3的浏览器引入了 PIE.htc 文件。下面就实现的功能以及兼容性、使用方法、具体代码实现分别做一下讲解。
实现功能以及兼容性
jscroll实现了系统默认滚动条的几乎所有功能,比如:拖动滚动条查看内容、滚动鼠标滚轮查看内容、点击滚动条可到达区域的上方或者下方来触发滚动条的滚动、键盘上下键来触发滚动条的滚动。firefox、chrome,、ie9+ 等最新浏览器支持css3以及js的最新API,所以没有任何兼容性问题。ie6, 7, 8 不支持css3通过引入PIE.htc 的hack文件来做兼容处理。js方面对于不支持的API通过旧的API来做了兼容。有最大兼容性问题的浏览器是ie6,不支持点击滚动条可到达区域来触发滚动条滚动,也不支持键盘上下键来触发滚动条的滚动。导致这个问题的原因主要是因为引入了支持css3的PIE.htc文件,如果不引入该hack文件,所有操作都能支持,没法办为了显示效果的一致,只好选择了不支持部分功能。
使用方法
使用自定义滚动条最多的情况应该是页面弹出层,或者是页面上某一个区域,千万不要对整个页面的滚动条进行自定义操作。对于需要使用jscroll的元素,需要添加自定义属性data-scroll="true"来告诉程序需要使用jscroll来替换系统默认的滚动条,同时还需要通过添加自定义属性data-width=""、data-height=""来指定元素要显示的宽度和高度。jscroll会根据用户定义的宽度和高度计算内容的显示宽度以及滚动条显示的高度并添加交互的事件。
具体代码实现
jscroll的实现逻辑并不复杂,实现具体功能的js代码不到400行,但是这里依赖了一些基础的方法,所以需要引入squid.js作为基础方法的支持。对滚动条样式的控制的css在一个单独的jscroll-1.0.css文件里面,用户可以自己修改扩展以满足自己的需求。下面是对实现具体功能的每个方法做一个简单的分析:
复制代码 代码如下:
init: function(selector, context) {
selecotr = selector || 'data-scroll'
context = context || document
var elems = squid.getElementsByAttribute(selector, context)
this.initView(elems)
}
init()是初始化函数,根据指定selector和context获取需要使用自定义滚动条的元素,selector默认是data-scroll,上下文默认是当前document。这里无论元素自定义属性data-scroll="true"或者data-scroll="false"都会使用自定滚动条覆盖系统默认滚动条,squid的getElementsByAttribute()方法只是提供通过元素是否有指定属性来查找元素而忽略属性值,这个方法没有jquery选择器或者高级浏览器提供的querySelectorAll()方法强大,因为这里squid只是做最基本的支持。找到需要自定义滚动条的元素之后调用initView方法来初始化滚动条整体结构和显示。
复制代码 代码如下:
initView: function(elems) {
var i = 0,
length = elems.length,
elem;
for(; i < length; i++) {
elem = elems[i]
if(this.hasScroll(elem)) {
this.create(elem)
}
}
this.initEvent()
}
initView()方法会首先对页面上获取的带有自定义属性data-scroll的元素遍历,判断每一个元素是否会出现滚动条,通过hasScroll()方法判断。如果元素会出现滚动条则调用create()方法分别创建自定义的滚动条。initView()方法结束会调用initEvent()方法。
复制代码 代码如下:
//是否有滚动条
hasScroll: function(elem) {
return elem.offsetHeight < elem.scrollHeight
}
hasScroll()方法用于判断元素是否会出现滚动条,返回true或者false。这里忽略元素的margin和padding,通过jscroll创建的滚动条默认margin和padding都是0。
复制代码 代码如下:
//创建滚动条元素整体结构
create: function(elem) {
var wrapper,
list,
//滚动条元素
s,
//带滚动条元素显示的高度
height = elem['data-height'] || elem.getAttribute('data-height'),
//带滚动条元素显示的宽度
width = elem['data-width'] || elem.getAttribute('data-width'),
//滚动条显示高度
value;
//wrapper
wrapper = document.createElement('div')
wrapper.className = 'jscroll-wrapper'
//forbid select text, for ie9
/*
* wrapper.onselectstart = function() {
* return false
* }
*/
squid.css(wrapper, {
height: height + 'px',
width: width + 'px'
})
squid.addClass(elem, 'jscroll-body')
//overwrite the user define style
squid.css(elem, {
overflow: 'visible',
position: 'absolute',
height: 'auto',
width: (width - 40) + 'px',
padding: '0 20px 0 23px'
})
//list
list = document.createElement('div')
list.className = 'jscroll-list unselectable'<BR> list.unselectable = 'on'
squid.css(list, {
height: (height - 5) + 'px'
})
//滚动条
s = document.createElement('div')
s.className = 'jscroll-drag unselectable'
s.unselectable = 'on'
s.setAttribute('tabindex', '1')
s.setAttribute('hidefocus', true)
list.appendChild(s)
wrapper.appendChild(list)
//把需要出现滚动条的元素包裹起来
elem.parentNode.replaceChild(wrapper, elem)
wrapper.insertBefore(elem, list)
//滚动条高度
value = this.scrollbarHeight(elem, height)
squid.css(s, {
height: value + 'px'
})
//add event
this.regEvent(wrapper)
}
create()方法用户调整创建带有自定义滚动条的元素整体结构,首先通过创建了wrapper元素,用于包装会出现滚动条的元素elem和滚动条可滚动的区域元素list以及滚动条元素s。通过从出现滚动条元素设置的自定义属性data-width、data-height分别设置wrapper元素的宽度和高度。通过scrollbarHeight()方法计算得到了滚动条元素显示的高度,整体结构不算复杂。创建自定义滚动条整体结构之后是为滚动条元素s和滚动条可到达区域元素list添加事件处理,通过regEvent()方法实现。
复制代码 代码如下: