Edited at

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

More than 3 years have 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>