Simulate the select using javascript and implement the jselect Method

Source: Internet
Author: User

Because mainstream browsers have different rendering effects on select elements, the display varies in different browsers. The most important thing is that the UI is too rough by default, even if it is beautified by css, it cannot produce beautiful results. This is intolerable for our front-end developers who focus on UX. So when the project is not busy, we plan to write a simulated select control. Next we will share with you the implementation details, problems encountered, and how to use them.
1. Implementation Details
Init: function (context ){
// Obtain all select elements in 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 initialization method of the simulated select control (jselect) obtains all the select elements on the page, binds the global event globalEvent, and initView is displayed on the initialization page. The globalEvent method is as follows:
Copy codeThe Code is as follows:
GlobalEvent: function (){
// Add a click event to the document. The user processes each jselect element and closes it.
Var target,
ClassName,
Elem,
Wrapper,
Status,
That = this;

Squid. on (document, 'click', function (event ){
Target = event.tar get,
ClassName = target. className;

Switch (className ){
Case 'select-icon ':
Case 'select-default unselectable ':
Elem = target. tagName. toLowerCase () = 'div '? Target: target. previussibling
Wrapper = elem. nextSibling. nextSibling

// Right-click firefox to trigger the click Event
// Click the left mouse button to execute
If (event. button = 0 ){
// Initialize the selected Element
That. initSelected (elem)
If (squid. isHidden (wrapper )){
Status = 'block'
// Close all expanded jselect statements.
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. previussibling. previussibling)
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 binds a click event in the document, and then triggers a click event on the page. It uses the Event Agent to determine whether the current click element is the target element to be processed, the judgment condition is through the element class. The branches of the statements in the Code are: Expand the jselect element drop-down for the current click, select the click list item, and determine whether to disable jselect.

The initView method is as follows:
Copy codeThe 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 the system select
If (! Enabled | enabled = 'true ')
Continue
If (squid. isVisible (elem ))
Elem. style. display = 'none'

This. create (elem)
}
}

InitView hides select elements that need to be replaced by jselect, and then calls the create method to generate the overall structure of a single jselect, insert it to the page, and replace the default select position.

The create method is as follows:
Copy codeThe 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 = '<ul class = "select-item">' + lis + '</ul>'
//
Div = document. createElement ('div ')
Div. style. display = 'none'
Div. className = 'select-wrapper'
// Selected Element
_ Default = document. createElement ('div ')
_ Default. className = 'select-default unselectable'
_ Default. unselectable = 'on'
// Enable the div element to obtain the focus
_ Default. setAttribute ('tabindex', '1 ')
_ Default. setAttribute ('data-value', selectedValue)
_ Default. setAttribute ('hidefo', true)
_ Default. innerHTML = selectedText
Div. appendChild (_ default)
// Select icon
Icon = document. createElement ('span ')
Icon. className = 'select-icon'
Div. appendChild (icon)
// Drop-down list
Wrapper = document. createElement ('div ')
Wrapper. className = 'select-list hide'
Wrapper. innerHTML = ul
// Generate new elements
Div. appendChild (wrapper)
// Insert it to the end of the select Element
Elem. parentNode. insertBefore (div, null)
// Obtain the left top value of the select Element
// Set select display first, obtain left, top value, and hide again
Elem. style. display = 'block'
// Event binding
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.css Text = cssText
}

The create method copies the system select data to the jselect drop-down list. The hierarchical relationship of jselect is that the outermost layer has an element package whose class is select-wrapper, there are select-default elements used to store the selected elements. The element whose class is select-icon tells the user that this is a drop-down list, the div element whose class is select-list contains the option text and values copied from the system select statement in the text and data-value attributes of the li element. The sysEvent method is to add and click events for jselect to expand and close the drop-down list and press enter to select the drop-down events. The squid. position method is used to obtain the position of the system select element relative to its offsetParent, which is different from obtaining the offset of the system select element. In fact, it is to get its own offset to get the top and left values, and then subtract the top and left values of the offset obtained by offsetParent respectively. Insert jselect to the system select element and display it on the page.

The basic process of jselect creation is described above, and the rest is the implementation of the details. For example, click to expand the drop-down list to display the last selected element. The initSelected method is as follows:
Copy codeThe Code is as follows:
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. nextSibling ){
Text = n. innerText | n. textContent
Value = n. getAttribute ('data-value ')
If (curText = text & curValue = value ){
// Display selected elements
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 = 'low'
} Else {
If (wrapper. scrollTop === min)
Dir = 'low'
Else
Dir = 'up'
}
This. inView (n, wrapper, dir)
If (hidden)
Wrapper. style. display = 'none'
This. activate (n)
Break
}
}
}

