|
|
/*!
|
|
|
* js模拟系统select v1.0
|
|
|
* http://www.cnblogs.com/typeof/
|
|
|
*
|
|
|
* 主流浏览器对html的select元素渲染都不一样,IE系列(6, 7, 8)也不一样,
|
|
|
* firefox,chrome,safari,opera 渲染和事件处理也稍有差异
|
|
|
* 该脚本解决了在不同浏览器下渲染和事件响应不一致的问题,对系统select是完全
|
|
|
* 意义上的替换。v1.0版本只支持单个select选择即不支持二级或者三级联动且不支持系统select的onchange事件。
|
|
|
* 该版本支持模拟select选择的数据和系统select选中数据的同步,不影响form表单的提交
|
|
|
*
|
|
|
* 如果page上select有的不想通过该脚本替换,只想维持系统select,可以在相应的select元素添加自定义属性data-enabled="true",
|
|
|
* 否则可以在想要通过该脚本替换的select元素上添加自定义属性data-enabled="false"或者不加,会默认为这个select需要
|
|
|
* 通过该脚本进行替换
|
|
|
*
|
|
|
* 日期:2012-11-07 15:38
|
|
|
*/
|
|
|
(function(squid) {
|
|
|
function JSelect() {
|
|
|
this.init()
|
|
|
}
|
|
|
|
|
|
JSelect.prototype = {
|
|
|
constructor: JSelect,
|
|
|
init: function(context) {
|
|
|
//获取指定上下文所有select元素
|
|
|
var elems = squid.getElementsByTagName('select', context)
|
|
|
this.globalEvent()
|
|
|
this.initView(elems)
|
|
|
},
|
|
|
initView: function(elems) {
|
|
|
var i = 0,
|
|
|
elem,
|
|
|
length = elems.length,
|
|
|
enabled;
|
|
|
|
|
|
for(; i < length; i++) {
|
|
|
elem = elems[i]
|
|
|
enabled = elem.getAttribute('data-enabled')
|
|
|
//使用系统select
|
|
|
if(!enabled || enabled === 'true')
|
|
|
continue
|
|
|
if(squid.isVisible(elem))
|
|
|
elem.style.display = 'none'
|
|
|
|
|
|
this.create(elem)
|
|
|
}
|
|
|
},
|
|
|
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('span')
|
|
|
div.style.display = 'none'
|
|
|
div.className = 'select-wrapper'
|
|
|
//已选元素
|
|
|
_default = document.createElement('span')
|
|
|
_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('i')
|
|
|
icon.className = 'select-icon'
|
|
|
div.appendChild(icon)
|
|
|
//下拉列表
|
|
|
wrapper = document.createElement('span')
|
|
|
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 = ' display: inline-block;'
|
|
|
div.style.cssText = cssText
|
|
|
},
|
|
|
globalEvent: function() {
|
|
|
//document 添加click事件,用户处理每个jselect元素展开关闭
|
|
|
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() === 'span' ? target : target.previousSibling
|
|
|
wrapper = elem.nextSibling.nextSibling
|
|
|
|
|
|
//firefox 鼠标右键会触发click事件
|
|
|
//鼠标左键点击执行
|
|
|
if(event.button === 0) {
|
|
|
//初始化选中元素
|
|
|
that.initSelected(elem)
|
|
|
if(squid.isHidden(wrapper)) {
|
|
|
status = 'inline-block'
|
|
|
//关闭所有展开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
|
|
|
}
|
|
|
})
|
|
|
},
|
|
|
sysEvent: function(elem) {
|
|
|
var stand = elem.firstChild,
|
|
|
dropdown = elem.lastChild,
|
|
|
target,
|
|
|
//firefox = 'MozBinding' in document.documentElement.style,
|
|
|
chrome = /chrome/i.test(navigator.userAgent),
|
|
|
keyup = chrome ? 'keypress' : 'keyup',
|
|
|
that = this;
|
|
|
|
|
|
squid.on(elem, 'mouseover', function(event) {
|
|
|
if(!that.doScrolling) {
|
|
|
target = event.target
|
|
|
that.activate(target)
|
|
|
}
|
|
|
})
|
|
|
|
|
|
squid.on(elem, 'mouseout', function(event) {
|
|
|
if(!that.doScrolling) {
|
|
|
target = event.target
|
|
|
that.deactivate(target)
|
|
|
}
|
|
|
})
|
|
|
|
|
|
squid.on(stand, 'keydown', function(event) {
|
|
|
var keyCode = event.keyCode;
|
|
|
|
|
|
switch(keyCode) {
|
|
|
//回车选中
|
|
|
case 13:
|
|
|
that.enter(dropdown)
|
|
|
break
|
|
|
//向上键
|
|
|
case 38:
|
|
|
that.doScrolling = true
|
|
|
that.up(dropdown)
|
|
|
break
|
|
|
//向下键
|
|
|
case 40:
|
|
|
that.doScrolling = true
|
|
|
that.down(dropdown)
|
|
|
break
|
|
|
default:
|
|
|
break
|
|
|
}
|
|
|
})
|
|
|
|
|
|
squid.on(stand, keyup, function(event) {
|
|
|
var keyCode = event.keyCode;
|
|
|
|
|
|
switch(keyCode) {
|
|
|
//回车选中
|
|
|
case 13:
|
|
|
that.doScrolling = false
|
|
|
break
|
|
|
//向上键
|
|
|
case 38:
|
|
|
that.doScrolling = false
|
|
|
break
|
|
|
//向下键
|
|
|
case 40:
|
|
|
that.doScrolling = false
|
|
|
break
|
|
|
default:
|
|
|
break
|
|
|
}
|
|
|
})
|
|
|
},
|
|
|
zIndex: function(elem) {
|
|
|
var index = 10,
|
|
|
cur = elem.parentNode.parentNode,
|
|
|
next = squid.next(cur);
|
|
|
|
|
|
if(next) {
|
|
|
cur.style.zIndex = index
|
|
|
next.style.zIndex = --index
|
|
|
}
|
|
|
},
|
|
|
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
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
activate: function(elem) {
|
|
|
var tagName = (elem.tagName || '').toLowerCase(),
|
|
|
className = elem.className,
|
|
|
parent = elem.parentNode,
|
|
|
first = parent.firstChild,
|
|
|
last = parent.lastChild;
|
|
|
|
|
|
switch(tagName) {
|
|
|
case 'li':
|
|
|
//li.select-option 元素
|
|
|
if(!~className.indexOf('selected') && (elem !== first || elem !== last)) {
|
|
|
this.deactivate(elem)
|
|
|
elem.className = className + ' selected'
|
|
|
}
|
|
|
break
|
|
|
default:
|
|
|
break
|
|
|
}
|
|
|
},
|
|
|
deactivate: function(elem) {
|
|
|
var tagName = (elem.tagName || '').toLowerCase(),
|
|
|
className = (' ' + elem.className + ' ').replace(/[\n\r\t]/, '');
|
|
|
|
|
|
switch(tagName) {
|
|
|
case 'li':
|
|
|
//li.select-option 元素
|
|
|
var i = 0,
|
|
|
lis = squid.siblings(elem),
|
|
|
length = lis.length,
|
|
|
cur;
|
|
|
|
|
|
for(; i < length; i++) {
|
|
|
cur = lis[i]
|
|
|
cur.className = squid.trim(className.replace(' selected ', ''))
|
|
|
}
|
|
|
break
|
|
|
default:
|
|
|
break
|
|
|
}
|
|
|
},
|
|
|
fireSelected: function(elem, s) {
|
|
|
var text = elem.innerText || elem.textContent,
|
|
|
value = elem.getAttribute('data-value'),
|
|
|
r;
|
|
|
|
|
|
s.setAttribute('data-value', value)
|
|
|
if(s.innerText)
|
|
|
s.innerText = text
|
|
|
else
|
|
|
s.textContent = text
|
|
|
|
|
|
//触发系统select选中,用于form表单提交
|
|
|
r = s.parentNode.previousSibling.previousSibling
|
|
|
r.value = value
|
|
|
r.setAttribute('data-text', text)
|
|
|
},
|
|
|
closeSelect: function() {
|
|
|
var elems = squid.getElementsByClassName('select-list'),
|
|
|
i = 0,
|
|
|
length = elems.length,
|
|
|
elem;
|
|
|
|
|
|
for(; i < length; i++) {
|
|
|
elem = elems[i]
|
|
|
if(squid.isVisible(elem))
|
|
|
elem.style.display = 'none'
|
|
|
}
|
|
|
},
|
|
|
up: function(elem) {
|
|
|
var ul = elem.firstChild,
|
|
|
lis = ul.childNodes,
|
|
|
li = this.getSelectedIndex(lis),
|
|
|
cur,
|
|
|
i = li.index;
|
|
|
|
|
|
if(i > 0) {
|
|
|
i--
|
|
|
cur = lis[i]
|
|
|
//判断元素是否in view
|
|
|
this.inView(cur, elem, 'up')
|
|
|
this.activate(cur)
|
|
|
this.fireSelected(cur, elem.previousSibling.previousSibling)
|
|
|
}
|
|
|
},
|
|
|
down: function(elem) {
|
|
|
var ul = elem.firstChild,
|
|
|
lis = ul.childNodes,
|
|
|
li = this.getSelectedIndex(lis),
|
|
|
cur,
|
|
|
i = li.index;
|
|
|
|
|
|
if(i < lis.length - 1) {
|
|
|
i++
|
|
|
cur = lis[i]
|
|
|
//判断元素是否in view
|
|
|
this.inView(cur, elem, 'down')
|
|
|
this.activate(cur)
|
|
|
this.fireSelected(cur, elem.previousSibling.previousSibling)
|
|
|
}
|
|
|
},
|
|
|
enter: function(elem) {
|
|
|
var ul = elem.firstChild,
|
|
|
lis = ul.childNodes,
|
|
|
li,
|
|
|
i,
|
|
|
cur;
|
|
|
|
|
|
li = this.getSelectedIndex(lis)
|
|
|
i = li.index
|
|
|
cur = lis[i]
|
|
|
this.fireSelected(cur, elem.previousSibling.previousSibling)
|
|
|
this.closeSelect()
|
|
|
},
|
|
|
getSelectedIndex: function(elems) {
|
|
|
var i = 0,
|
|
|
length = elems.length,
|
|
|
elem;
|
|
|
|
|
|
for(; i < length; i++) {
|
|
|
elem = elems[i]
|
|
|
if(~elem.className.indexOf('selected')) {
|
|
|
return {
|
|
|
index: i
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return {
|
|
|
index: -1
|
|
|
}
|
|
|
},
|
|
|
inView: function(elem, wrapper, dir) {
|
|
|
var scrollTop = wrapper.scrollTop,
|
|
|
offsetTop = elem.offsetTop,
|
|
|
top;
|
|
|
|
|
|
if(dir === 'up') {
|
|
|
if(offsetTop === 0) {
|
|
|
wrapper.scrollTop = offsetTop;
|
|
|
}else if(offsetTop < scrollTop) {
|
|
|
top = offsetTop - scrollTop
|
|
|
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)
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
scrollInView: function(elem, top) {
|
|
|
setTimeout(function() {
|
|
|
elem.scrollTop += top
|
|
|
}, 10)
|
|
|
},
|
|
|
doScrolling: false,
|
|
|
render: function(tmpl, data) {
|
|
|
var i = 0,
|
|
|
cur,
|
|
|
length = data.length,
|
|
|
prop,
|
|
|
value,
|
|
|
item,
|
|
|
r = [];
|
|
|
|
|
|
for(; i < length; i++) {
|
|
|
cur = data[i]
|
|
|
item = tmpl.replace(/\{\{\w+\}\}/g, function(a) {
|
|
|
prop = a.replace(/[\{\}]+/g, '')
|
|
|
value = cur[prop] || ''
|
|
|
if(prop === 'class') {
|
|
|
value += 'select-option'
|
|
|
if(cur.selected) {
|
|
|
value += ' selected'
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return value
|
|
|
})
|
|
|
r.push(item)
|
|
|
}
|
|
|
|
|
|
return r.join('')
|
|
|
},
|
|
|
tmpl: '<li class="{{class}}" data-value="{{value}}">{{text}}</li>'
|
|
|
}
|
|
|
|
|
|
squid.swing.jselect = function() {
|
|
|
return new JSelect()
|
|
|
}
|
|
|
})(squid); |