MaterializeCSSで困ったこととその回避方法
以前にDate Pickerの日本語化で紹介したMaterializeCSSですが、使い込んでいくうちにいくつか困ったことが発生し、なんとかそれを回避する方法を見つけたのでそのノウハウを紹介します。
1. jQuery UI Autocompleteとの組み合わせ
MaterializeCSS自体にはオートコンプリート(サジェスト)の機能がないので他のライブラリと組み合わせて実現することになります。
「どのライブラリを使うか?」と考えたとき、MaterializeCSSがjQueryに依存していることから候補は必然的にjQuery UIということになるわけですが、この2つをそのまま組み合わせると、
- MaterializeCSSのToolTipが使えなくなる
- jQuery UI付属のTheme CSSがMaterializeCSSの世界観と合わない
という問題がありました。
前者はjQuery UIにもToolTipが存在してそれと干渉しているのが原因なので、jQueryUI Download BuilderにてWidgets>Autocompleteのみをチェック(依存関係のある機能は自動チェックされる)したカスタムJSファイルを生成、使用すればOKです。
後者はMaterializeCSSの世界観に合わせたCSSを自作するしかないです。Bootstrapのような著名なCSSフレームワークはそれ用のTheme CSSが作成されているようですが、MaterializeCSSはまだそこまで至ってないようなので。
Theme CSSを自作するといってもAutocomplete機能だけに限ればこの程度で十分です。
<style> .ui-autocomplete { background-color: #fff; box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12); cursor: pointer; position: absolute; } .ui-front { z-index: 100 } .ui-menu-item { color: #26a69a; font-size: 1.2rem; padding: 1rem 1rem; } .ui-state-focus { background-color: #f2f2f2 !important; } </style>
2. HTML5 required属性との組み合わせ
HTML5にはinputやselectタグに"required"属性が追加されており、クライアントサイドでの必須入力チェック(validation)が簡単に実装できるようになっています。
一方でMaterializeCSSのFormsコンポーネント、特にmaterial_selectについては本来のselectタグを隠して別のDOMを生成・表示することで実現していますから、selectタグにrequired属性を指定してもチェック結果が表示されません。
ではどうやって回避するか?ですが、「本来のselectタグが非表示だからダメ」なのであれば「非表示じゃなくてサイズ0で表示するようにすれば良くね?」ということで、こんなことをしてみました。
$(document).ready(function() { $("select").material_select(); $("select[required]").css({display: "inline", height: 0, padding: 0, width: 0}); // required対策 }
MaterializeCSSのSelectを使うためには「初期化」として.material_select()の呼び出しが必要ですが、その直後にrequired属性を持ったselectタグについてサイズ0でinline表示するCSSを強制適応する、というかなり強引な方法です。
validationのポップアップが本来表示されるべき位置より若干下に表示されるのがイマイチですが、これで一応目的は達成されます。
[Google Chromeでの表示例]
3. selectのスクロールバーをクリックするとセレクトボックスが閉じてしまう
material_selectにはもう一つ問題があって、「セレクトボックスのスクロールバーをクリック(またはドラッグ)するとボックスが閉じてしまう」という不具合が発生します。
特にIEで顕著に発生し、chromeでもたまに起こることがありますが、こうなると選択肢が多数存在する場合に下のほうにある候補を選択できなくなるというかなり致命的な問題です。
調査したところ、「スクロールバーはdiv要素の内側に表示されているにも拘わらず、それをクリックするとblur(=lost focus)イベントが発生してしまう」というChromiumのIssueを発見しました。
どうやらこれと同じことが発生しているようなので、上記Issueで提示されていた回避コードをMaterliazeCSSに適応するコードを書いてみました。
$(document).ready(function() { $("select").material_select(); var onmousedown = function(e) { // preventing the default still allows the scroll, but blocks the blur. // We're inside the scrollbar if the clientX is >= the clientWidth. if (e.clientX >= e.target.clientWidth || e.clientY >= e.target.clientHeight) { e.preventDefault(); } }; $("select").siblings("input.select-dropdown").on("mousedown", onmousedown); }
mousedownイベントハンドラは「(スクロールバーが含まれる)client領域内であればイベントを無視する」というコードです。
これを先ほどのrequiredと同様、material_selectの初期化直後にselect-dropdownクラスのinputタグだけに限定適応することで、
- スクロールバーをクリック(またはドラッグ)してもセレクトボックスは閉じない
- 選択肢をクリックするとセレクトボックスは自動で閉じる
- セレクトボックス外をクリックしてもセレクトボックスは閉じる
がIE11でもきちんと実現できるようになりました(Issue中で指摘されている「HTML及びBODYを除外すべき」は不要)。
で、これを本体に組み込めるようにしたパッチをPull Requestしてみたのですが、なぜか「セレクトボックスが閉じなくなる問題が発生するから却下」という回答が…
手元の環境(Win7及びWin8.1)では何も問題ないので、この記事を読んで同じ問題に悩んでいる方はぜひ追試とコメントをお願いします。