The scroll bars provided by mainstream browsers for HTML elements by default are not beautiful, and it is impossible for front-end developers to beautify them with a unified style through CSS. For example, IE can achieve simple beautification through styles, and the Webkit kernel browser can control the display effect of the scroll bar. Firefox does not allow users to define styles for the scroll bar. But front-end developers who pursue a friendly user experience will not be stopped by the inconsistent behavior of these browsers. We can implement custom scroll bars ourselves through standard html element simulation.
Here is a user-definable scroll bar jscroll that I wrote when I was not too busy at work, hereafter referred to as jscroll. By default, jscroll only provides one scroll bar style. Some styles come from google webstore, and some of them are mainly used to achieve rounded corners and shadow effects. Some css3 styles are mainly used to achieve rounded corners and shadow effects. In order to achieve consistent scroll bar display effects across browsers, the PIE.htc file is introduced for browsers that do not support CSS3 in IE6, 7, and 8. Next, we will explain the implemented functions, compatibility, usage methods, and specific code implementation.
Implement functions and compatibility
jscroll implements almost all the functions of the system's default scroll bar, such as: dragging the scroll bar to view content, rolling the mouse wheel to view content, clicking the scroll bar to reach the top or bottom of the area to trigger scrolling of the scroll bar, keyboard up and down keys to trigger scrolling of the scroll bar. The latest browsers such as firefox, chrome, and ie9 support the latest API of css3 and js, so there are no compatibility issues. IE6, 7, and 8 do not support css3. The hack file of PIE.htc is introduced for compatibility processing. In terms of js, compatibility with unsupported APIs is made through old APIs. The browser with the biggest compatibility problem is IE6. It does not support clicking the scroll bar to reach the area to trigger the scroll bar to scroll, nor does it support the up and down keys on the keyboard to trigger the scroll bar to scroll. The main reason for this problem is the introduction of the PIE.htc file that supports css3. If the hack file is not introduced, all operations can be supported. In order to have a consistent display effect, we have to choose not to support some functions.
How to use
The most common use of custom scroll bars should be in the pop-up layer of the page, or in a certain area on the page. Never customize the scroll bar of the entire page. For elements that need to use jscroll, you need to add the custom attribute data-scroll="true" to tell the program that you need to use jscroll to replace the system default scroll bar. You also need to add the custom attributes data-width="", data- height="" to specify the width and height of the element to be displayed. jscroll will calculate the display width of the content and the height of the scroll bar based on the user-defined width and height and add interactive events.
Detailed code implementation
The implementation logic of jscroll is not complicated. The js code to implement specific functions is less than 400 lines, but it relies on some basic methods, so it is necessary to introduce squid.js as the support of basic methods. The css that controls the scroll bar style is in a separate jscroll-1.0.css file. Users can modify and extend it to meet their own needs. The following is a simple analysis of each method to achieve specific functions:
init: function(selector, context) {
selecotr = selector || 'data-scroll'
context = context || document
var elems = squid.getElementsByAttribute(selector, context)
this.initView(elems)
}
init() is an initialization function. It gets the elements that need to use custom scroll bars based on the specified selector and context. The default selector is data -scroll, the context defaults to the current document. Here, regardless of the element's custom attribute data-scroll="true" or data-scroll="false", the custom scroll bar will be used to overwrite the system default scroll bar. Squid's getElementsByAttribute() method only provides the ability to find elements based on whether the element has specified attributes. Ignoring the attribute value, this method is not as powerful as the jquery selector or the querySelectorAll() method provided by advanced browsers, because Squid only provides the most basic support here. After finding the element that needs to be customized, call the initView method to initialize the overall structure and display of the scroll bar.
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()
}
The initView() method will first traverse the elements with the custom attribute data-scroll obtained on the page, and determine whether a scroll bar will appear on each element through the hasScroll() method. If scroll bars appear on the element, call the create() method to create custom scroll bars respectively. When the initView() method ends, the initEvent() method will be called.
//Whether there is a scroll bar
hasScroll: function (elem) {
return elem.offsetHeight < elem.scrollHeight
}
hasScroll() method is used to determine whether the scroll bar will appear on the element and returns true or false. The margin and padding of the element are ignored here. The default margin and padding of the scroll bar created through jscroll are both 0.
//Create the overall structure of the scroll bar element
create : function(elem) {
var wrapper,
list,
//Scroll bar element
s,
//Height displayed with scroll bar element
height = elem[' data-height'] || elem.getAttribute('data-height'),
//The width of the element with scroll bar
width = elem['data-width'] || elem.getAttribute(' data-width'),
//Scroll bar display height
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'
list.unselectable = 'on'
squid.css(list, {
height: (height - 5) 'px'
} )
//Scroll bar
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)
/ /Wrap the elements where scroll bars need to appear
elem.parentNode.replaceChild(wrapper, elem)
wrapper.insertBefore(elem, list)
//Scroll bar height
value = this.scrollbarHeight(elem, height)
squid.css(s, {
height: value 'px'
})
//add event
this.regEvent(wrapper )
}
create() method The user adjusts the overall structure of creating elements with custom scroll bars. First, a wrapper element is created to wrap the elements elem and where scroll bars will appear. Scroll bar scrollable area element list and scroll bar element s. Set the width and height of the wrapper element respectively through the custom attributes data-width and data-height set from the scroll bar element. The height displayed by the scroll bar element is calculated through the scrollbarHeight() method, and the overall structure is not complicated. After creating the overall structure of the custom scroll bar, add event processing for the scroll bar element s and the scroll bar reachable area element list, which is implemented through the regEvent() method.
//Calculate the height of the scroll bar
scrollbarHeight: function(elem, height) {
var value = elem.scrollHeight;
return (height / value) * height
}
scrollbarHeight() method is passed simply The mathematical calculation returns the height that the scrollbar element should display.
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事件处理程序实现了清除上次操作的滚动条元素。
//Add scroll bar event
regEvent: function(elem) {
var that = this,
sbody = elem.firstChild,
list = sbody.nextSibling,
//Scroll bar element
s = list.firstChild,
//Scroll bar scrolling minimum value
min = 0,
//Scroll bar scrolling maximum value
max = list.offsetHeight - s.offsetHeight,
_default = parseInt(sbody['data-height'] || sbody.getAttribute('data-height'), 10),
unit = (sbody.scrollHeight - _default) / max ,
//firefox browser
firefox = 'MozBinding' in document.documentElement.style,
//mousewheel event
mousewheel = firefox ? 'DOMMouseScroll' : 'mousewheel',
//opera browser
opera = window.oprea && navigator.userAgent.indexOf('MSIE') === -1,
//is firing mousedown event
firing = false,
/ /Mouse click, timer execution time
interval,
//Scroll bar height from container
top,
//Scroll bar current top value
cur,
//Every time How many pixels to scroll
speed = 18;
//Variable cache min, max
this.limits[s] = {
min: 0,
max: max
}
//scroll event mouse slides the wheel to move the scroll bar
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)
//Scroll up
if(delta > 0) {
top = cur - speed
if(top < min) {
top = min
}
}else{//scroll down
top = cur speed
if(top > max) {
top = max
}
}
s.style.top = top 'px'
that.doScroll(sbody, top * unit)
//Prevent the body element scroll bar from scrolling
event.preventDefault()
})
//ie6, 7, 8, if the mouse is clicked twice in succession and the time interval is too short, the second event will not be triggered
//Drag the scroll bar and click to scroll Reachable area
squid.on(list, 'mousedown', function(event) {
var target = event.target,
y = event.clientY;
target = event .target
if(target.tagName.toLowerCase() === 'shape')
target = s
//The element clicked by the mouse is a scroll bar
if(target === s) {
//invoke elem setCapture
s.setCapture && s.setCapture()
that.scrolling.diffy = y - s.offsetTop
//Judged during mouse movement Is the scroll bar being dragged? function() {
if(firing) {
that.mouseHandle(list, y, unit)
}
}, 80)
}
})
//The mouse releases the scroll bar to stop scrolling
squid.on(list, 'mouseup', function() {
//invoke elem releaseCapture
s.releaseCapture && s.releaseCapture()
firing = false
clearInterval(interval)
})
//The scroll bar element gets focus and can trigger the keyup event
squid.on(s, 'click', function () {
this.focus()
})
//After the scroll bar obtains focus, trigger the up and down keys on the keyboard, and the scroll bar scrolls
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() method implements the following functions and should be the core method of jscroll component:
1. When the mouse is in the element area containing the scroll bar, roll the mouse wheel up and down, and the viewed content will follow the scroll wheel up and down
2. Click the scroll bar to reach the area, that is, above or below the scroll bar. The scroll bar and the viewed content can scroll up or down. When the mouse clicks on the scroll bar, it can reach the area without releasing it, and the scroll bar and the viewed content can be continuously scrolled. This function is specifically implemented by calling the mouseHandle() method.
3. After clicking the scroll bar element, you can use the up and down keys on the keyboard to trigger the scroll bar and scroll the content
Copy code
The code is as follows:
//When the mouse clicks on the scroll bar to reach the top or bottom of the area, the scroll bar scrolls
mouseHandle: function(elem, place, unit) {
var prev = elem.previousSibling,
//Include scroll bar display content element
a = prev.tagName.toLowerCase() === 'div' ? prev : prev.previousSibling,
//
n = elem.firstChild,
//Scroll bar element
s = this.ie6 ? n.lastChild : n.tagName.toLowerCase() === 'div' ? n : n.nextSibling,
//Scroll bar height
height ,
//The top value of the list element from the body
value,
//The height of the scroll bar from the container
top,
//The top value of the scroll bar from the body
sTop,
//Scroll bar scrolling minimum value
min,
//Scroll bar scrolling maximum value
max,
//Each time you click the scroll bar to reach the area, the scroll bar moves down or up 10px
step = 10,
//When the distance to the top or bottom of the area that can be reached by clicking the mouse on the scroll bar is less than distance, the scroll bar can automatically move to the top or bottom
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
//Click the area below the scroll bar with the mouse, and the scroll bar will scroll down
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 {
//When the mouse click area is less than the length of the scroll bar from the top of the scroll bar, the scroll bar automatically scrolls to the top
if(place - value < distance && top < distance) {
top = min
}else{
//The height of the scroll bar from the top of the page minus the mouse clientY value is greater than 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)
}
The
mouseHandle() method determines whether the upper or lower area of the scroll bar is clicked by judging the position coordinates of the mouse click position on the page and the position of the scroll bar element on the page. If the lower area is clicked, the scroll bar scrolls down, otherwise it scrolls up. When the clicked position is in the upper area or the lower area is less than the distance value, the scroll bar automatically scrolls to the minimum or maximum value.
Display effect
The demo of this control uses the content of Taobao user registration agreement. Because advanced browsers such as Firefox and Chrome can ensure good compatibility and display effects, only the display effects of low-version IE browsers are shown here. IE Browsing The screenshot of the device display is as follows:
ie6 under
After initialization
While scrolling
Scroll to the bottom
ie7
After the scroll bar is initialized
While scrolling
Scroll to the bottom
ie9
Before you start scrolling
While scrolling
Scroll to the bottom
Summary: There are only so many basic function implementation codes, and the analysis may not be detailed enough. The most involved ones may be the calculation of position and the binding processing of events. If you have any questions, you are welcome to communicate, learn and exchange together.
Note: The path to the PIE.htc file must be placed correctly, and the absolute path must be written when quoting, otherwise there will be no css3 effect under ie6, 7, and 8 (in that case, the compatibility processing done in my code will be It makes no sense!), if you need to change the reference path, you can modify it in the jscroll-1.0.css file. Finally, the source code is attached. Interested parties are welcome to download and try it out.