この記事は、http://css-tricks.com/ の Chris Coyier の許可を得て、翻訳しています。一部変更して翻訳している部分もある場合があります。オリジナルの記事はここよりご覧いただけます。
以前Dirk Tucholskiさんから「FLOWmarketというWEBサイトのメニュー部分はどうなっているのか。」と言うメールをいただきました。なかなか面白い動きをするサイトだったので、私だったらどうするか考えてみました。このアイデアの面白いところはメニューリンクが垂直方向に長く連なっているところです。しかもそのメニュー全てが表示されているわけではありません。可視エリアをマウスで上下にスクロールすると、メニュー自体が上下に動き、それまで表示されていなかったメニューアイテムが表示されます。更にマウスが当たっているメニューはハイライトされています。
HTML
一般的なメニュー用コード:
<div id="menu"> <ul> <li><a href="#">Nature</a></li> <li><a href="#">Receivability</a></li> <li><a href="#">Alone time</a></li> <!-- etc --> </ul> </div>
CSS初めの一歩
今回高さは固定設定します。なので、ここではオーバーフロー値はoverflow: auto;と設定しましょう。こうすることにより、メニューはスクロールしてくれますしJavaScriptに関係なくアクセスすることが出来ます。まずは基本的なスタイリング設定です。
#menu { height: 360px; overflow: auto; } #menu ul { list-style: none; } #menu a { text-decoration: none; display: block; color: black; }
JavaScriptの初めの一歩
ここでのアイデアはメニューリンクがホバーされるまでは静止状態で、ホバーされたらそれに応じて調整すると言うことです。スタイリングする為に「hover」クラスを追加し、効果を実行させる為に内側のdiv(今回追加します。)を上下に移動させます。ではどの位まで上下に動かしたらよいのでしょう?これにはどのリンクがホバーされているのか正確に把握する必要があります。てっぺんに近いところにあるリンク(もしくは底辺に近いリンク)はそれよりも中心に近い位置にあるリンクより、スクロールする幅が広くなります。この遠い位置にあるリンクには、距離を感じさせない為に「speed×乗数」で速度アップを図ります。位置を把握する為には、ループさせてデータ属性を適用させます。
$("#menu").css("overflow", "hidden").wrapInner("<div id='mover' />"); var $el, speed = 13.5, // needs to be manually tinkered with items = $("#menu a"); items .each(function(i) { $(this).attr("data-pos", i); }) .hover(function() { $el = $(this); $el.addClass("hover"); $("#mover").css("top", -($el.data("pos") * speed - 40)); // 40 is the top padding for the fadeout }, function() { $(this).removeClass("hover"); });
$("div").data("pos");と言うようなHTML5のデータ属性(例:<div data-pos=1>)にアクセスすることも可能です。その方が簡潔でクールですね。
フェードアウト
WebKitブラウザでデモをご覧になった方は、スクロールするとメニューの上端と下端がフェードアウトしていくのをご確認いただけるかと思います。これはただメニューに:beforeと:afterの疑似要素を加え、上端と下端で絶対位置指定し、白から透明へのグラデーションを使用しているだけです。
#menu:before { content: " "; position: absolute; top: 0; left: 0; height: 50px; width: 100%; z-index: 2; background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, rgba(255,255,255,100)),color-stop(1, rgba(255,255,255,0))); } #menu:after { content: " "; position: absolute; bottom: 0; left: 0; height: 50px; width: 100%; z-index: 2; background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, rgba(255,255,255,0)),color-stop(1, rgba(255,255,255,100))); }
必要ならば他のブラウザのプレフィックスを使用してください。
「speed」が重要
メニューごとにシフトアップまたはダウンされる幅は、「speed×乗数」の計算結果によって変わってきます。ちなみに上記のJavaScriptではスピードは任意で13.5で設定されています。なんとかメニューアイテムの個数や高さその他もろもろに基づいて、適切な数字を数学的に割り出そうと試みましたがなかなかうまくいきませんでした。メニューが変わる度に計算し直していてはきりがないので、何かよいアイデアを思い付いたら是非教えてください。
キーボードナビゲーション
今のスピードですと、選択されたメニューへと的確に移動するのは少し難しいので、それをサポートするためにキーボードナビゲーションを加えます。「document」上で「keydown」イベントを監視し、ファンクションを実行することで完成します。コードの書き換えを減らす為に、現在アクティブなメニュー項目はそのまま選択された状態にしておき、上矢印か下矢印が押されたらそれに応じてメニュー機能を処理する用設定された「mouseenter」か「mouseleave」イベントをトリガーにします。
$(document).keydown(function(event) { cur = $(".hover").attr("data-pos"); // Down arrow if (event.keyCode == 40) { $("[data-pos=" + cur + "]").trigger("mouseleave"); if (cur != max) { cur++; } $("[data-pos=" + cur + "]").trigger("mouseenter"); } // Up arrow if (event.keyCode == 38) { $("[data-pos=" + cur + "]").trigger("mouseleave"); if (cur > 0) { cur--; } $("[data-pos=" + cur + "]").trigger("mouseenter"); } });
ここでは「cur」変数の設定がなされていませんが、デモ上ではきちんと設定されています。
使い勝手の面では・・・
このアイデアの核心である「ユーザーの期待よりもメニューが速くスクロールする」というものは、ユーザビリティで考えるとあまり良いものではありません。不慣れで微妙な動きの為に、選択したいリンクを確実に選ぶのはなかなか難しいと私も自覚しています。先述のキーボードナビゲーションはある程度の手助けにはなりますが、これもまた潜在的に馴染みがある考えとは言えないと思います。
今回の手法は長いメニュー全てに対処出来る解決方法ではありません(こちらもご参照ください)。どちらかと言うとむしろ面白さを追求した効果です。メニュー項目がそんなに重要ではないサイトや、ポエムを表示する方法などで使用するにはいいかもしれませんね。サイト上で問題なく動いていれば、多少のアクセシビリティは目を瞑ってもらいましょう。そう考えると、このFLOWmarketサイトは素晴らしいですよね。