jQuery Deferredで作るオリジナル確認ダイアログ


ある意味、前回(オリジナルのダイアログを閉じた時に、元の要素にフォーカスを戻す方法 | while(isプログラマ))のつづき。今回は確認ダイアログ版。

ただ、今回のエントリーはダイアログを作ることではなく、jQuery Deferredの勉強のメモです。最近になってjQuery Deferredについて調べてみました。まだまだ分かっていないことは多いのですが、試しにjQuery Deferredを使った確認ダイアログを作成することにしました。

今回作るのは、標準のconfirmメソッドを使うと下記のようなものです。

つまり、確認ダイアログを表示して、『OK』を押せばYahoo!JAPANへ、『キャンセル』を押せばGoogleへ飛ぶという処理です(キャンセルなら何もしないのが普通じゃないかと思われそうですが、分かりやすいようにどちらも何かしらの動作をするようにします)。

ただ、これだとダイアログのレイアウトを変更できないので、独自のダイアログを作成することに。ついでに、上記のYahoo!やGoogleに飛ぶ処理をjQuery Deferredで実現することにしました。試しに書いてみたコードが下記です。

サンプル:ダイアログのテスト

ようは、OKボタンを押した時にresolveメソッドを呼び、doneメソッドの引数に入れた関数を呼ぶ。キャンセルボタンを押した時にrejectメソッドを呼び、failメソッドの引数に入れた関数を呼ぶという処理をしています。

このぐらいならjQuery Deferredを使う必要はないんですけどね。そもそもキャンセルボタンを押すことが何でreject(異常終了)なのかと突っ込まれそうです。多分、この例はjQuery Deferredを使う例としては間違っていると思います。

ところで、jQuery Deferredを調べていると、Deferredオブジェクトの取得に『new $.Deferred』としているページが多々ありました。なので、Deferredオブジェクトかどうかの判定には『obj instanceof $.Deferred』とすればいいだろうと思ったのですが、結果はfalseになってしまいました。なぜだろう? と思って、jQueryのコードを見てみると、Deferredメソッド内で作成したオブジェクトをreturnしており、Deferred自身のインスタンスを生成しているわけではないようです。jQueryの公式ページのサンプルコードでは、Deferredオブジェクトの取得にnewをつけていません(deferred.promise() | jQuery API Documentation)。やたら、newを付けている解説ページが多いのだけど、なぜそうなったのだろうか・・・。

ところで、今回作成したopenConfirmメソッドでは、promiseメソッドで取得したpromiseオブジェクトを返していますが、ここはDeferredオブジェクトをそのまま返しても結果は変わりません。promiseオブジェクトとの違いとしては、promiseオブジェクトにはresolveやrejectがありません。それだけの違いですが、一般的にpromiseオブジェクトを返すようにするそうです。この辺りは、書きページの『deferred図解』の『なぜわざわざpromiseを渡すのか』を参考にしてください。
参考:jQuery deferredの使い方 – deferredの基本 | CodeGrid

jQuery Deferredはいろいろ応用ができそうな機能です。複数のDeferredオブジェクトを使ってwhenメソッドを使うとか。ただ、自分自身よく分かってないところもあるので、もう少し勉強しようと思います。

オリジナルのダイアログを閉じた時に、元の要素にフォーカスを戻す方法


先日、コーディングWebアクセシビリティ – WAI-ARIAで実現するマルチデバイス環境のWebアプリケーションという本を読みました。Webアクセシビリティについて書かれた実践的な本で、WAI-ARIAについて全くと言っていいほど知らなかった自分には、もっと入門的な本も必要だと思いました(本の内容が難しいとうわけではありません。書いてあることは易しかったのですが、自分はこの本の想定読者のレベルに達してないというだけです)。ただ、この本を読んでもっとWebアクセシビリティを意識したサイトを作っていこうと思えたので、そういう意味では読んでよかったと思います。

さて、本を読み終わったらいつもブクログに本の感想というか批評というか日記というか、とにかくその本について思ったことを書くのですが、なんと今回はそのレビューページを、この本の編集の方に見てもらえたらしく、さらにレビューに書いた疑問を監訳者の方にまで見てもらえたようです(『コーディングWebアクセシビリティ – WAI-ARIAで実現するマルチデバイス環境のWebアプリケーション』のレビュー ヘイドン・ピカリング (amano225さん) – ブクログ)。たまにこうやって関係者からのコメントをもらえることがあるのですが、見られることを想定して書いてるわけではないので、嬉しくもありますが、どこか恥ずかしい気もちもあり、複雑なところです(さすがにオープンな場に書いている以上、見られたくないとは思っていません)。

