LoginSignup
9

More than 5 years have passed since last update.

jQueryでポップアップメニュー

Last updated at Posted at 2016-10-31

概要

jQueryベースでポップアップメニューを表示し、選択されたメニューに応じたfunction()を実行する方法。

定型なコードだと思うのだけど、実装すると意外にコード量が必要で面倒だった。今後の省力化のためでクラス(っぽいもの)化してみた、って話。左クリック、右クリック(長押し)への紐付けは click(function(){}), bind("contextmenu", function(){}) で好きなように。

・・・コード量が増えたのは、汎用性を意識したからだ?ハテ?(目を逸らしつつ。

デモはこちら
popmn.png

なお、jQuery mobile 利用環境であれば、自前実装せずともdata-role="popup" を指定することで簡単に実装できるみたい。⇒ http://qiita.com/tyoshikawa1106/items/f1c7824d9f4614d8a71f

使い方

var menu = new PopupMenu( $, {ポップアップメニュー内容});でインスタンスを生成して、表示したい場所のclick()等でmenu.show(event, this)するのみ。

sample.html
<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 の定義は以下。

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タグにはこちらを適用。

popupmenu_mobile.js
/**
 * 表示位置を左側へずらしたポップアップメニュー
 */
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は削除した方が良いかもしれない。そこまで大量のメニューを定義するとは思えなかったので、実装しなかったけど。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9