JavaScript』の最近のブログ記事

jQueryプラグイン化した新バージョンを作成しました。

CookieをIEからFirefoxへインポートする際、ドメイン名の解釈の違いでcookieが複数生成されるバグを修正しました。(2010.03.06)

IE6,IE7でJavaScriptが動作しないバグを修正しました。(2009.09.10)

クッキー(cookie)とJavaScriptを使って、いわゆるYahoo!トピックスのタブ表示みたいなのを作ろう!という企画。

タブ表示サンプル

たまに使いたくなるクッキー。でも処理がめんどくさそうなので今まで敬遠してきた。
そんな折、テレビから「START!一歩を踏み出そう」の声が・・・。なんか作らないといけないような強迫観念にさいなまれて、サンプルを作成してみた。

≫サンプルはこちら

以下、HTMLのマークアップ。(簡略化してます)

<ul id="tabhead">
	<li id="head-tab0" class="on"><a id="anchor-tab0" href="#">foo_a</a></li>
	<li id="head-tab1"><a id="anchor-tab1" href="#">foo_b</a></li>
	<li id="head-tab2"><a id="anchor-tab2" href="#">foo_c</a></li>
</ul>
<div id="tabbody">
	<div id="body-tab0" class="current">
		<h2>foo_a</h2>
		<ul>
			<li>hoge</li>
		</ul>
	</div>
	<div id="body-tab1">
		<h2>foo_b</h2>
		<ul>
			<li>hoge</li>
		</ul>
	</div>
	<div id="body-tab2">
		<h2>foo_c</h2>
		<ul>
			<li>hoge</li>
		</ul>
	</div>
</div>

肝心のJavaScript部分は以下↓

//変数設定
var CookieTab = {
	cookieName: 'select', //cookieに書き込むプロパティ名
	cookieExpires: 10, //cookieの有効期限(単位:日)
	tabHeadId: 'tabhead', //タブheadのID名
	tabBodyId: 'tabbody', //タブbodyのID名
	tabHdPrefix: 'head-', //タブheadリストのID接頭語
	tabBdPrefix: 'body-', //タブbodyリストのID接頭語
	tabAnPrefix: 'anchor-' //タブheadアンカーのID接頭語
};

//DOM読込完了後、tab制御開始
dom.event.addEventListener(window,'load',function(){
	CookieTab.setting();
});

//tabの初期制御
CookieTab.setting = function() {
	this.tabValue = this.getCookie(this.cookieName);
	this.domAnalysis();
	if(!this.tabValue || this.tabValue==undefined){
		this.tabValue = 'tab0';
	}
	this.tabControl();
}

//cookie書込用関数
CookieTab.setCookie = function(ckName, ckValue, ckExpires, ckDomain, ckPath, ckSecure) {
	var date = new Date();
	date.setTime(date.getTime() + ckExpires*24*60*60*1000);
	var ckStr = escape(ckName) + '=' + escape(ckValue);//日本語使用時の文字化け対策
	ckStr += '; expires=' + date.toGMTString();//有効期限をGMT値に変換
	if(ckDomain) {
		ckStr += '; domain=' + ckDomain;
	}
	if(ckPath) {
		ckStr += '; path=' + ckPath;
	}
	if(ckSecure) {
		ckStr += '; secure';
	}
	document.cookie = ckStr;
}

//cookie取得用関数
CookieTab.getCookie = function(ckName) {
	var ckMatch = ('; ' + document.cookie + ';').match('; ' + ckName + '=(.*?);');
	var ckValue;
	if(ckMatch){
		ckValue = ckMatch[1];
	} else {
		ckValue = '';
	}
	ckValue = unescape(ckValue);//escapeしたものを元に戻す
	return ckValue;
}

//DOM解析用関数
CookieTab.domAnalysis = function() {
	this.linum = this.ID(this.tabHeadId).getElementsByTagName('li').length;
	this.tabBdDiv = this.ID(this.tabBodyId).getElementsByTagName('div').length;
	if(this.linum != this.tabBdDiv){
		throw new Error('tabHead is not equal to tabBody.');
	}
}

