jQuery Mobile Menu Pluginでreadイベントが2回実行される件

  • 2
    Like
  • 0
    Comment
More than 1 year has passed since last update.

問題点

このサイトを参考jQuery Mobile Menu Pluginを使って、スマホ用のドロワを実装してました。(横からペロって出てくるメニューですね)
あるページに、送信ボタンのクリックイベントをjQueryで実装したら、2回送信されていました。
原因を突き詰めると、どうやらjQuery Mobile Menu Pluginが悪いらしい。

githubのissuesにもあるように、readイベントが2回実行されるもよう。

対処

あまり宜しく無い実装ですが、イベントが登録されたかチェックして2回目のイベント登録を無視するようにしました。

<!DOCTYPE html>
<html>
  <head>
    <link href="/css/jquery.mobile-menu.css" rel="stylesheet">
    <link href="/css/style.css" rel="stylesheet">

    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
    <script src="/js/jquery.mobile-menu.js"></script>
  </head>
  <body>

    <nav class="navbar">
      <!-- start / drawer menu -->
      <div id="main-nav">
        <ul class="list-group">
          <li class="list-group-item">
            <a href="/">ホーム</a>
          </li>
          <li class="list-group-item">
            <a href="/login">ログイン</a>
          </li>
          <li class="list-group-item">
            <a href="/announce_list">お知らせ</a>
          </li>
        </ul>
      </div>
      <!-- end / drawer menu -->
    </nav>

    <form>
      <input type="text" name="name">
      <button id="do_send">送信</button>
    </form>


  <script type="text/javascript">
    $(function (){
      // Mobile Menuの設定
      $("body").mobile_menu({
        menu: ['#main-nav ul'],  //ドロワーメニュー内の内容
        menu_width: 200,  //メニューのサイズ
        prepend_button_to: '#mobile-bar',  //トグルとなるボタン
        button_content: '<i><</i>'  //ボタンの画像
      });

      //  #do_sendに登録されているクリックイベントの数が1以上なら強制return
      flg = hasDomEvent('do_send', 'click', 1);
      if (flg) { return; };

      // クリックイベントの登録
      $("#do_send").click(function(e){
        alert("クリックイベント発生!!");
        return false;
      });

    });


    /**
     *  指定したDOMのイベントの数が制限数以上かチェックする
     *    mobile-menu.jsのバグで2回readイベントが走って、同じ関数が2回登録されるのを防ぐ
     *
     *  @param  string  selector    セレクタ。調べたいDOMオブジェクトのセレクタ。現状idのみ可
     *  @param  string  eventName   イベント名。click,change等
     *  @param  int     limitLength 制限(最大)イベント数。DOMに許容されるイベント数
     *
     *  @return bool  検証結果。true:イベントがある,false:イベントが無い
     *  @example
     *    //  #do_sendに登録されているクリックイベントの数が1以上なら強制return
     *    flg = hasDomEvent('do_send', 'click', 1);
     *    if (flg) { return; };
     */
    function hasDomEvent(selector, eventName, limitLength) {
      var events = $._data(document.getElementById(selector), 'events');
      // console.log(events);
      if (events == null) { return false };
      var anyEvents = events[eventName];
      // console.log(anyEvents);
      var anyEventsLength = anyEvents.length;
      // console.log(anyEventsLength);
      var flg = (anyEventsLength > limitLength);
      // console.log(flg);

      return flg;
    }
  </script>
  </body>
</html>

まとめ

  • 登録イベントの数をチェック
  • イベントが登録されていたらreturn

そもそもプラグインで修正してくれればいいんですけどね。。。

追記:readイベントが実行された時に検証する

上の方法はあまり良くありませんでした。反省してます。
クリックイベントに限らず、readイベントに登録してあったajax処理が2回実行されて困りました。

以下修正版です。

   <script type="text/javascript">
    $(function (){
      // readイベントの回数を検証
      // 2回目以降なら終了
      if(!readEventValidateModul.getInstance().validate()){return;}
      // Mobile Menuの設定
      $("body").mobile_menu({
        menu: ['#main-nav ul'],  //ドロワーメニュー内の内容
        menu_width: 200,  //メニューのサイズ
        prepend_button_to: '#mobile-bar',  //トグルとなるボタン
        button_content: '<i><</i>'  //ボタンの画像
      });

      // クリックイベントの登録
      $("#do_send").click(function(e){
        alert("クリックイベント発生!!");
        return false;
      });

    });



    }
/**
 *  readイベントが実行された回数で検証するモジュール
 */
var readEventValidateModul;

readEventValidateModul = (function() {
  var count, increment, init, instance, isFirst, validate;
  count = 0;
  instance = null;
  init = function() {
    return {
      validate: validate
    };
  };
  increment = function() {
    count++;
  };
  isFirst = function() {
    return count === 0;
  };
  validate = function() {
    if (isFirst()) {
      increment();
      return true;
    }
    return false;
  };
  return {
    getInstance: function() {
      if (!instance) {
        instance = init();
      }
      return instance;
    }
  };
})();
  </script>