Code Tips‎ > ‎

JavaScript - disabledへの値の設定の仕方いろいろ


あるサイトでおもしろコードを見つけました(恥ずかしい間違いなので、サイト名は伏せておきます)。「スタート」「ストップ」ボタンを押すと、2つのボタンが交互に有効・無効に切り替わるサンプルです。
<body>
	<input type="button" value="スタート" id="start">
	<input type="button" value="ストップ" id="stop" disabled>
</body>

<script language="javascript">
document.getElementById("start").onclick = function() {
	// 無効化
	document.getElementById("start").disabled = "true";
	// 有効化
	document.getElementById("stop").disabled = "";
};

document.getElementById("stop").onclick = function() {
	document.getElementById("start").disabled = "";
	document.getElementById("stop").disabled = "true";
};
</script>


※これ以降のコードは、以下の2行があると仮定してください。
var elm1 = document.getElementById("…");
var elm2 = document.getElementById("…");

無効化・有効化の部分のみ抜き出すとこんな感じです。
// 無効化
elm1.disabled = "true";
// 有効化
elm2.disabled = "";
どの部分が面白いか分かりますでしょうか?


disabledプロパティはboolean型です。そのため通常設定する値は、boolean型の「true」か「false」です。つまり素直に書くなら以下になります。
// 無効化
elm1.disabled = true;
// 有効化
elm2.disabled = false;

「"true"」などと書いてしまうと文字列型になってしまうため本来はNGです。しかしJavaScriptで文字列型をboolean型に代入しようとすると暗黙的に型変換が起こります。その際、1文字以上の文字列は内容に関わらずtureに、0文字の文字列(空文字)とnullはfalseに変換されます。このコードは文字列型からboolean型に変換する際の特性をうまく利用して、(偶然にも)正しく動いています。

おそらくは知識不足によって生まれたコードなのだと思いますが、いろいろ可能性のある興味深いコードだと思いました。私はこのコードに触発され、いろいろなコードを書いてみました。

一見どちらも空文字に見える。
// 無効化(1文字の文字列なのでtrue)
elm1.disabled = " ";
// 有効化(空文字なのでfalse)
elm2.disabled = "";

一見どちらもfalse。
// 無効化(1文字以上の文字列なのでtrue)
elm1.disabled = "false";
// 有効化
elm2.disabled = false;


その他の型からboolean型への変換の特性を利用した分かりにくいコードもいろいろ書いてみました。難読化に使えそうです。

NaNの特性を利用。
// 無効化(NaNはbooleanにするとfalse。その否定なのでtrue)
elm1.disabled = !NaN;
// 有効化(NaNは何と比較してもfalse)
elm2.disabled = (NaN == NaN);

記号で表現。
// 無効化(オブジェクトなのでtrue)
elm1.disabled = {};
// 有効化(空配列なのでfalse)
elm2.disabled = [];
// 無効化(falseの値を持つBooleanオブジェクトなのでtrue)
elm1.disabled = Boolean([]);
// 有効化(空配列なのでfalse)
elm2.disabled = [];
// 無効化(まず、右辺が配列オブジェクトでbooleanに変換するとtrue、その否定でfalse。
//  左辺が配列、右辺がbooleanの比較なので、両辺を数値に変換し比較。両辺とも0なのでtrue。)
elm1.disabled = ([] == ![]);
// 有効化(両辺を配列として比較している。2つは異なる配列であるためfalse)
elm2.disabled = ([] == []); 
// 無効化(オブジェクトをbooleanに変換するとtrue。両辺ともその否定でfalseなので、式はtrue)
elm1.disabled = (!{} == !{});
// 有効化(両辺をオブジェクトとして比較している。2つは異なるオブジェクトであるためfalse)
elm2.disabled = ({} == {});
配列やオブジェクトは応用すると遊べますね。※配列・オブジェクトの比較結果については、こちら「JavaScript等価演算子(==)」を参考にしてください。

軽いだまし。
// 無効化(長さのある配列であるためtrue)
elm1.disabled = [false];
// 有効化(voidはどんな引数を与えてもundefinedを返す。それをboolean型にパースするとfalse)
elm2.disabled = void(true);

数値型からboolean型の変換では、「0→false」「0以外→true」になります。この特性を利用。
// 無効化(右辺がtrue)
elm1.disabled = 1 + 1;
// 有効化(右辺がfalse)
elm2.disabled = 1 - 1;

boolean型から数値型の変換では、「true→1」「false→0」になります。この特性も利用。
// 無効化
elm1.disabled -= 1;
// 有効化
elm2.disabled -= 1;

さらにシンプルに。
// 無効化
elm1.disabled--;
// 有効化
elm2.disabled--;
これは自分でもなかなか秀逸ではないかと思います。JavaScriptの型変換の特性をうまく利用し、ごくシンプルに最少の文字数で表現しています。変に難読化しようとしているわけではなく、JavaScriptの基礎的な部分を把握していれば理解できるコードになっています。



このページの内容は本質的にはdisabledの問題ではなく、単に「boolean型へ変換した際に、trueになる値とfalseになる値」のバリエーションともいえます(最後の2例を除く)。しかしdisabledとからめた事により面白みが増しているように感じます。いかがでしょうか?

2012/10/27