主流のブラウザでは選択要素の描画方法が異なるため、各ブラウザでの表示も異なります。最も重要なのは、デフォルトでは UI が粗すぎ、CSS で美化してもあまり美しい効果を実現できないことです。これは、UX を重視する私たちフロントエンド開発者にとっては耐えられません。したがって、プロジェクトがそれほど忙しくないときに、シミュレートされた選択コントロールを作成する予定です。次に、実装の詳細、発生した問題、使用方法について説明します。
1. 実装の詳細
init: function(context) {
//指定されたコンテキストのすべての select 要素を取得します
var elems = suck.getElementsByTagName('select', context)
this.globalEvent()
this.initView(elems)
}
ユーザー登録アプリケーションのシナリオでは、複数の select 要素があります。シミュレートされた選択コントロール (以下、jselect と呼びます) 初期化メソッドは、ページ上のすべての選択要素を取得し、グローバル イベント globalEvent をバインドし、ページを初期化して initView を表示します。 globalEvent メソッドは次のとおりです:
globalEvent: function() {
//Document はクリック イベントを追加し、ユーザーは各 jselect 要素の展開と終了を処理します
var target,
className,
elem,
wrapper,
status,
that = this;
squid.on(document, 'click', function(event) {
target = events.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 マウスの右ボタンをクリックすると、クリック イベントがトリガーされます
//マウスの左ボタンをクリックすると、実行されます
if( event.button === 0) {
//初期化選択要素
that.initSelected(elem)
if(squid.isHidden(wrapper)) {
status = '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
}
} )
}
globalEvent は、ドキュメント上のクリック イベントのバインドを実装し、イベント プロキシを使用して、現在クリックされている要素は、ページ上でクリック イベントがトリガーされたときに処理する必要があるターゲット要素です。コード内のステートメントの分岐は次のとおりです。 現在クリックされている jselect 要素を展開します。ドロップダウンでクリックしたリスト項目を選択し、jselect を閉じる必要があるかどうかを判断します。
initView メソッドは次のとおりです:
initView: function(elems) {
var i = 0,
elem,
length = elems.length,
enabled;
for(; i < length; i ) {
elem = elems[i]
enabled = elem.getAttribute('data-enabled')
//システム選択を使用
if(!enabled || Enabled === 'true')
続行
if(squid.isVisible(elem))
elem.style.display = 'none'
this.create(elem)
}
}
initView は、jselect で置き換える必要がある select 要素を非表示にし、create メソッドを呼び出して単一の jselect の全体構造を生成し、ページに挿入して置き換えるメソッドを実装します。デフォルトの選択位置。
作成メソッドは次のとおりです:
create: function(elem) {
var data = [],
i = 0,
長さ,
オプション,
オプション,
値,
text、
obj、
lis、
ul、
_default、
icon、
selectedText、
selectedValue、
div、
ラッパー、
位置、
左、
上、
cssText;
options = elem.getElementsByTagName('option')
length = options.length
for(; i < length; i ) {
option = options[i]
値 = option.value
テキスト = option.innerText || option.textContent
obj = {
value: 値,
text: テキスト
}
if(option.selected) {
selectedValue = 値
selectedText = text
obj['selected'] = true
}
data.push(obj)
}
lis = this.render(this.tmpl, data)
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元素能够获取焦点
_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)
//選択元素の次面に挿入
elem.parentNode.insertBefore(div, null)
//获取選択元素左上值
//先设設置選択显示,取完左、上值後重新隐藏
elem.style.display = 'block'
// イベント绑定
this.sysEvent(div)
position = suck.position(elem)
elem.style.display = 'none'
left =position.left
top =position.top
cssText = 'left: ' left 'px;トップ: ' トップ 'px;表示: ブロック;'
div.style.cssText = cssText
}
create メソッド实现了将系统select データ集合を jselect 下拉列表,jselect の層は select のクラスを持っています-wrapper の要素包含、ここには選択された要素を保存するために使用されるクラスが select-default の要素、クラスが select-icon の要素報告用の要素、これは下のアルファベット表、クラスは select-list の div 要素がここに含まれています1ul 要素は、システムが選択したオプションのテキストと値で、li 要素にそれぞれ保存されます。sysEvent メソッドは、jselect ポイント拡張開始ダウンドロー テーブル イベントおよびボックス 上下選択のダウンドロー要素の選択に使用されます。 squid.position メソッドは、システム選択要素の offsetParent に対する位置を取得するために使用されます。つまり、ここでは、取得システム選択要素のオフセットを先頭に取得し、左に移動してから、それぞれ親のオフセットを取り除きます。
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、
値、
ディレクトリ、
最小 = 0、
最大、
非表示 = 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
}
}
}
このメソッドは、ユーザーが選択したコンテンツを保存するために使用されるクラス select-default の div 要素を受け取ります。具体的な実装方法は、最初にすべてのオプションを走査してクラスが選択された li 要素を取得し、それをマークすることです。 activate メソッドを通じて現在選択されているとおりです。ここで計算する必要があるものがあります。つまり、ドロップダウン リストが展開されるたびに、選択した要素をページの表示領域までスクロールする必要があります。ドロップダウン リストには多くのコンテンツが存在する可能性がありますが、ドロップダウン リストの外側の選択リストには最大の高さがあり、その最大の高さを超えると、スクロール バーが表示されます。デフォルトでは実行されないため、選択した要素がスクロール バーの下、またはスクロール バーの上のスクロール バーの下にある可能性があるため、コンテナのスクロール バーの位置をリセットするには計算が必要です。具体的には、選択されたコンテンツがスクロール バーの上に表示されるか下に表示されるかは、選択された要素の offsetTop 値が外側のコンテナ選択リストの実際の高さの半分より大きいかどうかによって決まります。は inView メソッドです。 inView メソッドは次のとおりです
inView: function (elem, Wrapper, dir ) {
varscrollTop =wrapper.scrollTop,
//選択された要素 offsetTop
offsetTop = elem.offsetTop,
top;
if(dir = == 'up' ) {
if(offsetTop === 0) {
//一番上までスクロールバー
wrapper.scrollTop = offsetTop
}else if(offsetTop 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)
}
}
}
inView メソッドは、上にスクロールするかどうかを決定する必要があります。引き続き下にスクロールします。scrollInView メソッドのコードは、ドロップダウン リストの外側のコンテナのscrollTop を指定された値に設定するだけです。このメソッドは次のように実装されます
scrollInView: function (elem, top) {
setTimeout(function() {
elem.scrollTop = top
}, 10)
}
このメソッドは setTimeout とJavaScript の実行キューでの主な問題は、IE8 で展開されたドロップダウン リストのスクロール バーが、コードによって設定されたスクロールトップを無視して、最終的に一番上までスクロールしてしまうことです (パフォーマンスの観点から、また、scrollTop の設定も有効になるようですが、最終的にはスクロール バーが上部にリセットされます。なぜ IE8 でこの問題が発生するのかはわかりません。) 選択した要素はビジュアル領域内に表示できません。他のブラウザでは問題は発生しません。
全体の実装の詳細はおおよそこれだけです。キーボードの上下キーを押して Enter キーを押してドロップダウン リストを閉じるロジックは非常に簡単です。
発生した問題
キーボードのキーダウン、キーアップ、キー押下イベントに応答するために div にフォーカスを取得させる方法、Google に応答する方法 (Google は異常時に使いにくい。誰にもそれができるわけがない)いくつかの情報を検索した結果、div 要素がフォーカスを取得してユーザーの操作に応答できるように、div 要素に tabindex 属性を設定する必要があることが最終的にわかりました。デフォルトでは、ブラウザはダブルクリックまたは頻繁にクリックすることによって現在の領域を選択するため、このデフォルトの操作をキャンセルしてユーザーに快適な操作を提供するには、選択できない属性を div 要素に追加する必要があります。この問題は、IE ブラウザにのみ適用されます。他のブラウザでは、選択できないクラス名を追加することで回避できます。その他の問題は、論理制御と一部の位置計算ですが、ここでは説明しません。
使い方 まず、ページテンプレート内で jselect で置換したい要素を非表示にするか、デフォルトでは jselect はページ上のすべての選択を取得して置換します。 jselect で置き換えたくない場合は、select 要素
にカスタム属性 data-enabled="true" を追加する必要があります。もちろん、data-enabled="false" を追加すると、このカスタム属性がない場合と同様に jselect に置き換えられます。より複雑なレイアウト構造のページでは、使用中に他の問題が発生する可能性があります。私がテストしたページ構造は非常に単純なので、テストしていない可能性があります。
jselect を使用するには、まず、squid.js を導入し、次に jselect-1.0.js、jselect-1.0.css ファイルをインポートする必要があります。jselect を呼び出す必要がある場合は、次の呼び出しメソッドを使用して jselect を初期化します。 Swing.jselect();
注: jselect のソース コードとデモは、
ここからダウンロードできます。