ところで、自分が疑問に思ったのは本書の最後にほうに書いてあるダイアログについての記述です。ダイアログを閉じる時に、そのダイアログを開く前にフォーカスのあった要素にフォーカスを戻しましょうと書かれてあったのですが、それがあまりにも冗長のように思いました。本書には下記のように書いてありました。

ダイアログを開く時の処理

ダイアログを閉じる時の処理

つまり、開いた時にフォーカスがある要素のIDを取得し、なければその要素に’origin’というIDをつける(IDをつける処理は省略)。そして、閉じる時にそのIDから要素を選択して、その要素をフォーカスするという記述です。

なんだか冗長です。自分は下記のようにしたほうがいいのではないかとブクログに書いた次第です。

ダイアログを開く時の処理

ダイアログを閉じる時の処理

編集の方によると、確かにこちらのほうがいいだろうとのことでした。自分の意見が認められたみたいでうれしいです(意見したつもりはないのですが)。

さて、ここまでは余談です。今回、実際に作ってみることにしました。まず、HTML内にダイアログ用の記述として下記のような記述を追加します。

.dialog__layerはレイヤー用ですが、#TestDialogの背景をrgbaで指定しても同じことになるので、必要ないかもしれません。

CSSは下記のようにしました。クラスの命名は最近話題のBEMっぽくしてみました(参考:BEMを参考にしたCSS設計 – Qiita)。

次にJavaScriptの記述です。

何をしているかというと、ダイアログを開いた時にdocument.activeElementで現在フォーカスしている要素を取得しています(参考:JavaScriptでフォーカスのあたっている要素を取得する「document.activeElement」: 小粋空間)。そして、その要素をjQueryのdataメソッドでダイアログ用のタグに”back-focus”というキーで紐付け、ダイアログ内のボタンをおした時にその要素を取得してフォーカスを元に戻すという処理を行っています。

試しに、テキストボックス内に郵便番号を入力してエンターキーを押すと、書式が正しくなければ”正しい書式で入力してください”、正しければ”OKです”と表示するサンプルを作成してみました。
実行サンプル:ダイアログのテスト

エンターキーを押したらダイアログがでて、そのままエンターキーを押したらフォーカスが元に戻るのが分かると思います。注意点として、エンターキーをおした時にダイアログを表示する場合は、下記のようにe.preventDefault()がないとすぐに閉じてしまうようです。

特にこだわりがなければ、alertメソッドを使えばいいだけの話なんですけどね。ダイアログのレイアウトも変更したいという場合に。

ところで、document.activeElementという指定はつい最近知ったのですが、調べてみるとIE4の時からあった指定方法だそうです(document.activeElement – Web API インターフェイス | MDN)。昔からある手法ななのに、初めて知ることがあると、自分はまだまだ全然JavaScriptを分かってないんだなと思います。

jQueryのisメソッドのセレクタ指定でhover擬似クラスを使う


jQueryにはhover擬似クラスは対応してないはずなので、isメソッドでも使えないだろうと思って使ってみたら、使えました。

あれ? でも前にやった時には使えなかったような・・・、と思ってjQueryのバージョンをいろいろ変えてみて試してみると、1.4.3からis(‘:hover’)という指定ができるようになったようです。1.4.2以前だと、『Syntax error, unrecognized expression: hover』というエラーが表示されました。結構前からできたんですね。ただ、調べても1.4.3からできるようになったと書かれてるサイトがないのですが。

例えば、下記のように、setIntervalを用いて3秒ごとにあるブロック要素が左右に動くという指定をし、さらに当該要素にマウスが乗っている場合は動かないようにする場合は、『if(!($moveDiv.is(‘:hover’)))』で動かなくなります。

さて、ところで最初に『jQueryにはhover擬似クラスは対応してないはず』と書き、その後にisメソッド内だと対応していると書きましたが、よくよく考えたら試してないので下記のようなコードで試してみました。

mouseenterはその要素にマウスが入ったら、つまりhover状態になったら実行するイベントです。なので、mouseenterのイベントが発動した時には確実にカーソルが要素上に乗っている状態なので、hover擬似クラスが対応していたら背景が赤くなるはずです。

普通に赤くなりました。実行サンプル:jQueryのhover擬似クラス

