Help us understand the problem. What is going on with this article?

フォーム内でonclick属性を使う時には、name / idに気をつけよう

More than 3 years have passed since last update.

はじめに

form周りのコードを書いていたところ、思わぬ動作に遭遇しました。基本的な知識かもしれませんが、知らなかったので備忘録がてらメモしたいと思います。

結論

さっそく結論を書いてしまうと、<form>タグ内においてフォーム部品(inputなど)のnameやidと、onclick属性で呼び出すユーザ定義関数名が重複していると関数が実行されないというものです。

名前が重複していなかったり、element.onclick = function() {}.on()でイベントを設定していれば避けられるので、遭遇する機会は少ないかもしれません。ですがせっかくなので、これに至るまでの道のりを簡単に残したいと思います。。。

動作はchrome 46.0.2490.80で確認しました

鳴らない、電話

(実際のものから相当省略していますが)以下のようなコードを書いていました。
とりあえず動作を確認しようと思い、onclick属性で関数を実行させようとしました。

index.html
<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()してみました。

index.html
<!-- 略 -->
<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()はグローバルにしていたので、直接グローバルにアクセスしてみました。

index.html
<!-- 略 -->
<input type="submit" name="callPhone" onclick="window.callPhone();" value="着信あり">
コンソール.
ジリリリ...

電話が鳴りました!

つまりcallPhone();だけだと、グローバルより前で発見されたと考えられます。onclick属性はこんな動作だったかな・・・?

要素を変更してみる

inputタグから別の要素に変えてみます。

index.html
<!-- 略 -->
<p onclick="callPhone();">着信あり</p>
コンソール.
ジリリリ...

電話が鳴りました!

・・・

あ、name属性がいけないのかな?

index.html
<!-- 略 -->
<p name="callPhone" onclick="callPhone();">着信あり</p>
コンソール.
ジリリリ...

鳴った!

うーん、どうもinputの時だけ鳴らないようだ。

inputでname属性を変えてみる

inputタグのname属性を少し変更してみます。

index.html
<!-- 略 -->
<input type="submit" name="callPhone_" onclick="callPhone();" value="着信あり">
コンソール.
ジリリリ...

鳴った!

だんだん分かってきました。念のためname="callPhone"に戻してみると鳴らなくなりました。

もちろんname自体を削除しても関数が実行されるようになります

さらに色々試してみる

気になって色々と試してみると、以下のことが分かりました。

  • inputの他にもselecttextareabuttonなど、いわゆるフォーム部品のタグで同じ挙動になった
  • 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以外は未確認です)。

index.html
<!-- true -->
<input type="submit" name="callPhone" onclick="console.log(callPhone === this.form.callPhone);" value="着信あり">
Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away