//tabの初期設定
CookieTab.tabControl = function() {
	this.tabDisplay();
	for(var i=0; i<this.linum; i++){
		this.tabAnchor(i);//tabクリック時の動作設定
	}
}

//tabの表示制御
CookieTab.tabDisplay = function() {
	for(var i=0; i<this.linum; i++){
		//互換性維持のためclassの取得・設定にはhoge.className = fooを使用
		this.ID(this.tabHdPrefix + 'tab' + i).className = '';
		this.ID(this.tabBdPrefix + 'tab' + i).className = '';
	}
	this.ID(this.tabHdPrefix + CookieTab.tabValue).className = 'on';
	this.ID(this.tabBdPrefix + CookieTab.tabValue).className = 'current';
}

//tabクリック時の動作制御
CookieTab.tabAnchor = function(tabnum) {
	this.ID(this.tabAnPrefix + 'tab' + tabnum).onclick = function(){
		CookieTab.setCookie(CookieTab.cookieName,'tab' + tabnum,CookieTab.cookieExpires,location.hostname);//Cookieを発行するドメイン名を明示的に指定
		CookieTab.tabValue = CookieTab.getCookie(CookieTab.cookieName);
		CookieTab.tabDisplay();
		return false;
	}
}

//getElementByIdの短縮形を使用
CookieTab.ID = function(id) {
	var myId = document.getElementById(id);
	return myId;
}

解説は時間と紙面の関係上、割愛。スクリプトに少しコメントを入れてるので、そちらを参照ください。

ちょっと前から「オライリーのサイの本」を読んで、ちょこちょこ勉強してるけど、やっぱ手を動かさないとダメ。「名前空間を汚さないようにモジュール化する」ってことの意味が、やっと少し分かった気がする。

今回は「jQuery」を使わず実装したけど、jQueryを使った場合のスクリプトは以下。
jQueryにプラスして、「jquery.cookie.js」も使用するのであしからず。

//DOM読込完了後、tab制御開始
$(document).ready(function(){
	CookieTab.setting();
});

//tabの初期制御
CookieTab.setting = function() {
	this.tabValue = $.cookie(this.cookieName);
	if(!this.tabValue || this.tabValue==undefined){
		this.tabValue = 'tab0';
	}
	this.tabControl();
}

//tabの初期設定
CookieTab.tabControl = function() {
	this.tabDisplay();
	$('#' + this.tabHeadId + ' li a').each(function(value){
		$(this).click(function(){//tabクリック時の動作設定
			value+='';//数値型→文字列型変換
			$.cookie(CookieTab.cookieName, 'tab' + value, { expires: CookieTab.cookieExpires, domain: (location.hostname) });//Cookieを発行するドメイン名を明示的に指定
			CookieTab.tabValue = $.cookie(CookieTab.cookieName);
			CookieTab.tabDisplay();
			return false;
		});
	});
}

//tabの表示制御
CookieTab.tabDisplay = function() {
	$('#' + this.tabHeadId + ' li').each(function(){
		$(this).removeClass('on');
	});
	$('#' + this.tabBodyId + ' div').each(function(){
		$(this).removeClass('current');
	});
	$('#' + this.tabHdPrefix + this.tabValue).addClass('on');
	$('#' + this.tabBdPrefix + this.tabValue).addClass('current');
}

jQueryには、jQueryの難しさがあるし、こっちの方法も結構めんどい。適材適所で使うのがよいかと。

サンプルのダウンロードもできるので、興味がある方はどうぞ。
これを使う機会、いつか来るかな。

≫CookieTab.jsサンプルファイルの一括ダウンロードはこちら


ライセンス:
クリエイティブコモンズライセンス:帰属(Attribution, by)」での公開・配布となります。

