概要
jQueryベースでポップアップメニューを表示し、選択されたメニューに応じたfunction()を実行する方法。
定型なコードだと思うのだけど、実装すると意外にコード量が必要で面倒だった。今後の省力化のためでクラス(っぽいもの)化してみた、って話。左クリック、右クリック(長押し)への紐付けは click(function(){}), bind("contextmenu", function(){})
で好きなように。
・・・コード量が増えたのは、汎用性を意識したからだ?ハテ?(目を逸らしつつ。
デモはこちら。
なお、jQuery mobile 利用環境であれば、自前実装せずともdata-role="popup"
を指定することで簡単に実装できるみたい。⇒ http://qiita.com/tyoshikawa1106/items/f1c7824d9f4614d8a71f
使い方
var menu = new PopupMenu( $, {ポップアップメニュー内容});
でインスタンスを生成して、表示したい場所のclick()
等でmenu.show(event, this)
するのみ。
<html lang="ja">
<head>
<title>Pupupメニューテスト</title>
<meta http-equiv="Content-Script-Type" content="text/javascript; charset=utf-8" />
<meta name="viewport" id="id_viewport" content="width=device-width" >
<script src="https://code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="./popupmenu.js"></script>
<script language="JavaScript">
<!--
$(document).ready(function(){
var menu1 = new PopupMenu( $, [
{ "title" : "hoge",
"action" : function(){ alert("hoge!"); }
},
{ "title" : "ふが",
"action" : function(){ alert("ふが!"); }
}
]);
var menu2 = new PopupMenu( $, [
{ "title" : "piyo",
"action" : function(e, targetNode){
var target = $(targetNode);
alert( "piyo\r\nクリックエリアの内容:\r\n" + target.text() );
}
},
{ "title" : "foo",
"action" : function(){ alert("foo!"); }
}
]);
var menu3 = new PopupMenu( $, [
{ "title" : "右クリックコンテキストメニュー",
"action" : function(){ alert("スマホでは長押しに相当"); }
}
]);
$("#id_div1").click(function(event){
menu1.show(event, this);
});
$("#id_div2").click(function(event){
menu2.show(event, this);
});
$("#id_div3").click(function(){
menu1.hide(); // これでmenu2, menu3側も閉じる。
}).bind("contextmenu", function(event){
menu3.show(event, this);
return false; // デフォルトのコンテキストメニューを解除
});
});
//-->
</script>
</head>
<body bgcolor="#ccddff">
<div id="id_div1" style="background-color: green;">
<br>
<br>
<br>
ここでクリックするとポップアップ:その1する<br>
<br>
<br>
<br>
</div>
<div id="id_div2" style="background-color: yellow;">
<br>
<br>
<br>
ここでクリックするとポップアップ:その2する<br>
<br>
<br>
<br>
</div>
<div id="id_div3">
<br>
<br>
<br>
ここで(左)クリックすると閉じる。<br>
右クリック(か長押し)すると、ポップアップ:その3する。<br>
<br>
<br>
<br>
</div>
</body>
</html>
ポップアップメニューのクラス定義
上記で利用している、popupmenu.js
の定義は以下。
/**
* @description ポップアップメニュー表示クラス
* @param{Object} jQueryCommon 利用するjQuery オブジェクトを渡す。静的に使いまわすので、2回目以降はNULL指定可能。
* @param{Object} menuList 表示するポップアップメニューリスト。title, action メンバを持つこと。
*/
function PopupMenu( jQueryCommon, menuList ){
/**
* @description ポップアップメニューに表示するリスト。title, action メンバを持つ。
*/
this.itsMenuList = menuList;
/**
* @description 設定されたポップアップメニューの最大文字列。デフォルト14文字。
*/
this.itsMaxStringLength = (function( list, defaultLength ){
var i, n = list.length, title, action;
for( i=0; i<n; i++ ){
title = list[i].title;
action = list[i].action;
if( (title && action) && ( defaultLength < title.length ) ){
defaultLength = title.length;
}
}
return defaultLength;
}(menuList, 14));
this.itsId = "_id_popup"; // ※init()の中で数字を付加してユニーク化される。
this.init( jQueryCommon );
};
(function(){
/**
* @description ポップアップメニューのdiv要素CSS
*/
var MENU_DIV_CSS = {
"position" : "absolute",
"min-width" : "200px",
"background-color" : "#eeeeee",
"border": "1pt solid #666666",
"box-shadow" : "2px 2px 4px 0px #666666"
};
/**
* @description ポップアップメニューのulタグのCSS
*/
var MENU_UL_CSS = {
"border": "0pt",
"margin" : "2px",
"padding" : "6px",
"line-height": "12pt",
"color": "#000",
"list-style" : "none",
"font-size" : "11pt"
};
/**
* @description ポップアップメニュー項目のCSS
*/
var LI_CSS = {
"padding" : "6px",
"background-color" : "#eeeeee",
"border" : "1px solid transparent"
};
/**
* @description ポップアップメニュー項目のHover時のCSS
*/
var LI_CSS_HOVER = {
"background-color" : "#d1e2f2",
"border" : "1px solid #78aee5"
}; // http://www.tohoho-web.com/wwwxx035.htm
/**
* @description このクラスで利用するjQuery。
*/
var jQ = null;
/**
* @description 生成されたポップアップメニューIDのリスト。
*/
var menuIds = [];
/**
* @description 全てのポップアップメニューを非表示にする。
* @param{String} exceptId 捜査対象外のID。null指定なら、全部を「非表示」にする。
*/
var hideAll = function( exceptId ){
var n = menuIds.length;
var target;
while( 0<n-- ){
target = jQ("#" + menuIds[n]);
if( (menuIds[n] != exceptId) && (target.size() > 0) ){
target.hide();
}
}
}
/**
* @description 最近のshow()で渡された、第一引数Eventと、第二引数。
*/
var staticLastEvent = null, staticLastParam = null;
/**
* @description 本クラスの初期化。
* @param{Object} initJQuery jQueryオブジェクトを渡す。初回以降はnullでも構わない。
*/
PopupMenu.prototype.init = function( initJQuery ){
var BIND_INDEX_ATTR = "data-action-index";
var i, list = this.itsMenuList, n = list.length, str, title, action;
var target, item;
jQ = (!jQ) ? initJQuery : jQ;
this.itsId += "_" + menuIds.length;
target = jQ("body");
target.append( "<div id='" + this.itsId + "' style='display:none;'><div>" );
target = jQ("#" + this.itsId);
target.css( MENU_DIV_CSS );
str = "<ul>";
for( i=0; i<n; i++ ){
title = list[i].title;
action = list[i].action;
if( title && action ){
str += "<li " + BIND_INDEX_ATTR + "='" + i + "'>" + title + "</li>";
}
}
str += "</ul>";
target.append( str );
target = target.children("ul").eq(0);
target.css( MENU_UL_CSS );
target = jQ("#" + this.itsId + " ul li" );
target.css( LI_CSS );
target.hover(
function(){ jQ(this).css( LI_CSS_HOVER ); },
function(){ jQ(this).css( LI_CSS ); }
);
target.click(function(event){
var item = jQ(this);
var index = item.attr( BIND_INDEX_ATTR );
hideAll(null);
list[ index ].action( staticLastEvent, staticLastParam );
})
menuIds.push( this.itsId );
}
/**
* @description ポップアップメニューを表示する。
* メニュー以外の場所をクリックすると消える。他のポップアップメニューが表示された時も、以前のは消える。
* @param{event} event jQuery.click(function(event){})で渡されるeventオブジェクトを指定する。
* 呼び出し先の関数の引数にそのまま渡す。
* @param{Object} paramObj newの際のactionパラメータに指定された関数呼び出し時の第二引数に渡される。
* 通常はthisを渡すこと。
*/
PopupMenu.prototype.show = function(event, paramObj){
/*
[メモ] click() はeventが渡される仕様。
---
jQueryのイベントは、コールバック関数の最初の引数でjQuery.Eventオブジェクトを受け取ることができます。
このオブジェクトを使って、規定のイベント動作のキャンセルや、バブリングの抑制などを行います
http://semooh.jp/jquery/api/events/click/fn/
*/
var target = jQ("#" + this.itsId);
var isShowed = (target.css("display") == "none");
var pos;
hideAll( this.itsId );
if( isShowed ){
pos = this.getMenuPosition( event );
target.css({
"top" : pos.pageY + "px",
"left" : pos.pageX + "px",
});
target.slideDown("fast");
staticLastEvent = event;
staticLastParam = paramObj;
}else{
target.hide();
}
};
/**
* @description ポップアップメニュー表示毎に、表示位置決めのためにcallbackされる。
* サブクラスで任意にオーバーライドのこと。
*/
PopupMenu.prototype.getMenuPosition = function( event ){
return { "pageX" : event.pageX, "pageY" : event.pageY };
};
/**
* @description 表示中のポップアップメニューを全て非表示にする。
*/
PopupMenu.prototype.hide = function(){
hideAll( null );
};
}());
表示位置の任意修正
ポップアップメニューの表示位置を変更する場合は、以下のように上記を継承したサブクラスを生成して行う。下記を用いて new PopupMenuMobile($,{})
すればOK。デモの2つ目のdivタグにはこちらを適用。
/**
* 表示位置を左側へずらしたポップアップメニュー
*/
var PopupMenuMobile = function( jQueryCommon, menuList ){
PopupMenu.call(this, jQueryCommon, menuList);
};
PopupMenuMobile.prototype = Object.create( PopupMenu.prototype );
PopupMenuMobile.prototype.getMenuPosition = function( event ){
var nX = event.pageX - this.itsMaxStringLength * 14 + 8;
nX = (nX > 0) ? nX : 0;
return { "pageX" : nX, "pageY" : event.pageY };
};
以上ー。
補足
hideAll()
のタイミングで、無効なIDは削除した方が良いかもしれない。そこまで大量のメニューを定義するとは思えなかったので、実装しなかったけど。