Creating popup menus in JQuery Mobile is not quite simple. I am talking about menu that you might want to display when user clicks on, for example, a list item or a button. Refer to JQuery Mobile documents for select menus and custom menus . These menus are actually drop down lists and in all the cases, the menu has to be already visible.
However this is not what I wanted in my mobile apps. I do not want menu to be visible initially, but want to display it when user clicks on a button on a list item. So I started investigating how this could be done. As it turned out, the menu that JQuery Mobile shows (when nativeMenu attribute is set to false), is different from the select tags that you create for the menu. JQM hides the menu that you create and creates a new (wrapper) menu using div and anchor tags. Option tags become anchor tags in the new menu. This is all fine, but the problem is that JQM does not have any direct APIs to access elements of the new menu. This makes it difficult to attach event handlers to menu options.
But JQM follows certain patterns when creating the wrapper menu. As of the current version, which is 1.1, JQM does following –
- It creates an outer div with ui-selectmenu class
- Within the above div it creates ul element with id = id_of_the_select_element + “-menu”
- It creates li element for each menu option inside the above ul element
- Actual menu items are in anchor element inside the above li element
All this is not documented by JQuery Mobile, so it might change in the future versions.
So, having understood the above pattern, it was then easy to create a menu and attach event handler. Here is a simple example, which displays a menu when a button is clicked –
First a screen shot –
<!DOCTYPE html> <html> <head> <link type="text/css" href="jquery.mobile-1.2.0.css" rel="stylesheet"> <script type="text/javascript" src="jquery-1.8.2.js"></script> <script type="text/javascript" src="jquery.mobile-1.2.0.js"></script> <script type="text/javascript"> $(document).bind("pageinit", onPageInit); //variable to store menu instance newMenu = null; function onPageInit() { //create menu. First option in the third argument is menu title createMenu("dynamicMenu1","contentDiv","Menus,menu1,menu2,menu3",menuHandler); $("#btn1").click(function(){ showMenu(newMenu); }); } function createMenu(menuId, parentId, options, menuHandler) { //create a containing div var div = $("<div id='" + menuId + "div'></div>").appendTo("#"+parentId).hide(); //create select tag var menuElm = $("<select id='" + menuId + "' data-inline='true' data-native-menu='false'></select>").appendTo(div); //add options var optionsArray = options.split(","); for (var i = 0; i < optionsArray.length; i++) $("<option>" + optionsArray[i] + "</option>").appendTo("#"+menuId); //convert to JQueryMobile menu $("#" + menuId).selectmenu(); //find custom menu that JQM creates var menus = $(".ui-selectmenu"); for (var i = 0; i < menus.length; i++) { //if ($(menus[i]).children("ul:#" + menuId + "-menu").length > 0) if ($(menus[i]).children("ul").filter("#" + menuId + "-menu").length > 0) { newMenu = $(menus[i]); break; } } //Hack for JQM 1.2 - check if parent of select menu is ui-popup-container var menuContainer = $(menus).parent(".ui-popup-container"); if (menuContainer.length > 0) { var pageElm = menuContainer.parent("div[data-role='page']"); if (pageElm.length > 0) { menus.detach(); menuContainer.remove(); pageElm.append(menus); menus.css("width", "80%"); menus.css("max-width", "350px"); } } if (newMenu == null) { alert("Error creating menu"); return; } //Associate click handler with menu items, i.e. anchor tags $(newMenu).find(".ui-selectmenu-list li a").click(menuHandler); //Add Close option var menuHeader = $(newMenu).find(".ui-header"); var closeLinkId = menuId + "_close_id"; menuHeader.prepend("<span style='position:relative;float:left'>" + "<a href='#' id='" + closeLinkId + "'>X</href></span>"); $("#" + closeLinkId).click(function(e){ newMenu.hide(); }); return newMenu.hide(); } function showMenu(menu) { if (menu == null) return; //show menu at center of the window var left = ($(window).width() - $(menu).width()) / 2; //consider vertical scrolling when calculating top var top = (($(window).height() - $(menu).height()) / 2) + $(window).scrollTop(); $(menu).css({ left: left, top: top }); $(menu).show(); } //Callback handler when menu item is clicked function menuHandler(event) { if (newMenu != null) $(newMenu).hide(); alert(event.srcElement.text); } </script> </head> <body> <div data-role="page" > <div data-role="content" id="contentDiv"> <a href="" data-role="button" data-inline="true" id="btn1">Show Menu</a> </div> <span style='position:relative;float:left'></span> </div> </body> </html>
– Ram Kulkarni
Ram – very nice, this saved me a lot of headache! Curious, what if I wanted to capture a value of a list item, or even the index of which item in the list. I’m not finding an easy way to do that.
Here is how you can attach any data with menu item and retrieve it when it is clicked. In the createMenu function, add this towards the end –
——————
//Get all menu anchors
var menuAnchors = $(newMenu).find(“.ui-selectmenu-list li a”);
for (var i = 0; i < menuAnchors.length; i++) { //I am associating index with each menu item $(menuAnchors[i]).data("index",i); } ------------------ Then in the menu handler you can retrieve it as - alert(event.srcElement.text + " " + $(event.srcElement).data("index"));
And one other question. My app needs to call the select menu possibly a few times, with different items in the list each time possibly. I attempted to do a $(newMenu).remove() after it is closed, but the next time createmenu is called, it does not like that. Is there a clean way to destroy and recreate the menu each time?
I added the following to the createMenu function and it seems to work fine:
if (newMenu != null){
$(newMenu).remove();
$(“#” + menuId).remove();
newMenu = null;
}
I am trying to extend it so that I can add anchor tags to the list items, but I can’t find a convenient way to do this as it is. I could pass the create function an array or a string as you did and then add them to the options, but somehow I don’t like the idea.
Have you got an idea how to do this “the smart way”? 😉
Great job by the way. The jQuery Mobile select menu is quite useless if you can’t access it programmatically and you addressed that. Thanks for that.
Daniel,
JQM finally creates anchor tags for each list items. For example the list item for ‘menu1’ option in the example I posted in the blog is created by JQM as –
It might help if you describe how you want to add anchor tags.
Thanks a lot for solution! One question: how to add 2 different menus with this functions? Thanx
I have used a global variable newMenu, but if you make it a local variable inside createMenu function, then you should be able to create multiple menus by calling createMenu function. This function already returns the menu instance.
how to close the menu??
I have updated the code to add ‘X’ to the menu header to close it. I am no expert in CSS, so could not style it properly.
This is the snippet of code I added towards the end of createMenu function –
Thanks a lot for quick solution!
Is it possible to make that menu list scrollable???
Here is how you can make options scrollable –
I have also updated the post.
sorry but scroll is not working form me
I tested it in Chrome and IE and works fine. You may have to debug the problem at your end. Note that if height of the scrollable div (as specified in scrollableDivHeight variable) is greater than height of the menu, then scrollbar won’t appear. And I am assuming that you have set variable scrollable to true.
Yes,its working in Chrome but I want to make it workable on android phone
Yes, it does not work on mobile. I have removed this code from the post. Actually if there are many menu items, then the menu can be scrolled with the entire page. But I guess you don’t want that.
The code that I posted earlier for scrolling kind of works on mobile – menu items are displayed in a div with fixed height, but scroll bar is not displayed. So there is no way to scroll menu items. I can’t think of any quick and easy solution.
Ok.Thanks a lot.
Hi, I was testing using jquery-1.8.1 and it is not working, it failed when trying to find custom menu created by JQM, in this line
if ($(menus[i]).children(“ul:#” + menuId + “-menu”).length > 0)
log said:
jquery-1.8.1.js:4642Error: Syntax error, unrecognized expression: ul:#dynamicMenu1-menu
Would you please help me?
Replace ‘if’ statement with this –
Hai,
To create the show menu to one of the jquery chart applications how to link of the next process?
If I understand your question correctly, you are asking how to show menu on JQuery chart. If so, the process is similar to what I described in the post. It is two step process – first you create the menu, with createMenu function. Once created, it can be displayed by calling showMenu function. In your case, you may call it in any event handler of the chart.
HI Ram, firstly great work on the popup menu is just was I was looking for. Just to follow on from @Wills comment re: JQM 1,2.0 even after the change to the filter it still doesn’t show the menu.
After inspecting the html I can see a parent div with class ui-selectmenu-hidden that kind of shifts the menu way off screen. If you select the parent and remove this class you can get it to appear by adjusting the top and left (not sure if this is the best approach).
I think it would be worthwhile updating your post with the latest versions of JQuery and JQM as it is still a great solution for a simple to build dynamic popup menu.
Cheers
Jono
I have updated the post. Now the code should work with JQM 1.2 and JQ 1.8.2.
Hi, Ram, this is the best solution I found after several days of reading. Would it be possible to filter menu data? Tnx in advance!
You can write code to filter in showMenu method, just before $(menu).show(). For example, if I want to hide menu2, then I would do something like –
Or you can get all anchor tags of pop-up menu and filter them individually –
Ram,thank You very much!
hi, ram this site(http://ebager.dk/beting1.aspx) dynamic dropdown menu possible?
If you are asking where you could use pop-up/dropdown menu on your website, then I would say I am not an expert in UEx design. If you are asking if pop-up menu explained in this post could be used on the web site, then the answer is yes. But note that the menu is created with JQuery Mobile and meant for mobile sites. Having said that, nothing stops you from using it on desktop sites.
Hi,
I tried to call the select menu programmatically, But trigger & click are not working. Can you suggest any workaround? I’m using native select menu.