最近、ちょこちょこお世話になってるjQuery
便利なのはいいんだけど、読み込み時間が気になる。ってことで、jQueryの機能が必要ないページで読み込むのを控えてたら、JavaScriptエラーでつまづいたので自分用にメモ。

たとえば、ロード完了時にアラートを出す以下のコード。

このケース、jQueryが読み込まれてないと、「$ってナンデスカ?」っていうエラー「$ is not defined」が発生してしまう。

ってことで、jQueryの読み込みを判別するため、以下のif文を追加した。
サンプルページ1 / サンプルページ2

これでめでたくエラーとはおさらばです。

GWだし、なにか作るかってことで、フォームの入力項目を切り替える「switchform」のサンプルをアップ。

たまに実装する機会があるので、使い回せるように作ってみた。
まずは作成したswitchformのサンプルから。  (⇒ダウンロードはこちら

フォームの項目を切り替えるサンプル

使いどころとしては、会員登録フォームで「個人」か「法人」かを選んでもらう時、かな。
「個人」で登録したい人には、「会社名」とか「部署名」の入力欄を表示しない方が、ユーザビリティ的にいいんじゃないかと。

使い方は以下参照。

フォーム部分のHTML

<table id="target-form">
  <thead>
    <tr>
      <th>種別<span>[必須]</span></th>
      <td>
        <ul>
          <li><input type="radio" name="case" id="trigger0" value="0" /><label for="trigger0">ケース0</label></li>
          <li><input type="radio" name="case" id="trigger1" value="1" /><label for="trigger1">ケース1</label></li>
          <li><input type="radio" name="case" id="trigger2" value="2" /><label for="trigger2">ケース2</label></li>
        </ul>
      </td>
    </tr>
  </thead>
  <tbody>
    <tr class="group0">
      <th><label>グループ0</label></th>
      <td><input type="text" /></td>
    </tr>
    <tr class="group1">
      <th><label>グループ1</label></th>
      <td><input type="text" /></td>
    </tr>
    <tr class="group2">
      <th><label>グループ2</label></th>
      <td><input type="text" /></td>
    </tr>
    <tr>
      <th><label>共通の項目</label></th>
      <td><input type="text" /></td>
    </tr>
  </tbody>
</table>

「switchform」の使い方

(1) <table>のIDは、「target-form」とする。(JavaScriptで変更可能)

(2) 種別のラジオボタンには、「trigger」で始まるIDをtrigger0,trigger1のように数字で順に振る。

(3) 入力行の<tr>要素に「group」で始まる対応したclassを振る。
たとえば、ケース0で入力する項目には、「class="group0"」と指定。

(4) 共通で使用する項目の場合、入力行へのclass指定は不要。

(5) ラジオボタンを増やしたい場合は、「trigger**」と「group**」の数字を増やす。


使いまわせるよう工夫したつもりだけど、実はあんまり便利じゃないかも。
まあ、自分用だからいっか。

each関数を使って、JavaScriptを簡略化しました。(2009.06.01)

というわけで、僕の旧ポートフォリオサイト「スカイラブデザイン」をプチリニューアルしたときの話。

「スカイラブデザイン」のサイトキャプチャ

上がそのキャプチャで、コンテンツを変えたワケでもないし、基本デザインも変わってないんだけど、jQueryを使って少し動きを付けてみた。

実際にサイトを触ってもらえると分かると思うけど、ラベルをクリックすると、対応するコンテンツが現れたり消えたりするという、いわゆるトグルアクションをjQueryで実装してみただけ。

安易にjQueryに頼りたくないなあと思って、今まで避けてきたけど、使い方くらい知っとこうと思って今回自分のサイトで試してみた。

jQueryのトグルアクションに苦戦。

jQueryを使い慣れてる人なら、30分もかけず、このくらいの動きを実装できると思うけど、jQuery以前にJavaScriptの基本もあやふやな僕は、丸1日くらい悩んでしまった。

悩んで実装した結果を備忘録としてメモ書き。

HTMLのコード

jQueryの前に、まずはHTMLのコードから。(簡略化してます)

<html>
<body>
<div id="container">
	<div id="header">ヘッダー</div>
	<ul id="gnav">
		<li id="gnav01"><a href="#box01">グローバルナビ1</a></li>
		<li id="gnav02"><a href="#box02">グローバルナビ2</a></li>
		<li id="gnav03"><a href="#box03">グローバルナビ3</a></li>
	</ul>
	<div id="contents">
		<div id="contentsMain">
			<div id="box01">コンテンツ1</div>
			<div id="box02">コンテンツ2</div>
			<div id="box03">コンテンツ3</div>
		</div>
	</div>
	<div id="footer">フッター</div>
</div>
</body>
</html>

ヘッダがあってナビがあってフッターがあって、その間にコンテンツがあるという典型的なHTMLソース。ここまでは問題なかったのだが・・・。

jQueryでやったこと(概要)

jQueryでどんな操作をしたのか、コードではなく、まずはフローとして書いてみる。

1) コンテンツエリア(#contents)の表示をオフに。

CSSで非表示にするとSEO的にあれなので、JavaScript側でオフにする。

2) 全体(#container)を上下中央揃えに。

ウインドウの大きさと、コンテンツエリアを除いた全体の高さを算出し、positionを使って上下中央の位置に揃える。

3) グローバルナビ(#gnav)のクリックに応じて、対応するコンテンツを表示(トグルダウン)。

コンテンツエリアをいったん全部表示し、必要ないコンテンツの表示はオフにする。それと同時に、コンテンツの高さを算出しトグルアクションに備える。

4) トグルダウンにスムーズな動きを付ける。

