Do I have to create a new panel for every page?

16,116

Solution 1

Your best course of action is to dynamically create a panel for every page.

I made you a working example: http://jsfiddle.net/Gajotres/pZzrk/

$(document).on('pagebeforeshow', '[data-role="page"]', function(){                
    $('<div>').attr({'id':'mypanel','data-role':'panel'}).appendTo($(this));
    $('<a>').attr({'id':'test-btn','data-role':'button'}).html('Click me').appendTo('mypanel');
    $.mobile.activePage.find('#mypanel').panel();
    $(document).on('click', '#open-panel', function(){   
         $.mobile.activePage.find('#mypanel').panel("open");       
    });    
});

Few descriptions:

  • $.mobile.activePage is needed because we will have same panel in every page, and all of them will have same id. If we open it without active page we will open its first occurrence inside the DOM.

jQuery Mobile developers have stated that in next major version panel widget will no longer need to be part of a page, instead it will be placed in a same level as a page div. So one panel will ne needed. Unfortunately you will need to dynamically create it.

Solution 2

Here's the solution I came up with. I store the panel contents in a hidden div, and defer the jquery mobile initialization. When the document loads, I append the panel contents to each of the (jqm) page elements and then initialize jqm. The only performance hit occurs when the page first loads.

HTML:

<div data-role='page' class='page'>
  <div data-role='content'>
    <h1>One</h1>
    <a href='#nav' data-role='button'>show nav</a>
  </div>
</div>

<div data-role='page' class='page'>
  <div data-role='content'>
    <h1>Two</h1>
    <a href='#nav' data-role='button'>show nav</a>
  </div>
</div>

<div id='panel-content' style='display:none'>
   <div data-role='panel' class='panel-holder' data-position="right" data-display="reveal" id='nav'>
   <div data-role="content">
     <ul>
       <li><a href="#first" data-link="first">first</a></li>
       <li><a href="#second" data-link="first">second</a></li>
     </ul>
   </div>
</div>
</div>

Script:

$.mobile.autoInitializePage = false;
$(document).on("ready" function(evt) {
    var panelHtml = $("#panel-content").html();
    var pages = $(".page");
    for (var i = 0; i < pages.length; i++)
    { //done synchronously so we can initialize jquery mobile after the DOM is all setup
       $(pages[i]).append(panelHtml);
    }

    $("#panel-content").remove(); // this doesn't need to be in the DOM anymore
    $.mobile.initializePage();
});

Solution 3

I've been wrestling with this myself, and here's the solution I'm using:

$( ".page" ).on(
    "pageshow",
    function ( event, prevPage ) {
        var $page = $( this ),
            $prevPage = $( prevPage.prevPage ),
            $menuPanel = $( "#panel-menu", $prevPage );

        $menuPanel
            .remove()
            .prependTo( $page );

        $page.trigger( "create" );
    }
);

The trick seems to be to use .remove() instead of .detach() to pick up the panel you want to move - without any stored jQuery objects - so that jQuery Mobile doesn't make assumptions about the markup. Also, it strikes me that this solution adds considerable overhead from DOM manipulation. It's not ideal, but it does work.

EDIT: Note that it's set up to work for every page, and is fired on loading the first page, where you presumably have the menu to start with and where there is no previous page object. But you probably could keep the menu elsewhere and look for it there too as a fallback.

Solution 4

with inspiration from Gajotres and the way AppFramework handles panels I've made this. It works by copying defined panels to the active page, the panels are defined by id in right-panel and left-panel attributes for the page div:

$(document).on('pagebeforeshow', '[data-role="page"]', function(){
    // remove unused tmp panels from DOM
    $("#tmpRightPanel").remove();
    $("#tmpLeftPanel").remove();

    // Hide buttons by default (I'm using a static header and footer on every page too)
    $("#openRightPanel").css("display", "none");
    $("#openLeftPanel").css("display", "none");

    // check if right-panel attribute is set on the page
    if ($(this).attr("right-panel")) {
        // if it is, it should append the defined right-panel to the page
        $("#"+$(this).attr("right-panel")).clone().appendTo($(this));

        // rename it to tmpRightPanel
        $.mobile.activePage.find('#'+$(this).attr("right-panel")).attr("id", "tmpRightPanel");

        // make it a panel
        $.mobile.activePage.find('#tmpRightPanel').panel();

        // make it visible (the original right panel is set to display: none)
        $.mobile.activePage.find('#tmpRightPanel').css("display", "block");

        // make the button to open the panel visible
        $("#openRightPanel").css("display", "block");
    }

    // same as right-panel above
    if ($(this).attr("left-panel")) {
        $("#"+$(this).attr("left-panel")).clone().appendTo($(this));
        $.mobile.activePage.find('#'+$(this).attr("left-panel")).attr("id", "tmpLeftPanel");
        $.mobile.activePage.find('#tmpLeftPanel').panel();
        $.mobile.activePage.find('#tmpLeftPanel').css("display","block");
        $("#openLeftPanel").css("display", "block");
    }
});

