はじめに
form周りのコードを書いていたところ、思わぬ動作に遭遇しました。基本的な知識かもしれませんが、知らなかったので備忘録がてらメモしたいと思います。
結論
さっそく結論を書いてしまうと、<form>
タグ内においてフォーム部品(inputなど)のnameやidと、onclick属性で呼び出すユーザ定義関数名が重複していると関数が実行されないというものです。
名前が重複していなかったり、element.onclick = function() {}
や.on()
でイベントを設定していれば避けられるので、遭遇する機会は少ないかもしれません。ですがせっかくなので、これに至るまでの道のりを簡単に残したいと思います。。。
動作はchrome 46.0.2490.80で確認しました
鳴らない、電話
(実際のものから相当省略していますが)以下のようなコードを書いていました。
とりあえず動作を確認しようと思い、onclick
属性で関数を実行させようとしました。
<script>
function callPhone() {
console.log('ジリリリ...');
}
</script>
<form action="" onsubmit="return false;">
<input type="submit" name="callPhone" onclick="callPhone();" value="着信あり">
</form>
一向に電話は鳴りません。
はて?と思いDevtoolsでコンソールを見ると以下のように表示されています。
Uncaught TypeError: callPhone is not a function
関数じゃない・・・?
よくいみがわかりませんでした。
色々試してみる
何が返ってきているのか
関数じゃないなら何なんだ!
ということで、まずはconsole.log()
してみました。
<!-- 略 -->
<input type="submit" name="callPhone" onclick="console.log(callPhone);" value="着信あり">
<input type="submit" name="callPhone" onclick="console.log(callPhone);" value="着信あり">
自分自身が返って来ていました。
念のためinputのすぐ下に<script>console.log(callPhone);</script>
を仕込みましたが、こちらは意図した通りfunction()
が返って来ました。
グローバルにアクセスしてみる
次にcallPhone()
はグローバルにしていたので、直接グローバルにアクセスしてみました。
<!-- 略 -->
<input type="submit" name="callPhone" onclick="window.callPhone();" value="着信あり">
ジリリリ...
電話が鳴りました!
つまりcallPhone();
だけだと、グローバルより前で発見されたと考えられます。onclick属性
はこんな動作だったかな・・・?
要素を変更してみる
input
タグから別の要素に変えてみます。
<!-- 略 -->
<p onclick="callPhone();">着信あり</p>
ジリリリ...
電話が鳴りました!
・・・
あ、name属性がいけないのかな?
<!-- 略 -->
<p name="callPhone" onclick="callPhone();">着信あり</p>
ジリリリ...
鳴った!
うーん、どうもinputの時だけ鳴らないようだ。
inputでname属性を変えてみる
input
タグのname属性を少し変更してみます。
<!-- 略 -->
<input type="submit" name="callPhone_" onclick="callPhone();" value="着信あり">
ジリリリ...
鳴った!
だんだん分かってきました。念のため**name="callPhone"**に戻してみると鳴らなくなりました。
もちろんname自体を削除しても関数が実行されるようになります
さらに色々試してみる
気になって色々と試してみると、以下のことが分かりました。
-
input
の他にもselect
、textarea
、button
など、いわゆるフォーム部品のタグで同じ挙動になった - typeは関係無かった(textでもradioでも同じ)
- フォーム部品であっても、
<form>
外に配置すると一転、関数が実行されるようになった - name属性だけでなく、idでも同じ挙動になった
以上のことから、<form>
内の部品要素のnameやidと、onclick属性で呼び出す関数名が重複していると意図した関数が実行されない、との結論に至りました。
終わりに
しかし何でなんだろうなぁと思っていたところ、stackoverflowにズバリその答えが書いてありましたw
Why JS function name conflicts with element ID?
http://stackoverflow.com/questions/9158238/why-js-function-name-conflicts-with-element-id
簡単に言うと、チェインによりthis.formが自動的かつ優先的に、見つかっていたということのようです。
またstackoverflowでは、onchange属性
について書かれているので、onclick
だけでなく色んなonXXXX属性で発生しそうです(onclick以外は未確認です)。
<!-- true -->
<input type="submit" name="callPhone" onclick="console.log(callPhone === this.form.callPhone);" value="着信あり">