欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

javascript模擬select,jselect的方法實(shí)現(xiàn)

 更新時(shí)間:2012年11月08日 15:46:58   作者:  
由于主流瀏覽器對(duì)select元素渲染不同,所以在每種瀏覽器下顯示也不一樣,最主要的是默認(rèn)情況下UI太粗糙,即使通過css加以美化也不能達(dá)到很美觀的效果
由于主流瀏覽器對(duì)select元素渲染不同,所以在每種瀏覽器下顯示也不一樣,最主要的是默認(rèn)情況下UI太粗糙,即使通過css加以美化也不能達(dá)到很美觀的效果。這對(duì)于我們這些專注于UX的前端開發(fā)人員是無法容忍的。于是在項(xiàng)目不太忙的時(shí)候,就計(jì)劃寫一個(gè)模擬的select控件出來。接下來就把實(shí)現(xiàn)的細(xì)節(jié)、遇到的問題以及如何使用和大家分享一下。
1. 實(shí)現(xiàn)細(xì)節(jié)
init: function(context) {
//獲取指定上下文所有select元素
var elems = squid.getElementsByTagName('select', context)
this.globalEvent()
this.initView(elems)
}
在一個(gè)用戶注冊(cè)的應(yīng)用場(chǎng)景,有多個(gè)select元素。模擬的select控件(以下簡(jiǎn)稱jselect)初始化方法會(huì)獲取頁面上所有select元素,然后綁定全局事件globalEvent,初始化頁面顯示initView。globalEvent方法如下:
復(fù)制代碼 代碼如下:

globalEvent: function() {
//document 添加click事件,用戶處理每個(gè)jselect元素展開關(guān)閉
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 鼠標(biāo)右鍵會(huì)觸發(fā)click事件
//鼠標(biāo)左鍵點(diǎn)擊執(zhí)行
if(event.button === 0) {
//初始化選中元素
that.initSelected(elem)
if(squid.isHidden(wrapper)) {
status = 'block'
//關(guān)閉所有展開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實(shí)現(xiàn)了在document綁定click事件,然后在頁面上觸發(fā)點(diǎn)擊事件的時(shí)候通過事件代理來判斷當(dāng)前點(diǎn)擊元素是否是需要進(jìn)行處理的目標(biāo)元素,判斷條件是通過元素的class,代碼中語句的分支分別是:展開當(dāng)前點(diǎn)擊的jselect元素下拉、選中點(diǎn)擊列表項(xiàng)、判斷是否需要關(guān)閉jselect。

initView方法如下:
復(fù)制代碼 代碼如下:

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

for(; i < length; i++) {
elem = elems[i]
enabled = elem.getAttribute('data-enabled')
//使用系統(tǒng)select
if(!enabled || enabled === 'true')
continue
if(squid.isVisible(elem))
elem.style.display = 'none'

this.create(elem)
}
}

initView實(shí)現(xiàn)了將需要使用jselect替換的select元素先隱藏然后調(diào)用create方法,生成單個(gè)jselect的整體結(jié)構(gòu)并插入到頁面并替代默認(rèn)select位置。

create方法如下:
復(fù)制代碼 代碼如下:

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'
//已選元素
_default = document.createElement('div')
_default.className = 'select-default unselectable'
_default.unselectable = 'on'
//讓div元素能夠獲取焦點(diǎn)
_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值
//先設(shè)置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方法實(shí)現(xiàn)了將系統(tǒng)select數(shù)據(jù)拷貝到j(luò)select下拉列表,jselect的層級(jí)關(guān)系是最外層有一個(gè)class為select-wrapper的元素包裹,里面有class為select-default的元素用于存放已選的元素,class為select-icon的元素用戶告訴用戶這是一個(gè)下拉列表,class為select-list的div元素里面包含了一個(gè)ul元素里面是從系統(tǒng)select拷貝的option的文本和值分別存放在li元素的文本和data-value屬性。sysEvent方法是為jselect添加點(diǎn)擊展開關(guān)閉下拉列表事件以及鍵盤上下選擇下拉元素回車選中下拉元素事件。squid.position方法用于獲取系統(tǒng)select元素相對(duì)于其offsetParent的位置,這里與獲取系統(tǒng)select元素的offset是有區(qū)別。其實(shí)就是獲取自己的offset得到top,left值然后分別減去offsetParent獲取的offset的top,left值。最后是把jselect插入到系統(tǒng)select元素后面,顯示到頁面。

jselect創(chuàng)建的基本流程就是上面描述的這樣,剩下就是細(xì)節(jié)地方的實(shí)現(xiàn),比如說:點(diǎn)擊展開下拉顯示上次已選擇的元素,具體實(shí)現(xiàn)該功能的是initSelected方法如下
復(fù)制代碼 代碼如下:

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
}
}
}