3)で算出したコンテンツの高さを元に、上下中央揃えを保ったままスムーズにトグルダウンさせる。ただし、ウインドウからコンテンツがあふれる場合は、ウインドウの上辺基準に変更する。

5) グローバルナビを再度クリックした際のトグルアップ処理。

トグルアップは上記の3),4)の逆の処理を行う。で算出したコンテンツの高さを元に、上下中央揃えを保ったままスムーズにトグルダウンさせる。

6) ウインドウサイズがリサイズされた際の処理。

ウインドウを拡大・縮小した場合、上下中央揃えの処理を再度行う。

以上1)~6)の処理をjQueryで行うことにした。この辺から既に試行錯誤が始まっていた・・・。

jQueryの実装

下記のコードそのまんま。


$(function(){//$(document).ready()の短縮形

	/****** 初期設定 ******/
	var content = $('#contents');
	var contain = $('#container');
	var contmain = $('#contentsMain');
	var header = $('#header');
	var gnav = $('#gnav');
	var footer = $('#footer');
	content.css('display','none');
	var opPos;
	var clPos = clPositon();
	contain.css('top',clPos);

	/****** トリガー設定用 ******/
	var gnavs = gnav.find('li[id^=gnav]');//id=gnav**が付与されたli要素を抽出
	gnavs.each(function(){//gnavsのすべての要素に対して繰り返し処理実施
		var selector = $(this).find("a").attr('href');//gnavsの各a要素のhref属性#hogeを取得
		var box = $(selector);
		$(this).toggle(
			function(){//open
				contmain.empty();
				contmain.append(box);
				opPos = opPositon();
				toggleDownAction(content,contain,opPos);

			},
			function(){//close
				clPos = clPositon();
				toggleUpAction(content,contain,clPos);
			}
		)
	});

	/****** トグルアクション関数 ******/

	//トグルダウン(open)
	function toggleDownAction(cc,ct,op){
		cc.show();
		ct.animate(
			{top:op}
		)
	}
	//トグルアップ(close)
	function toggleUpAction(cc,ct,cp){
		cc.hide();
		ct.animate(
			{top:cp}
		)
	}

	/****** 位置算出用関数 ******/

	//オープン状態に取る位置を算出
	function opPositon(){
		getSize();
		cMargin = Math.round((docHeight - 60 - hdHeight - gnHeight - ftHeight - ctHeight)/2);
		if(docHeight < ctHeight){cMargin=0;}
		if(cMargin < 0){cMargin=0;}
		return cMargin;
	}

	//クローズ状態に取る位置を算出
	function clPositon(){
		getSize();
		nMargin = Math.round((docHeight - 100 - hdHeight - gnHeight - ftHeight)/2);
		if(nMargin < 0){nMargin=0;}
		return nMargin;
	}

	//各DOM要素のサイズ取得
	function getSize(){
		docHeight = $(window).height();
		hdHeight = header.height();
		gnHeight = gnav.height();
		ftHeight = footer.height();
		ctHeight = content.height();
		cnHeight = contain.height();
	}

	//ウインドウリサイズ時の処理
	$(window).resize(function(){
		if(content.css('display')=='none'){
			currentPos = clPositon();
		} else {
			currentPos = opPositon();
		}
		contain.css('top',currentPos);
	})
})

