Pocket

こんにちは。ishibashiです。
この前仕事でjQueryのclosestについてちょっとはまったことがあったので、その時のことを書きたいと思います。

– closestとは –
開始要素から最も近い親要素を選択します。引数にセレクター書式を指定した場合、マッチする最も近い親要素を返します。
フィルタにマッチすれば、開始要素そのものが返る場合もあります。
ルートドキュメントまで辿ってもマッチする要素が無い場合、戻り値はnoneになります。
(http://semooh.jp/jquery/api/traversing/closest/%5Bexpr%5D/)
—————–
とあります。

今回はボタンの2度押しを防止する目的で下記のようなスクリプトを作成しました。

<!-- 2度押し防止スクリプト -->
<script type="text/javascript"><!-- 
  (function($) {
    $(function() {
      $('button').click(function () {
        if ($(this).attr('type') == 'submit') {

          // 一番近くのformにhiddenタグを追加。
          // この下の処理で対象のボタンを非活性にすると、
          // 対象のボタンのnameとvalue属性が取得出来なくなるため。
          $('<input />').attr('type', 'hidden')
          .attr('name', $(this).attr("name"))
          .attr('value', $(this).attr("value"))
          .appendTo($(this).closest('form'));

          // ボタンを非活性にし、2度押しを防止
          $(this).attr('disabled', 'disabled');

          // 一番近くのformをsubmit
          $(this).closest('form').submit();

          return false;
        }
      });
    });
  })(jQuery);
--></script>

各画面にこのスクリプトを読み込ませることで、2度押しを制御することが出来ました。

ここで、一つ問題が。
ちゃんと期待通りに動作する画面とそうでない画面があったのです。
デバックしてみるとどうやら14行目のclosest(‘form’)が取れないことが原因だということが分かりました。
正しく動作しないページにも自体はformは存在していました。

HTMLを見てみると正しくformが取れないページは以下のような構造をしていました。

<table>
  <tr>
    <td>
      <table>
        <tr>
          <td>
            <form name="test_form" action="/">
            <input type="text" name="a">
          </td>
        </tr>
        <tr>
          <td>
            <input type="text" name="b">
            <button type="submit" name="btn">
          </td>
        </tr>
      </table>
      </form>
    </td>
  </tr>
</table>

そう、原因はテーブルの違う階層でformの開始・終了タグが定義されていることでした。
closestは開始タグと終了タグに不整合があると正しく機能しないようです。
今回はformの開始タグをもう少し手前で宣言し、タグの不整合を解消したところスクリプトが正しく動作するようになりました。

最近はdivを使ってレイアウトを作成するのが主流かと思いますが、
今回のようにtableタグでレイアウトを整形しているレガシーなソースをメンテすることもあると思います。
もしclosestが上手く機能しない場合には、タグの不整合を疑って見ると良いことがあるかもしれません。

それではまた。