Home > Web Front-end > JS Tutorial > JavaScript simulates select, jselect method implementation_javascript skills

JavaScript simulates select, jselect method implementation_javascript skills

WBOY
Release: 2016-05-16 17:48:28
Original
1222 people have browsed it

Since mainstream browsers render select elements differently, the display in each browser is also different. The most important thing is that the UI is too rough by default, and even if it is beautified through CSS, it cannot achieve a very beautiful effect. This is intolerable to us front-end developers who focus on UX. So when the project is not too busy, I plan to write a simulated select control. Next, I will share with you the implementation details, problems encountered and how to use it.
1. Implementation details
init: function(context) {
//Get all select elements of the specified context
var elems = squid.getElementsByTagName('select', context)
this.globalEvent()
this.initView(elems)
}
In a user registration application scenario, there are multiple select elements. The simulated select control (hereinafter referred to as jselect) initialization method will obtain all select elements on the page, then bind the global event globalEvent, and initialize the page to display initView. The globalEvent method is as follows:

Copy code The code is as follows:

globalEvent: function() {
//Document adds a click event, and the user handles the expansion and closing of each jselect element
var target,
className,
elem,
wrapper,
status,
that = this;

squid.on(document, 'click', function(event) {
target = event.target,
className = target.className;

switch(className) {
case 'select-icon':
case 'select-default unselectable':
elem = target.tagName.toLowerCase() === 'div' ? target : target.previousSibling
wrapper = elem .nextSibling.nextSibling

//firefox The right mouse button will trigger the click event
//The left mouse button clicks to execute
if(event.button === 0) {
//Initialization selection Element
that.initSelected(elem)
if(squid.isHidden(wrapper)) {
status = 'block'
//Close all expanded jselect
that.closeSelect()
}else{
status = 'none'
}
wrapper.style.display = status
elem.focus()
}else if(event.button === 2){
wrapper.style.display = 'none'
}
that.zIndex(wrapper)
break
case 'select-option':
case 'select-option selected':
if(event.button === 0) {
that.fireSelected(target, target.parentNode.parentNode.previousSibling.previousSibling)
wrapper.style.display = 'none'
}
break
default:
while(target && target.nodeType !== 9) {
if(target.nodeType === 1) {
if(target.className === ' select-wrapper') {
return
}
}
target = target.parentNode
}
that.closeSelect()
break
}
} )
}

globalEvent implements binding the click event on the document, and then uses the event proxy to determine whether the currently clicked element is the target element that needs to be processed when the click event is triggered on the page. The judgment condition is based on the class of the element. The branches of the statements in the code are: expand the currently clicked jselect element drop-down, select the clicked list item, and determine whether jselect needs to be closed.

The initView method is as follows:
Copy code The code is as follows:

initView: function(elems) {
var i = 0,
elem,
length = elems.length,
enabled;

for(; i < length; i ) {
elem = elems[i]
enabled = elem.getAttribute('data-enabled')
//Use system select
if(!enabled || enabled === 'true')
continue
if(squid.isVisible(elem))
elem.style.display = 'none'

this.create(elem)
}
}

initView implements the method of hiding the select elements that need to be replaced with jselect and then calling the create method to generate the overall structure of a single jselect and insert it into the page and replace the default select position.

The create method is as follows:
Copy code The code is as follows:

create: function(elem) {
var data = [],
i = 0,
length,
option,
options,
value,
text,
obj,
lis,
ul,
_default,
icon,
selectedText,
selectedValue,
div,
wrapper,
position,
left,
top,
cssText;

options = elem.getElementsByTagName('option')
length = options.length
for(; i < length; i ) {
option = options[i]
value = option.value
text = option.innerText || option.textContent

obj = {
value: value,
text: text
}
if(option.selected) {
selectedValue = value
selectedText = text
obj['selected'] = true
}
data.push(obj)
}

lis = this.render(this.tmpl, data)
ul = '
    ' lis '
'
//
div = document.createElement('div')
div.style.display = 'none'
div.className = 'select-wrapper'
//已选元素
_default = document.createElement('div')
_default.className = 'select-default unselectable'
_default.unselectable = 'on'
//让div元素能够获取焦点
_default.setAttribute('tabindex', '1')
_default.setAttribute('data-value', selectedValue)
_default.setAttribute('hidefocus', true)
_default.innerHTML = selectedText
div.appendChild(_default)
//选择icon
icon = document.createElement('span')
icon.className = 'select-icon'
div.appendChild(icon)
//下拉列表
wrapper = document.createElement('div')
wrapper.className = 'select-list hide'
wrapper.innerHTML = ul
//生成新的元素
div.appendChild(wrapper)
//插入到select元素后面
elem.parentNode.insertBefore(div, null)
//获取select元素left top值
//先设置select显示,取完left, top值后重新隐藏
elem.style.display = 'block'
//事件绑定
this.sysEvent(div)
position = squid.position(elem)
elem.style.display = 'none'
left = position.left
top = position.top
cssText = 'left: ' left 'px; top: ' top 'px; display: block;'
div.style.cssText = cssText
}

create方法实现了将系统select数据拷贝到jselect下拉列表,jselect的层级关系是最外层有一个class为select-wrapper的元素包裹,里面有class为select-default的元素用于存放已选的元素,class为select-icon的元素用户告诉用户这是一个下拉列表,class为select-list的div元素里面包含了一个ul元素里面是从系统select拷贝的option的文本和值分别存放在li元素的文本和data-value属性。sysEvent方法是为jselect添加点击展开关闭下拉列表事件以及键盘上下选择下拉元素回车选中下拉元素事件。squid.position方法用于获取系统select元素相对于其offsetParent的位置,这里与获取系统select元素的offset是有区别。其实就是获取自己的offset得到top,left值然后分别减去offsetParent获取的offset的top,left值。最后是把jselect插入到系统select元素后面,显示到页面。

