Web標準化時代のJavaScript

aタグのイベントの書き方

古くからaタグでは多くのイベントが使えました。よく使われるイベントとしてはonclick、onmouseover、onmouseoutがあります。これらのイベントはaタグの中に記述されるのが現在でも一般的です。これは古いブラウザ/多くのブラウザで動作すること、そしてWeb作成ソフトでも、そのようにスクリプトを出力してしまうためです。ここでは手書きの場合での書き換え方法で説明します。
以下のサンプルは+をクリックするとdivタグ部分の表示、非表示が切り替わります。これはツリーメニューなどで使われる手法です。【サンプル1を実行
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>Sample</title>
<style type="text/css"><!--
#DIV001 { display:none; }
--></style>
<script type="text/javascript"><!--
function displayBlock(divID) {
var divObj = document.getElementById(divID);
if (divObj.style.display == "block") {
divObj.style.display = "";
}else{
divObj.style.display = "block";
}
}

// --></script>
</head>
<body>
<p>
<a href="#DIV001" onClick="displayBlock('DIV001')">+</a>←クリックしてください
<div id="DIV001">ここが表示されます</div>
</p>
</body>
</html>
まず、HTMLファイルとJavaScriptファイルを分離します。HTMLファイル内ではaタグにonClickなどのイベントは一切記述せず、scriptタグによるjsファイルの読み込みのみです。スクリプトではaタグを探し出す必要があります。divタグではid属性を使ってID名を指定している場合がほとんどですが、aタグの場合にはID名が割り振られるのはアンカー指定の時くらいです。このためdivタグなどを参照する時に利用するgetElementById()ではなく、getElementsByTagName()を使ってaタグの情報を読み出します。
getElementsByTagName("a")とするとページ内にある全てのaタグの情報を読み出し配列として返します。つまりgetElementsByTagName("a")[0]とした場合、ページ内の一番最初にあるaタグの情報を参照/設定できることになります。読み出したaタグにイベントを設定するには「getElementsByTagName("a")[0].onlick = 処理」のようにします。onclick部分が設定するイベント名になります。処理の部分は通常は関数名を指定します。これらのイベント設定を行うにはページが読み込まれた後に行う必要があるためwindow.onload = function(){ 〜 }を使う事になります。以下のサンプルではaタグのhref属性を読み出し#DIV001の場合のみイベントハンドラを設定するものです。【サンプル2を実行
■HTMLファイル
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>Sample</title>
<style type="text/css"><!--
#DIV001 { display:none; }
--></style>
<script type="text/javascript" src="assign.js"></script>
</head>
<body>
<p>
<a href="#DIV001">+</a>←クリックしてください
<div id="DIV001">ここが表示されます</div>
</p>
</body>
</html>
■スクリプトファイル (assign.js)
window.onload = function() {
var aTag = document.getElementsByTagName("a");
for (var i=0; i<aTag.length; i++) {
if (aTag[i].href.lastIndexOf("#DIV001") > -1) {
aTag[i].onclick = displayBlock;
}
}
}
function displayBlock() {
var linkStr = this.href;
var n = linkStr.lastIndexOf("#DIV001");
var divID = linkStr.substring(n+1, linkStr.length); // #を削除
var divObj = document.getElementById(divID);
if (divObj.style.display == "block") {
divObj.style.display = "";
}else{
divObj.style.display = "block";
}
}
ところで、このスクリプトこれで十分でしょうか? もちろん、複数のdivタグに対して処理を行う場合でも、少し改良すればよいだけで問題があるようには見えません。問題が発生するのはスクリプトの規模が大きくなった場合、大量の関数を定義し利用する場合、数年後に再利用する場合です。どこが問題なのかと言えば関数の定義時に関数名が指定されていることです。関数を使う場合には、どうしても関数名が必要になると思っている人もいるかもしれません。しかし、JavaScriptでは以下のサンプルのように関数名を指定せずに、呼び出す事ができます。これはwindow.onload = function() { 〜 }と同じで無名関数/匿名関数を使ってイベント発生時の処理を定義すれば良いのです。*1
実際に書き直したものが以下のスクリプトです。関数名が指定されていないため、他のライブラリを読み込んで使用しても関数名の衝突もなく安心して利用することができます。(この場合ネックになるのはwindow.onloadになってしまいますが、例ということでご容赦くださいませ)【サンプル3を実行
■HTMLファイル
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>Sample</title>
<style type="text/css"><!--
#DIV001 { display:none; }
--></style>
<script type="text/javascript" src="assign.js"></script>
</head>
<body>
<p>
<a href="#DIV001">+</a>←クリックしてください
<div id="DIV001">ここが表示されます</div>
</p>
</body>
</html>
■スクリプトファイル (assign.js)
window.onload = function() {
var aTag = document.getElementsByTagName("a");
for (var i=0; i<aTag.length; i++) {
if (aTag[i].href.lastIndexOf("#DIV001") > -1) {
aTag[i].onclick = function() {
var linkStr = this.href;
var n = linkStr.lastIndexOf("#DIV001");
var divID = linkStr.substring(n+1, linkStr.length); // #を削除
var divObj = document.getElementById(divID);
if (divObj.style.display == "block") {
divObj.style.display = "";
}else{
divObj.style.display = "block";
}
}
}

}
}
*1 もっとも、このような書き方ができないブラウザもあります。特に古いブラウザなどでは、function()内にfunction()を定義することができませんでした。スクリプトを見て、どうして無名関数/匿名関数を使わないのか、スマートではないと感じる人(プログラマ)もいるでしょう。しかし、使えなかった時代があったのですから、こればかりは仕方ないのです。また、メリットばかりではなくデメリットもあります。特に読みやすさ、という点では、普通に関数名を指定した方が良いかもしれません。