You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

501 lines
17 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*!
* js模拟系统select v1.0
* http://www.cnblogs.com/typeof/
*
* 主流浏览器对html的select元素渲染都不一样IE系列(6, 7, 8)也不一样,
* firefoxchromesafariopera 渲染和事件处理也稍有差异
* 该脚本解决了在不同浏览器下渲染和事件响应不一致的问题对系统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);