ということで、とりあえず実装できたんだけど、もう全然ダメ、俺。

jQueryの書き方がどうのこうのよりも、JavaScriptの変数の扱いが途中でよく分からなくなり、勉強不足を身にしみて感じた今回のプチリニューアル。

これを機会にオライリーのサイの本で、基礎から勉強しなおそうと思った冬の日の週末でした。

JavaScript 第5版JavaScript 第5版
村上 列

オライリー・ジャパン 2007-08-14
売り上げランキング : 11884
おすすめ平均

Amazonで詳しく見る
by G-Tools
スムーズに開閉するドロップダウンメニュー

スムーズに開閉するドロップダウンメニューを作ってみた。名付けて「Waterfall Menu」。

ベースにしたのは、この Sliding JavaScript Dropdown Menu
以前、このJavaScriptを仕事で使おうとしたんだけど、メニューの下に1pxの変な余白ができたりしたので、使うのをやめてしまった。

ということで、リベンジしたよという記事。

Waterfall Menu

jsでスムーズに開閉するドロップダウンメニュー。メニューにマウスオーバすると、サブメニューが展開します。

更新履歴
  • [2011.04.29] ver0.3 マウスオーバ時、title属性がツールチップとして表示されないよう修正
  • [2008.12.15] ver0.2 FireFox2.0でドロップダウンメニューが下のレイヤーに入り込むバグを修正
  • [2008.08.21] ver0.1 リリース
使い方

HTMLのソースは以下。

<dl class="dropdown" title="two">
	<dt id="ddheader-two">Dropdown Two</dt>
	<dd id="ddcontent-two">
		<ul>
			<li><a href="#" class="underline">Navigation Item 1</a></li>
			<li><a href="#" class="underline">Navigation Item 2</a></li>
			<li><a href="#" class="underline">Navigation Item 3</a></li>
			<li><a href="#" class="underline">Navigation Item 4</a></li>
			<li><a href="#">Navigation Item 5</a></li>
		</ul>
	</dd>
</dl>

<dl class="accordion">に変更すると、下のコンテンツも連動して移動。(JavaScriptじゃなくて、CSSを変えてるだけ)。
title属性を振ってるのは、どのサブメニューが伸びるのかを指定するため。

<dl class="dropdown"> ⇒ 重なるように移動
<dl class="accordion"> ⇒ 下のコンテンツも連動して移動
<dl title="hoge"> ⇒ hogeというidが付いた<dd>が移動

実装しては見たもののHTMLのソースが全然綺麗じゃない...。
もうちょっとプレーンなソースが書けるよう努力します。


ライセンス:
クリエイティブコモンズライセンス:帰属(Attribution, by)」での公開・配布となります。


参考記事:
[JS]軽量のスムーズに開閉するドロップダウンメニューのスクリプト | コリス
Sliding JavaScript Dropdown Menu - Web Development Blog
JavaScriptでのクロージャの使い方