这里有新鲜出炉的 Javascript 教程,程序狗速度看过来!
Javascript 是一种由 Netscape 的 LiveScript 发展而来的原型化继承的基于对象的动态类型的区分大小写的客户端脚本语言,主要目的是为了解决服务器端语言,比如 Perl,遗留的速度问题,为客户提供更流畅的浏览效果。
这里是自己在工作不太忙的时候写出来了一个用户可以自定义的滚动条 jscroll,以下简称 jscroll。jscroll 默认只提供一种滚动条样式,部分样式来自 google webstore ,其中有部分 CSS3 样式主要用于实现圆角,阴影效果
主流浏览器默认为 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() 方法实现。
- //计算滚动条的高度
- scrollbarHeight: function(elem, height) {
- var value = elem.scrollHeight;
- return (height / value) * height
- }
scrollbarHeight() 方法通过简单的数学计算返回滚动条元素应该显示的高度。
- initEvent: function() {
- var that = this,
- _default,
- elem,
- top,
- min,
- max,
- prev,
- parent,
- sbody,
- unit;
- //滚动条滚动
- squid.on(document, 'mousemove', function(event) {
- elem = that.scrolling.elem
- if(elem !== null) {
- squid.addClass(elem, 'scrolling')
- top = event.clientY - that.scrolling.diffy
- parent = that.ie6 ? elem.parentNode.parentNode : elem.parentNode
- min = that.limits[elem].min
- max = that.limits[elem].max
- prev = parent.previousSibling
- sbody = prev.tagName.toLowerCase() === 'div' ? prev : prev.previousSibling
- _default = parseInt(sbody['data-height'] || sbody.getAttribute('data-height'), 10)
- unit = (sbody.scrollHeight - _default) / max
- squid.addClass(sbody.parentNode, 'unselectable')
- if(top < min) {
- top = min
- }else if(top > max) {
- top = max
- }
- elem.style.top = top + 'px'
- that.doScroll(sbody, top * unit)
- }
- })
- //滚动结束
- squid.on(document, 'mouseup', function(event) {
- elem = that.scrolling.elem
- if(elem) {
- prev = that.ie6 ? elem.parentNode.parentNode.previousSibling : elem.parentNode.previousSibling
- sbody = prev.tagName.toLowerCase() === 'div' ? prev : prev.previousSibling
- squid.removeClass(sbody.parentNode, 'unselectable')
- }
- that.scrolling.elem = null
- that.scrolling.diffy = 0
- })
- }
initEvent() 方法实现了为 document 元素添加 mousemove 和 mouseup 事件,mousemove 实现了在拖动滚动条元素滚动时查看的内容跟随变化。代码首先判断当前是否有拖动滚动条查看内容的操作,如果有就计算滚动条被拖动到的位置,然后计算查看内容应该到的地方。代码里对 ie6 的判断,是因为引入的 PIE.htc 文件破坏了原有的结构(为了实现跨浏览器下显示效果的一致,付出太大了!!!)。mouseup 事件处理程序实现了清除上次操作的滚动条元素。
//添加滚动条事件 regEvent: function(elem) { var that = this, sbody = elem.firstChild, list = sbody.nextSibling, //滚动条元素 s = list.firstChild, //滚动条滚动最小值 min = 0, //滚动条滚动最大值 max = list.offsetHeight - s.offsetHeight, _default = parseInt(sbody['data-height'] || sbody.getAttribute('data-height'), 10), unit = (sbody.scrollHeight - _default) / max, //firefox浏览器 firefox = 'MozBinding' in document.documentElement.style, //鼠标滚轮事件 mousewheel = firefox ? 'DOMMouseScroll' : 'mousewheel', //opera浏览器 opera = window.oprea && navigator.userAgent.indexOf('MSIE') === -1, //is firing mousedown event firing = false, //鼠标点击,定时器执行时间 interval, //滚动条距离容器高度 top, //滚动条当前top值 cur, //每次滚动多少像素 speed = 18; //变量缓存min, max this.limits[s] = { min: 0, max: max } //scroll事件 鼠标滑动滚轮移动滚动条 squid.on(elem, mousewheel, function(event) { var delta; if(event.wheelDelta) { delta = opera ? -event.wheelDelta / 120 : event.wheelDelta / 120 }else{ delta = -event.detail / 3 } cur = parseInt(s.style.top || 0, 10) //向上滚动 if(delta > 0) { top = cur - speed if(top < min) { top = min } }else{//向下滚动 top = cur + speed if(top > max) { top = max } } s.style.top = top + 'px' that.doScroll(sbody, top * unit) //阻止body元素滚动条滚动 event.preventDefault() }) //ie6, 7, 8下,如果鼠标连续点击两次且时间间隔太短,则第二次事件不会触发 //拖动滚动条,点击滚动条可到达区域 squid.on(list, 'mousedown', function(event) { var target = event.target, y = event.clientY; target = event.target if(target.tagName.toLowerCase() === 'shape') target = s //鼠标点击元素是滚动条 if(target === s) { //invoke elem setCapture s.setCapture && s.setCapture() that.scrolling.diffy = y - s.offsetTop //鼠标移动过程中判断是否正在拖动滚动条 that.scrolling.elem = s }else if(target.className.match('jscroll-list')){ firing = true interval = setInterval(function() { if(firing) { that.mouseHandle(list, y, unit) } }, 80) } }) //鼠标松开滚动条停止滚动 squid.on(list, 'mouseup', function() { //invoke elem releaseCapture s.releaseCapture && s.releaseCapture() firing = false clearInterval(interval) }) //滚动条元素获取焦点,可以触发keyup事件 squid.on(s, 'click', function() { this.focus() }) //滚动条获取焦点后,触发键盘上下键,滚动条滚动 squid.on(s, 'keydown', function(event) { var keyCode = event.keyCode, state = false; cur = parseInt(s.style.top || 0, 10) switch(keyCode) { case 38: top = cur - speed if(top < min) { top = min } state = true break case 40: top = cur + speed if(top > max) { top = max } state = true break default: break } if(state) { s.style.top = top + 'px' that.doScroll(sbody, top * unit) } event.preventDefault() }) }
regEvent() 方法实现了以下功能,应该是 jscroll 组件的核心方法了:
1. 鼠标在包含滚动条的元素区域,上下滚动鼠标滚轮,查看的内容跟随滚轮上下翻的功能
2. 点击滚动条可到达区域,即滚动条上方或者下方,滚动条和查看的内容向上或者向下滚动。鼠标点击滚动条可到达区域不松开,可连续滚动滚动条和查看的内容,通过调用 mouseHandle() 方法来具体实现该功能。
3. 点击滚动条元素后,可以通过键盘上下键来触发滚动条和查看内容的滚动
//鼠标点击滚动条可到达区域上面或者下面时,滚动条滚动 mouseHandle: function(elem, place, unit) { var prev = elem.previousSibling, //包含滚动条显示内容元素 a = prev.tagName.toLowerCase() === 'div' ? prev : prev.previousSibling, // n = elem.firstChild, //滚动条元素 s = this.ie6 ? n.lastChild : n.tagName.toLowerCase() === 'div' ? n : n.nextSibling, //滚动条高度 height, //list元素距body的top值 value, //滚动条距离容器高度 top, //滚动条距body的top值 sTop, //滚动条滚动最小值 min, //滚动条滚动最大值 max, //每点击滚动条可到达区域,滚动条向下或向上移动10px step = 10, //鼠标点击滚动条可到达区域距离最顶端或者最底端小于distance时,滚动条能够自动移动到最顶端或者最低端 distance = 20; min = this.limits[s].min max = this.limits[s].max height = s.offsetHeight top = parseInt(s.style.top || 0, 10) value = squid.getOffset(elem).top sTop = squid.getOffset(s).top //鼠标点击滚动条下方区域,滚动条向下滚动 if(place > sTop) { if(value + elem.offsetHeight - place < distance && (elem.offsetHeight - height - top) < distance) { top = max }else{ if((sTop + height + step) <= place) { top += step }else{ top = place - value - height } } }else{ //鼠标点击区域距滚动条顶端小于滚动条长度时,滚动条自动滚动到最顶端 if(place - value < distance && top < distance) { top = min }else{ //滚动条距页面顶部高度减去鼠标clientY值大于step if(sTop - place >= step) { top -= step }else{ top = place - value } } } if(top < min) { top = min }else if(top > max) { top = max } s.style.top = top + 'px' this.doScroll(a, top * unit) }
mouseHandle() 方法通过判断鼠标点击位置在页面中的位置坐标,和滚动条元素在页面中的位置来判断是点击了滚动条的上方区域还是下方区域。如果点击了下方区域则滚动条向下滚动,否则向上滚动,对于点击的位置在上方区域或者下方区域小于 distance 值时,滚动条自动滚动到最小值或者最大值。
显示效果
该控件的 demo 使用了淘宝网用户注册协议内容,因为 firefox、chrome 等高级浏览器都能保证很好的兼容性和显示效果,所以这里只展示 ie 低版本浏览器显示效果, ie 浏览器显示截图如下:
ie6 下
初始化之后
滚动过程中
滚动到底部
ie7
滚动条初始化之后
滚动过程中
滚动到最底部
ie9
开始滚动前
滚动过程中
滚动到最底部
总结:基本的功能实现代码就这么多了,可能分析的不够细致,里面涉及最多的也许就是位置的计算,事件的绑定处理。如果有什么问题,欢迎一起沟通、学习、交流。
注意:PIE.htc 文件路径要放正确,引用时写成绝对路径,否则在 ie6, 7, 8 下没有 css3 的效果(如果那样我代码里所做的兼容处理就没啥意义了!),需要改变引用路径的话可以在 jscroll-1.0.css 文件中修改。最后附上源码,欢迎感兴趣者下载试用。
来源: http://www.phperz.com/article/17/0413/281294.html