jselect创建的基本流程就是上面描述的这样,剩下就是细节地方的实现,比如说:点击展开下拉显示上次已选择的元素,具体实现该功能的是initSelected方法如下
复制代码 代码如下:

initSelected: function(elem) {
var curText = elem.innerText || elem.textContent,
curValue = elem.getAttribute('data-value'),
wrapper = elem.nextSibling.nextSibling,
n = wrapper.firstChild.firstChild,
text,
value,
dir,
min = 0,
max,
hidden = false;

for(; n; n = n.nextSibling) {
text = n.innerText || n.textContent
value = n.getAttribute('data-value')
if(curText === text && curValue === value) {
//显示已选中元素
if(squid.isHidden(wrapper)) {
wrapper.style.display = 'block'
hidden = true
}
max = wrapper.scrollHeight
if(n.offsetTop > (max / 2)) {
if(wrapper.clientHeight wrapper.scrollTop === max)
dir = 'up'
else
dir = 'down'
}else{
if(wrapper.scrollTop === min)
dir = 'down'
else
dir = 'up'
}
this.inView(n, wrapper, dir)
if(hidden)
wrapper.style.display = 'none'
this.activate(n)
break
}
}
}

This method receives the div element with class select-default, which is used to store the content that the user has selected. The specific implementation method is to first traverse all options to obtain the li element with class selected, and mark it as currently selected through the activate method. element. There is something that needs to be calculated here, that is, each time the drop-down list is expanded, the selected element must be scrolled to the visible area of ​​the page. Because there may be a lot of content in the drop-down list, but the outer select-list of the drop-down list will have a maximum height. If the maximum height is exceeded, a scroll bar will appear. If the calculation is not performed by default, the selected element may be under the scroll bar or under the scroll bar. above the scroll bar, so calculations are needed to reset the position of the container scroll bar. Specifically, whether the selected content is displayed above or below the scroll bar depends on whether the offsetTop value of the selected element is greater than half the actual height of the outer container select-list. The way to display the selected element into the visual area is the inView method. The inView method is as follows
Copy code The code is as follows:

inView: function(elem, wrapper, dir ) {
var scrollTop = wrapper.scrollTop,
//Selected element offsetTop
offsetTop = elem.offsetTop,
top;

if(dir === 'up' ) {
if(offsetTop === 0) {
//Scroll bar to top
wrapper.scrollTop = offsetTop;
}else if(offsetTop < scrollTop) {
top = offsetTop - scrollTop
//The scroll bar scrolls to the top value
this.scrollInView(wrapper, top)
}
}else{
var clientHeight = wrapper.clientHeight;

if(offsetTop elem.offsetHeight === wrapper.scrollHeight) {
wrapper.scrollTop = wrapper.scrollHeight - wrapper.clientHeight
}else if(offsetTop elem.offsetHeight > clientHeight scrollTop) {
top = (offsetTop elem.offsetHeight) - (scrollTop clientHeight)
this.scrollInView(wrapper, top)
}
}
}

The inView method needs to determine whether to scroll up Still scrolling down, the scrollInView method code is simply to set the scrollTop of the outer container of the drop-down list to the specified value. The method is implemented as follows
Copy code The code is as follows:

scrollInView: function(elem, top) {
setTimeout(function() {
elem.scrollTop = top
}, 10)
}

This method is implemented in setTimeout and a delay is added to In the javascript execution queue, the main problem is that the scroll bar of the expanded drop-down list under IE8 will eventually scroll to the top, ignoring the scrollTop set by the code (from the performance point of view, it seems that the setting of scrollTop can also take effect, but in the end the scroll bar will be reset to the top , I don’t know why IE8 has this problem.) The selected element cannot be displayed within the visual area. This problem does not occur in other browsers.
The entire implementation details are roughly this much. The logic of pressing the up and down keys on the keyboard and pressing the Enter key to close the drop-down list is very simple.
Problems encountered
How to let div get focus to respond to keyboard keydown, keyup, keypress events, to Google (Google is not easy to use in extraordinary times, there is no way who can let us Features) After searching some information, I finally found that I need to set the tabindex attribute for the div element, so that the div element can gain focus and respond to user operations. Because the browser will select the current area by double-clicking or clicking too frequently by default. In order to cancel this default operation and give users a good experience, you need to add an attribute unselectable to the div element. However, this attribute can only be applied to IE browsers. This problem can be avoided in other browsers by adding a class name that is unselectable. Other issues are logical control and some position calculations, which will not be discussed here.
How to use
First, hide the elements you want to replace with jselect in the page template or do no processing. By default, jselect will get all the selects on the page and replace them in sequence. If you don’t want jselect to replace them, The select element
needs to add the custom attribute data-enabled="true". Of course, adding data-enabled="false" will be replaced by jselect just like without this custom attribute. During use, there may be other problems with pages with more complex layout structures. Because the page structure I tested is very simple, I may not have tested it out.
To use jselect, you need to introduce squid.js first, and then import the jselect-1.0.js, jselect-1.0.css files. Where jselect needs to be called, initialize jselect through the following calling method: squid.swing.jselect();
Note: jselect source code and demo can be downloaded from here.

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template