krzm.jp
黒住浩司 Webサイト

ナビゲーションメニューの動的効果

内容

先週、とある授業を行った際に、約1年ぶりにWordPressの構造やテンプレート等の作り方を調べ直し、少しだけWordPressサイトへの関心が復活しました。そこで、当サイトトップページのデザインも、実験ついでに、若干、変更してみました。主として変更を加えたものは、サイトのナビゲーションメニューです。

従来は、ヘッダー画像の下に、他のページと同じように黒い棒状のデザインで、横並びにメニューを配置していましたが、これをヘッダー画像の上に分散して配置してみました。

トップページのナビゲーションメニュー

このメニューは、WordPressのカスタムメニュー機能を使って生成していますが、出力されるHTMLは、nav要素、ul要素、li要素、a要素の親子構造になっています。nav要素を除けば、一般的なul要素によるリンクリストです。

このうちli要素を、絶対配置でヘッダー画像を含むdiv要素内で位置決めしています。ランダムな配置のように見せかけていますが、事前にCorelDRAWで画像の上に文字を並べてみてちょうどよい配置を決め、XY座標を調べてそれをleftやtopプロパティとして指定しました。当サイトは、CSS3のメディアクエリーで3つのサイズを持たせているので、各サイズに応じた座標をすべて算出しています。

この変更の前に、やはり授業でCSS3を扱った際に、擬似3D回転(transform:rotateYとperspective)のアニメーションが面白かったので、当トップページにも取り入れました。そこで、ページが1回転した後に、ナビゲーションボタンもランダムに入れ替わるような、そんな動的な変化を付けてみようと思いました。最も動的な変化としては、ボタン配置位置が変わればよいのでしょうが、さすがにそれは大変そうだったので、位置はそのままでボタンの内容を入れ替えることにしました。

幸い、WordPressが書き出すHTMLでは、各ボタンを特定できるid属性値がli要素に割り振られ、肝心のリンクであるa要素はその中に収納されます。ですから、li要素の要素内容を取り出し、それらをli要素にランダムに入れ直せば、ボタンが切り替わることになります。このため、li要素には位置だけを指定し、ボタンのサイズはa要素の文字数と、上下左右のpaddingで決まるようにしました。

要素内容の入れ替えは、DOMの操作ですから、JavaScriptで行います。7つあるボタンコンテナ(li要素)は、Selector APIのquerySelectorAllで配列風(ノードリスト)に特定します。あとは、これらの並び順どおりにその要素内容をinnerHTMLで取り出し、ボタンコンテナ側の順序を変えたうえで、要素内容は元の順番通りにinnerHTMLで再挿入すれば、ボタンの配置が入れ替わります。

とはいえ、全くランダムに入れ替えたものを、カウンタで処理する方法は見当が付かなかったので、たとえば0123456を、開始位置を変えて4560123とし、そこに0123456の内容を入れることにしました。結果として、0には0、1には1だったものが、0には3、1には4の内容が入ることになり、ボタンの内容が入れ替わります。

4560123
0123456

ボタンコンテナ側の開始数をランダムに決めるのは、Math.randomを使えば簡単です。問題は4560123のような、途中で0に戻る序列を作ることでした。以前、その方法を考えたのですが、これは高等数学の問題かと(高校中退の僕は)嫌になり、放置していたのですが、(いつものように道を歩いているときやシャワーを浴びている時ではなく)ソースコードを眺めていたら、ふと閃きました。カウンタの最大数(この場合7)を超えた数字から、その最大数を引けば良いだけだと…。4から始めた場合は、4, 5, 6, 7, 8, 9, 10のうち、7以上は7-7, 8-7, 9-7, 10-7となるので、0, 1, 2, 3を得られます。四則計算の範囲内であれば、僕にもできます。

と、最大の問題は解決したので、あとは全ボタンがフェードインする効果と、最後に上下にちょっと揺れる効果で、味付けしました。これらのアニメーション効果は、JavaScriptではなく、CSS3のopacity、transformとanimationプロパティで指定しています。また、ロールオーバー時の拡大/縮小も、transformとtransitionプロパティによるものです。もちろん、ボタンの外観も、border-radius、box-shadow、text-shadow、そしてrgbaカラーといったCSS3によるもので、このボタンに関する限り、画像は一切使用していません。

このように、WordPressが出力するHTML構造を踏まえたうえでの、JavaScriptとCSSの共演により、ナビゲーションボタンはその晴れの舞台に立っているわけです。

最後にJavaScriptのソースコードを掲載しておきますが、実行の契機(DOMContentLoaded)はイベントリスナーで得ています。このため、addEventListenerへの対応をチェックしていますが、当然、開始させるものを、それで開始させる前にチェックすることはできません。addEventListenerによって処理を始めるのに、処理が始まってからaddEventListenerに対応しているのかを調べても、対応しているから始まったのであって、対応していないものは始まる以前にエラーになってしまい、チェックどころか処理そのものが始まるわけがありません。ですので、チェックを含めた処理を無名関数に入れることで、イベント以前にチェックが行われるようにしています。

ちなみに、このソースコードの投稿には、やはり最近覚えたWordPressのカスタムフィールド機能を使ってみました。

サンプル ソースコード

サンプルコード1

(function () {
	var d = document;
	if (!d.addEventListener) {
		return;
	} else {
		d.addEventListener("DOMContentLoaded", function () {
			var ID1 = new Array;
			var ID2 = new Array;
			var CONTENT = new Array;
			var objs = d.querySelectorAll("#top_topnav li");
			var MAX = objs.length;
			for (var i=0; i<MAX; i++) {
				ID1[i] =objs.item(i).getAttribute("id");
				CONTENT[i] = d.getElementById(ID1[i]).innerHTML;
			}
			var n = Math.random();
			if (n < 0.143) {
				n = 0;
			} else if (n < 0.286) {
				n = 1;
			} else if (n < 0.429) {
				n = 2;
			} else if (n < 0.572) {
				n = 3;
			} else if (n < 0.715) {
				n = 4;
			} else if (n < 0.855) {
				n = 5;
			} else {
				n = 6;
			}
			var x = -1;
			for (i=n; i<(n+MAX); i++) {
				x++;
				var rdmCount;
				if (i >= MAX) {
					rdmCount = parseInt(i - MAX);
				} else {
					rdmCount = i;
				}
				ID2 = ID1[rdmCount];
				d.getElementById(ID2).innerHTML =  CONTENT[x];
			}
		}, false);
	}
}) ();