// make the open panel buttons clickable
$(document).on('click', '#openRightPanel', function(){   
    $.mobile.activePage.find('#tmpRightPanel').panel("open");
});

$(document).on('click', '#openLeftPanel', function(){
    $.mobile.activePage.find('#tmpLeftPanel').panel("open");
});

Make a page like this:

    <div id="main" data-role="page" data-title="Main" right-panel="right-panel" left-panel="left-panel">
         <div class="ui-content">
              some page
         </div>
    </div>

and place the panels somewhere outside a page, and hide them like this:

    <!-- leftpanel -->
    <div data-role="panel" id="left-panel" data-display="push" data-position="left" data-theme="a" style="display:none;">
         something something something
    </div>
    <!-- /leftpanel -->

    <!-- rightpanel -->
    <div data-role="panel" id="right-panel" data-display="push" data-position="right" data-theme="a" style="display:none;">
         something something something
    </div>
    <!-- /rightpanel -->
Share:
16,116
dsdsdsdsd
Author by

dsdsdsdsd

Updated on July 27, 2022

Comments

  • dsdsdsdsd
    dsdsdsdsd almost 2 years

    I would like to use a Panel in a jqm site for my Choose Language component. So that component will need to be present on every page. Here is the setup for a single-page panel.

    from jquerymobile.com ( ... I added a header button)

      <div id="mypanel" data-role="panel" >
        <!-- panel content goes here -->
      </div><!-- /panel -->
    
      <div id="myheader" data-role="header" >
        <a id='panel_toggle' data-role='button'>choose language</a>
      </div><!-- /header -->
    
      <!-- content -->
    <!-- footer -->
    
    </div><!-- page -->
    

    I figure that I have 3 solutions:

    • A - create a duplicate copy of the panel on every page ---- this will be a problem if the state on page_N changes, then all others will need to be synshronized

    • B - create a single panel that is pro-grammatically moved on page changes ---- this seems like a hack

    • C - discover if jqm already has a solution to this problem ---- hence I am asking the question :)

    Does jqm have a way to move a Panel from page to page?

  • dsdsdsdsd
    dsdsdsdsd about 11 years
    I'm really disliking the idea of multiple 'pages' on one index.html ... it's a cute trick, but just too many problems
  • Jay Mayu
    Jay Mayu about 11 years
    as you can see panels are wrapped inside pages. so I think that would be the good idea though u need to write panel div multiple times. Take buttons on the header for an example. You hard code html back button everytime in the header. Simply just like that
  • dsdsdsdsd
    dsdsdsdsd about 11 years
    in my case I am wanting to put a language chooser in the panel, and the problem with a language chooser component is that its state changes ... if language is english, then the English option is highlighted; if language is japanese, then the Japanese option is highlighted, etc. ... ... So if I have a duplicate panel on every page, I will have to update every one of them whenever a language is changed.
  • Jay Mayu
    Jay Mayu about 11 years
    why can't you keep the language in sessionStorage or localStorage and during pageshow event check the storage and highlight or do whatever in the panel??
  • Jay Mayu
    Jay Mayu about 11 years
    +1 for it. Good one, but it would be a performance hit. But I love the innovative solution.
  • Gajotres
    Gajotres about 11 years
    Unfortunately currently it is either this solution or other one (Mayus solution). Also this is not going to be that much big performance hit. Full page refresh take around 50ms.
  • Jorg Ancrath
    Jorg Ancrath about 11 years
    Would just like to thank Gajotres (as well as Jasper), you have been terrific help with tackling these jQuery Mobile issues!
  • zitroneneis
    zitroneneis almost 11 years
    +1, that's a nice solution. One comment though: if you want to add widgets to the side panel, you should use the 'pagebeforecreate' event to add the panel and content, because otherwise they will not be displayed correctly.
  • Gajotres
    Gajotres almost 11 years
    You are correct. Or they can be enhanced manually with $('#pageID').trigger('pagecreate');
  • Admin
    Admin almost 11 years
    Gajotres, I am a little confused by your code. It seems that it does not work on chrome. First .appendTo('mypanel') should probably be .appendTo('#mypanel') Second $('').attr({'id':'test-btn','data-role':'button'}).html('Cli‌​ck me').appendTo('mypanel'); has no href defined Third, I am not able to get the second page opening the panel Could someone please help?
  • dejavu
    dejavu almost 11 years
    Yeah, it should be #mypanel. For anchor tag, it is bind to onclick event which opens the panel. I don't know what problem are you facing. The code is pretty straightforward. Please post it as a new question. Don't bump an old thread.
  • Varun Nath
    Varun Nath over 10 years
    @Gajotres - This solution does work but this will cause the panel to be added every time the page is loaded. So won't there be multiple panels on each page ?
  • William Gaul
    William Gaul over 10 years
    This was easy to implement and worked nice for me. Thank you.
  • aqingsao
    aqingsao over 9 years
    Hi, panels outside pages is already supported in jqm version 1.4: "If you want to use the same panel on multiple pages you can place the markup outside the page.", please see demos.jquerymobile.com/1.4.0/panel-external