うーん。なんで自分はできないと思いこんでたんだ・・・(まあ、こんな使い方することはめったにないとは思いますが)。

jQueryのstopメソッドについて調べてみた


jQueryでアニメーションを行う可能性のある要素にたいして、アニメーションしていない時のみアニメーションさせるという指定をしたいがために、『$ele.not(‘:animated’).animate()』なんてやり方をしていたのですが、どうにも動きがおかしく感じ、もう少し調べてみるとアニメーションを停止するstopというメソッドがあることを知りました(参考:.stop() | jQuery 1.9 日本語リファレンス | js STUDIO)。

ただ、指定の要素はコールバックを指定している要素だったこともあり、アニメーションをストップするだけでは駄目だったのですが、もう少し調べてみると、第二引数にtrueを指定することにより、アニメーションの最後まで移動してコールバックまで実行してくれるということが分かりました(参考:jQuery.stopのjumpToEnd引数が便利すぎてやばい(JavaScript Advent Calendar 2010 5日目) | tech.kayac.com – KAYAC engineers’ blog)。

じゃあ、第一引数は? というと、連続でアニメーションの指定をしている時、第一引数がfalseであれば、次のアニメーションを実行し、trueであれば次のアニメーションも実行しないという指定だそうです。

というわけで、分かりやすいようにテストページを作ってみました:jQueryのstopメソッドのテスト

JavaScriptの箇所のコードは下記のとおりです。

スタートボタンを押すと右側の赤い四角が右端までゆっくり移動し、右端までたどり着くと青色になって今度は逆に左端までゆっくり移動するようなアニメーションです。

スタートボタンを押すと、ストップボタンが出現します。そのストップボタンを押すと、stopメソッドを実行します。引数は囲いの上の見出しに書いてあります(clearQuereが第一引数で、jumpToEndが第二引数)。

試しにスタートボタンを押して赤い四角が右端にたどり着くまでにストップボタンを押してみてください。
上から1番目は途中でストップボタンを押すと、ストップボタンを押した位置から赤色のまま左端にゆっくり移動するようになります。
上から2番目は途中でストップボタンを押すと、瞬時に右端まで移動して青色になり、左端にゆっくり移動するようになります。
上から3番目は途中でストップボタンを押すと、そのストップボタンを押した時の位置で停止します。
上から4番目は途中でストップボタンを押すと、瞬時に右端まで移動して青色になり、その場所で停止します。

これはすごい便利ですね。

jQueryをブラケット記法で書いている


二回続けてのブラケット記法エントリー。今回は普通、ドット記法で書くjQueryをブラケット記法で書いてみることに。

例えば、以下の様なHTMLがあるとします。

この時、idが”bracket-html”の中の要素のうち2つ目のoptionタグの要素内のHTMLを取得してconsole.logで出力する場合、ドット記法だと下記のように書くことがデキます。

いやいや、そこは『$(‘#bracket-test option:eq(1)’).html()』でいいだろ! と突っ込まれそうですが、とにかく上記のコードをブラケット記法で記述すると下記のようになります。

長ったらしいですし、括弧やシングルクォーテーションばっかりでわかりにくいです。普通はこんな書き方しないでしょうし、オススメもしません。ただ、ブラケット記法だとメソッド名に文字列の変数が利用できるので例えば、下記のようなことができます。

めったにこんな処理が必要なことがないとは思いますが、ブラケット記法だとこんなこともできるというサンプルです。

さて、ここで、いっそのこと『$』もブラケットに入れたい! と思う人がいるかもしれません。そもそもJavaScriptで作成したグローバル変数やグローバル関数はwindowオブジェクトに属しています。JavaScriptでは大抵windowを省略しますが、jQueryもwindowオブジェクトのメンバになっています。すなわち、『$===window.$』の結果はtrueです。というわけで、先ほどのブラケット記法での記述は下記のように書くこともデキます。

何々? どうせなら、『window』もブラケットに入れたい? そういう趣味をお持ちの人もいるかもしれません。windowオブジェクトにはtopというプロパティを持っており、これはウィンドウ階層における最上位のウィンドウへの参照を返します(参考:window.top – Web API インターフェイス | MDN)。よく分からない説明かもしれませんが、開いているページの参照を返すということです。すなわち、先ほどの記述は下記のように書くこともできます。

『top』もブラケットに入れたい場合は下記のような感じ。

『window』もブラケットn(ry。以下、無限ループ。