該方法接收class為select-default的div元素即用于存放用戶已選擇內(nèi)容的元素,具體實(shí)現(xiàn)方式是先遍歷所有選項(xiàng)獲取class有selected的li元素,通過activate方法標(biāo)示為當(dāng)前已選中的元素。這里有一個(gè)需要計(jì)算的地方,就是每次展開下拉列表都要將已選中的元素滾動(dòng)到頁面可視區(qū)。因?yàn)橛锌赡芟聛砹斜韮?nèi)容很多,但是下拉列表的外層select-list會(huì)有一個(gè)最大的高度,超過最大高度會(huì)出現(xiàn)滾動(dòng)條,默認(rèn)不做計(jì)算的話有可能已選中的元素會(huì)在滾動(dòng)條下面或者是滾動(dòng)條上面,所以需要通過計(jì)算來重置容器滾動(dòng)條的位置。具體是已選中內(nèi)容顯示到滾動(dòng)條的上面還是下面需要根據(jù)已選中元素的offsetTop值是否大于外層容器select-list的實(shí)際高度一半,把已選中元素顯示到可視區(qū)的方式是inView方法。inView方法如下
復(fù)制代碼 代碼如下:

inView: function(elem, wrapper, dir) {
var scrollTop = wrapper.scrollTop,
//已選中元素offsetTop
offsetTop = elem.offsetTop,
top;

if(dir === 'up') {
if(offsetTop === 0) {
//滾動(dòng)條置頂
wrapper.scrollTop = offsetTop;
}else if(offsetTop < scrollTop) {
top = offsetTop - scrollTop
//滾動(dòng)條滾動(dòng)到top值
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)
}
}
}

inView方法需要判斷是向上滾動(dòng)還是向下滾動(dòng),scrollInView方法代碼很簡(jiǎn)單就是把下拉列表外層容器的scrollTop設(shè)置為指定的值。方法實(shí)現(xiàn)如下
復(fù)制代碼 代碼如下:

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

這個(gè)方法實(shí)現(xiàn)放到了setTimeout里面做了一個(gè)延遲添加到j(luò)avascript執(zhí)行隊(duì)列里面,主要解決的是IE8下展開下拉列表滾動(dòng)條會(huì)最終滾動(dòng)到頂部,忽略代碼設(shè)置的scrollTop(從表現(xiàn)上來看好像對(duì)scrollTop的設(shè)置也能生效,但是最后會(huì)重置滾動(dòng)條到頂部,不知道IE8為什么會(huì)有這個(gè)問題。),不能把已選中的元素顯示到可視區(qū)范圍,其他瀏覽器下不會(huì)有這個(gè)問題。
整個(gè)的實(shí)現(xiàn)細(xì)節(jié)大致就這么多,鍵盤上下鍵回車鍵,關(guān)閉下拉列表邏輯都很簡(jiǎn)單。
遇到的問題
如何讓div獲取焦點(diǎn)來響應(yīng)鍵盤keydown, keyup, keypress事件,到谷歌(非常時(shí)期谷歌都不好用了,沒辦法誰讓這是咱的特色呢)查找一些資料最后發(fā)現(xiàn)需要為div元素設(shè)置tabindex屬性,這樣就可以讓div元素獲取焦點(diǎn),來響應(yīng)用戶的操作。因?yàn)闉g覽器在默認(rèn)情況下雙擊或者是點(diǎn)擊太頻繁的話會(huì)選中當(dāng)前區(qū)域,為了取消這個(gè)默認(rèn)操作給用戶一個(gè)好的體驗(yàn)需要為div元素添加一個(gè)屬性u(píng)nselectable,不過這個(gè)屬性只能適用于IE瀏覽器,其他瀏覽器下可以通過添加一個(gè)class名字是unselectable來避免這個(gè)問題。其他的問題都是邏輯上的控制,還有一些位置的計(jì)算了,這里就不再說了。
使用方法
首先是在頁面模板把希望通過jselect替換的元素隱藏或者不做任何處理,默認(rèn)情況下jselect會(huì)獲取頁面所有select依次替換,如果不希望jselect替換的select元素
需要添加自定義屬性data-enabled="true"。當(dāng)然添加data-enabled="false"和沒有這個(gè)自定義屬性一樣都會(huì)被jselect替換。在使用的過程中可能對(duì)于布局結(jié)構(gòu)比較復(fù)雜的頁面還會(huì)有其他的問題,因?yàn)槲覝y(cè)試的頁面結(jié)構(gòu)很簡(jiǎn)單,所以可能沒有測(cè)試出來。
使用jselect需要先引入squid.js,然后引入jselect-1.0.js, jselect-1.0.css文件,在需要調(diào)用jselect的地方通過如下的調(diào)用方式來初始化jselect:squid.swing.jselect();
注:jselect源碼以及demo可以通過這里下載。

相關(guān)文章

最新評(píng)論