This method receives the div element whose class is select-default, that is, the element used to store the user's selected content. The specific implementation method is to traverse all options to obtain the li element with selected in the class, the activate method is used to identify the currently selected element. Here is a place to calculate, that is, each time you expand the drop-down list, you must scroll the selected elements to the page visible area. There may be a lot of content in the list, but the outer select-list in the drop-down list has a maximum height, and a scroll bar will appear when the maximum height is exceeded, if no calculation is performed by default, the selected elements may be under the scroll bar or above the scroll bar. Therefore, you need to reset the position of the scroll bar of the container through calculation. Check whether the selected content is displayed on the scroll bar or whether the offsetTop value of the selected element is half the actual height of the select-list of the outer container, the inView method is used to display selected elements to the visible area. The inView method is as follows:
Copy codeThe Code is as follows:
InView: function (elem, wrapper, dir ){
Var scrollTop = wrapper. scrollTop,
// The selected element offsetTop
OffsetTop = elem. offsetTop,
Top;

If (dir = 'up '){
If (offsetTop = 0 ){
// Top the scroll bar
Wrapper. scrollTop = offsetTop;
} Else if (offsetTop <scrollTop ){
Top = offsetTop-scrollTop
// Scroll the scroll bar 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 or down. The scrollInView method code is to set scrollTop of the outer container in the drop-down list to the specified value. The method is as follows:
Copy codeThe Code is as follows:
ScrollInView: function (elem, top ){
SetTimeout (function (){
Elem. scrollTop + = top
}, 10)
}

This method is implemented in setTimeout and added to the javascript Execution queue with a delay. The main solution is that the scroll bar in the expanded drop-down list under IE8 will eventually scroll to the top, ignore the scrollTop set by the Code (it seems that the scrollTop setting can also take effect in terms of performance, but it will reset the scroll bar to the top, I don't know why IE8 has this problem .), You cannot display the selected elements in the visible area. This issue does not occur in other browsers.
The entire implementation details are roughly as follows: keyboard up and down keys and enter keys, and closing the drop-down list logic is quite simple.
Problems encountered
How to get the focus of the div to respond to the keydown, keyup, and keypress events on the keyboard to Google (Google was not easy to use in many times, so no one can make this a special feature) finally, you need to set the tabindex attribute for the div element, so that the div element can get the focus to respond to user operations. Because the browser double-click by default or click too frequently, the current region is selected. to cancel this default operation, you need to add an unselectable attribute to the div element for a good user experience, however, this attribute can only be applied to IE. In other browsers, you can add a class named unselectable to avoid this problem. Other problems are logical control, and there are still some locations for computing. I will not talk about it here.
Usage
First, in the page template, the elements that you want to replace with jselect are hidden or not processed. By default, jselect obtains all the select elements on the page to replace in sequence. If you do not want to replace the select elements with jselect
You need to add the custom property data-enabled = "true ". Of course, adding data-enabled = "false" will be replaced by jselect if this custom attribute is not available. In the process of use, there may be other problems for pages with complicated layout structures, because the Page Structure I tested is very simple and may not be tested.
To use jselect, You need to first introduce squid. js, then introduce the jselect-1.0.js, jselect-1.0.css file, and initialize jselect: squid. swing. jselect () through the following call method where you need to call jselect ();
Note: The jselect source code and demo can be downloaded here.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.