Home
The jQuery Mobile tutorial - Managing accordion menus

Chapter 20

Managing accordion menus

Accordion menus are of course managed by the jQuery methods, but also by the collapsible () method added by jQuery Mobile. Two new events (expand and collapse) were also added by jQuery Mobile to facilitate their management. Accordion menus are associated with the collapsible standard component.

Dynamically create an accordion menu

An accordion menu is created using a <div> element with data-role="collapsible" attribute. An HTML title (eg <h1>) located in it will be used to give a title to the menu, while the contents of the menu will be represented by other elements in the wrapping <div>.

If more than one accordion menus are located in another <div> element with the data-role="collapsible-set" attribute, opening a menu will close all the others, allowing to have a single menu open at one time. Conversely, if the menus are not included in a <div> element with the data-role="collapsible-set" attribute, many menus can be opened simultaneously.

Create an accordion menu dynamically

<!DOCTYPE html> 

<html> 

<head> 

  <meta name=viewport content="user-scalable=no,width=device-width" />

  <link rel=stylesheet href=jquery.mobile/jquery.mobile.css />

  <script src=jquery.js></script>

  <script src=jquery.mobile/jquery.mobile.js></script>

</head> 


<body> 


<div data-role=page id=home>

  <div data-role=header>

    <h1>Home</h1>

  </div>


  <div data-role=content>

    <p> This is an accordion menu </p>

  </div>

</div>


</body>

</html>


<script>


var html = "";

html += "<div id=id1 data-role=collapsible>";

html +=   "<h1>Menu 1 : Click to open / close </h1>";

html +=   "<p> Paragraph 1.1 </p>";

html +=   "<p> Paragraph 1.2 </p>";

html +=   "<p> Paragraph 1.3 </p>";

html += "</div>";

html += "<div id=id2 data-role=collapsible>";

html +=   "<h1>Menu 2 : Click to open / close </h1>";

html +=   "<p> Paragraph 2.1 </p>";

html +=   "<p> Paragraph 2.2 </p>";

html +=   "<p> Paragraph 2.3 </p>";

html += "</div>";

html += "</div>";


$("#home div:jqmData(role=content)").append (html);


</script>




By default each menu is closed. To initialize a menu in the open state, just add the data-collapsed="false" attribute on the corresponding <div> element.

Turning an HTML element into a jQuery Mobile accordion menu

Using Firebug look at the HTML generated by jQuery Mobile to display the previous accordion menu:




The <h1> element associated to the menu title has a new <a> child element with ui-collapsible-heading-toggle class to act as a button, allowing clicking on the title element . Other elements in the <div> with the data-role="collapsible" attribute have been grouped into a new <div> element with ui-collapsible-content CSS class.

Insert accordion menus by Ajax

You can also retrieve an accordion menu by the Ajax mechanism.

Retrieve two accordion menus by Ajax

<!DOCTYPE html> 

<html> 

<head> 

  <meta name=viewport content="user-scalable=no,width=device-width" />

  <link rel=stylesheet href=jquery.mobile/jquery.mobile.css />

  <script src=jquery.js></script>

  <script src=jquery.mobile/jquery.mobile.js></script>

</head> 


<body> 


<div data-role=page id=home>

  <div data-role=header>

    <h1>Home</h1>

  </div>


  <div data-role=content>

    <p> This is an accordion menu </p>

  </div>

</div>


</body>

</html>


<script>


$.ajax (

  url : "action.php", 

  complete : function (xhr, result)

  {

    if (result != "success") return;

    var response = xhr.responseText;

    $("#home div:jqmData(role=content)").append (response);

    

    $("#id1, #id2").collapsible ();

  }

}); 


</script>

The collapsible () method is a jQuery Mobile method for transforming the original HTML code into HTML that can display accordion menus according to the jQuery Mobile conventions.

action.php file

<?
$html = "";
$html .= "<div id=id1>";
$html .=   "<h1>Menu 1 : Click to open / close </h1>";
$html .=   "<p> Paragraph 1.1 </p>";
$html .=   "<p> Paragraph 1.2 </p>";
$html .=   "<p> Paragraph 1.3 </p>";
$html .= "</div>";
$html .= "<div id=id2>";
$html .=   "<h1>Menu 2 : Click to open / close </h1>";
$html .=   "<p> Paragraph 2.1 </p>";
$html .=   "<p> Paragraph 2.2 </p>";
$html .=   "<p> Paragraph 2.3 </p>";
$html .= "</div>";
$html .= "</div>";
echo utf8_encode ($html);
?>

If you omit the call to the collapsible () method, accordion menus are displayed as simple HTML elements without any processing done by jQuery Mobile.




Note that you can replace the collapsible () method call by the triggering of the create event on the window. So you can replace the line:

Create accordion menus by calling the collapsible () method

$("#id1, #id2").collapsible ();

By the latter (provided the data-role="collapsible" attribute is set for each accordion menu returned by the server):

Create accordion menus triggering create event on the window

$("#home").trigger ("create");

Open and close an accordion menu

The opening or closing of an accordion menu is done by clicking on its title. It is possible with jQuery to simulate the click on the title using the trigger ("click") method.

The element corresponding to the <h1> title has the ui-collapsible-heading CSS class, while the <a> link included in it has ui-collapsible-heading-toggle class. We can simulate a click event on either of the two elements.

In the following example, we insert a button to open or close the window menu alternatively.

Open / close accordion menus managing the click on the <h1> element

<!DOCTYPE html> 

<html> 

<head> 

  <meta name=viewport content="user-scalable=no,width=device-width" />

  <link rel=stylesheet href=jquery.mobile/jquery.mobile.css />

  <script src=jquery.js></script>

  <script src=jquery.mobile/jquery.mobile.js></script>

</head> 


<body> 


<div data-role=page id=home>

  <div data-role=header>

    <h1>Home</h1>

  </div>


  <div data-role=content>

    <div id=id1 data-role=collapsible>

      <h1>Menu 1 : Click to open / close</h1>

      <p> Paragraph 1.1 </p>

      <p> Paragraph 1.2 </p>

      <p> Paragraph 1.3 </p>

    </div>

    <div id=id2 data-role=collapsible>

      <h1>Menu 2 : Click to open / close</h1>

      <p> Paragraph 2.1 </p>

      <p> Paragraph 2.2 </p>

      <p> Paragraph 2.3 </p>

    </div>

    <a id=btn href=# data-role=button> Open / close menus </a>

  </div>

</div>


</body>

</html>


<script>


$("#btn").bind ("click", function (event)

{

  $("h1.ui-collapsible-heading").trigger ("click");

});


</script>




It can also simulate the click on the <a> element created by jQuery Mobile inside the <h1> element:

Simulate the click on the <a> element with ui-collapsible-heading-toggle class

$("#btn").bind ("click", function (event)

{

  $("a.ui-collapsible-heading-toggle").trigger ("click");

});

Test whether an accordion menu is open or closed

When an accordion menu is closed, jQuery Mobile makes its contents invisible by adding the ui-collapsible-content-collapsed CSS class to <div> element having the ui-collapsible-content class corresponding to the hidden content. This added CSS class sets the display CSS property to none, which allows to hide the contents of the menu.

In addition, the menu title is also assigned the ui-collapsible-heading-collapsed class, indicating that the menu is closed.

So to test whether a menu is open or closed, just look if the <h1> element defining the title, or the <div> element defining the content has respectively ui-collapsible-heading-collapsed (for title) or ui-collapsible-content-collapsed (for content) classes. If any of these classes is present, the menu is closed. In the opposite case, the menu is open.

The following program displays the status of each accordion menus of the window using a button.

View the status of the accordion menu

<!DOCTYPE html> 

<html> 

<head> 

  <meta name=viewport content="user-scalable=no,width=device-width" />

  <link rel=stylesheet href=jquery.mobile/jquery.mobile.css />

  <script src=jquery.js></script>

  <script src=jquery.mobile/jquery.mobile.js></script>

</head> 


<body> 


<div data-role=page id=home>

  <div data-role=header>

    <h1>Home</h1>

  </div>


  <div data-role=content>

    <div id=id1 data-role=collapsible>

      <h1>Menu 1 : Click to open / close</h1>

      <p> Paragraph 1.1 </p>

      <p> Paragraph 1.2 </p>

      <p> Paragraph 1.3 </p>

    </div>

    <div id=id2 data-role=collapsible>

      <h1>Menu 2 : Click to open / close</h1>

      <p> Paragraph 2.1 </p>

      <p> Paragraph 2.2 </p>

      <p> Paragraph 2.3 </p>

    </div>

    <a id=btn href=# data-role=button> Indicate the status of menus </a>

  </div>

</div>


</body>

</html>


<script>


$("#btn").bind ("click", function (event)

{

  var txt = "";

  if ($("#id1 h1.ui-collapsible-heading-collapsed").length) 

    txt += "Menu 1: closed\n";

  else 

    txt += "Menu 1: open\n";

    

  if ($("#id2 h1.ui-collapsible-heading-collapsed").length) 

    txt += "Menu 2: closed\n";

  else 

    txt += "Menu 2: open\n";

    

  alert (txt);

});


</script>

We test the presence of ui-collapsible-heading-collapsed class in each of the menu titles. If the class is present, the menu is closed, otherwise it is open.




Manage events on accordion menus

To more easily manage accordion menus jQuery Mobile has created two new events we can handle using the bind () method:

These two events are used on <div> elements defining the accordion menus (with the data-role="collapsible" attribute).

Use the expand and collapse events on the accordion menu

<!DOCTYPE html> 

<html> 

<head> 

  <meta name=viewport content="user-scalable=no,width=device-width" />

  <link rel=stylesheet href=jquery.mobile/jquery.mobile.css />

  <script src=jquery.js></script>

  <script src=jquery.mobile/jquery.mobile.js></script>

</head> 


<body> 


<div data-role=page id=home>

  <div data-role=header>

    <h1>Home</h1>

  </div>


  <div data-role=content>

    <div id=id1 data-role=collapsible>

      <h1>Menu 1 : Click to open / close</h1>

      <p> Paragraph 1.1 </p>

      <p> Paragraph 1.2 </p>

      <p> Paragraph 1.3 </p>

    </div>

    <div id=id2 data-role=collapsible>

      <h1>Menu 2 : Click to open / close</h1>

      <p> Paragraph 2.1 </p>

      <p> Paragraph 2.2 </p>

      <p> Paragraph 2.3 </p>

    </div>

  </div>

</div>


</body>

</html>


<script>


$("#id1, #id2").bind ("collapsiblecreate", function (event)

{

  $(this).bind ("collapse", function (event)

  {

    alert ("Menu: closed");

  });

  $(this).bind ("expand", function (event)

  {

    alert ("Menu: open");

  });

});


</script>

Note that the observation of expand and collapse events occurs when the menus have been permanently transformed into a new HTML code by jQuery Mobile, otherwise it may produce dysfunctions (hence the use of the collapsiblecreate event).

In the case where the accordion menus are created during a call to the server by Ajax, the procedure is as follows:

Use the expand and collapse events in accordion menus retrieved by Ajax

<!DOCTYPE html> 

<html> 

<head> 

  <meta name=viewport content="user-scalable=no,width=device-width" />

  <link rel=stylesheet href=jquery.mobile/jquery.mobile.css />

  <script src=jquery.js></script>

  <script src=jquery.mobile/jquery.mobile.js></script>

</head> 


<body> 


<div data-role=page id=home>

  <div data-role=header>

    <h1>Home</h1>

  </div>


  <div data-role=content>

    <p>This is an accordion menu </p>

  </div>

</div>


</body>

</html>


<script>


$.ajax (

  url : "action.php", 

  complete : function (xhr, result)

  {

    if (result != "success") return;

    var response = xhr.responseText;

    $("#home div:jqmData(role=content)").append (response);

    

    $("#id1, #id2").collapsible ();


    $("#id1, #id2").bind ("collapse", function (event)

    {

      alert ("Menu: closed");

    });

    $("#id1, #id2").bind ("expand", function (event)

    {

      alert ("Menu: open");

    });

  }

}); 


</script>

action.php file

<?
$html = "";
$html .= "<div id=id1 data-role=collapsible>";
$html .=   "<h1>Menu 1 : Click to open / close </h1>";
$html .=   "<p> Paragraph 1.1 </p>";
$html .=   "<p> Paragraph 1.2 </p>";
$html .=   "<p> Paragraph 1.3 </p>";
$html .= "</div>";
$html .= "<div id=id2 data-role=collapsible>";
$html .=   "<h1>Menu 2 : Click to open / close </h1>";
$html .=   "<p> Paragraph 2.1 </p>";
$html .=   "<p> Paragraph 2.2 </p>";
$html .=   "<p> Paragraph 2.3 </p>";
$html .= "</div>";
$html .= "</div>";
echo utf8_encode ($html);
?>

Customize accordion menus

Thanks to the use of CSS classes seen previously, you can customize accordion menus, both for the title and the content.

Styling accordion menus

<!DOCTYPE html> 

<html> 

<head> 

  <meta name=viewport content="user-scalable=no,width=device-width" />

  <link rel=stylesheet href=jquery.mobile/jquery.mobile.css />

  <script src=jquery.js></script>

  <script src=jquery.mobile/jquery.mobile.js></script>

  

  <style type=text/css>

    .ui-collapsible-heading span.ui-btn-text {

      font-style : normal;

      color : red;

    }

    .ui-collapsible-heading-collapsed span.ui-btn-text{

      font-style : italic;

      color : black;

    }

    .ui-collapsible-content {

      background : black;

      color : white;

      text-align : center;

    }

  </style>

</head> 


<body> 


<div data-role=page id=home>

  <div data-role=header>

    <h1>Home</h1>

  </div>


  <div data-role=content>

    <div id=id1 data-role=collapsible>

      <h1>Menu 1 : Click to open / close</h1>

      <p> Paragraph 1.1 </p>

      <p> Paragraph 1.2 </p>

      <p> Paragraph 1.3 </p>

    </div>

    <div id=id2 data-role=collapsible>

      <h1>Menu 2 : Click to open / close</h1>

      <p> Paragraph 2.1 </p>

      <p> Paragraph 2.2 </p>

      <p> Paragraph 2.3 </p>

    </div>

  </div>

</div>


</body>

</html>




Examples of manipulation of accordion menus

Load the contents of an accordion menu by Ajax

We wish to retrieve the contents of each accordion menu by Ajax at the opening of the menu. Content is retrieved if the current content is empty, so as not to perform the recovery at each opening menu.

Get the contents of the accordion menus by Ajax

<!DOCTYPE html> 

<html> 

<head> 

  <meta name=viewport content="user-scalable=no,width=device-width" />

  <link rel=stylesheet href=jquery.mobile/jquery.mobile.css />

  <script src=jquery.js></script>

  <script src=jquery.mobile/jquery.mobile.js></script>

</head> 


<body> 


<div data-role=page id=home>

  <div data-role=header>

    <h1>Home</h1>

  </div>


  <div data-role=content>

    <div id=id1 data-role=collapsible data-collapsed=true>

      <h1>Menu 1 : Click to open / close</h1>

    </div>

    <div id=id2 data-role=collapsible data-collapsed=true>

      <h1>Menu 2 : Click to open / close</h1>

    </div>

  </div>

</div>


</body>

</html>


<script>


$("#id1, #id2").bind ("collapsiblecreate", function ()

{

  $("a.ui-collapsible-heading-toggle", this).bind ("click", function (event)

  {

    var h1 = $(this).closest ("h1");

    var content = h1.siblings (".ui-collapsible-content");

    var div = $(this).closest (".ui-collapsible");

    if (content.is (".ui-collapsible-content-collapsed") && 

        $.trim (content.html ()) == "")

    {

      $.ajax (

      

        url : "action.php", 

        data : { menu : div.attr ("id") },

        complete : function (xhr, result)

        {

          if (result != "success") return;

          var response = xhr.responseText;

          $(content).append (response);

        }

      });     

    }

  });

});


</script>

Each menu is created with an empty content in the HTML. The click is managed on the menu title from the <a> element of ui-collapsible-heading-toggle class. From this element, it navigates the DOM to retrieve the <div> element corresponding to the content (of ui-collapsible-content class), then the <div> encompassing the menu title and content, allowing as well accessing the id of the menu.

Subsequently, a simple test allows to know if the menu is closed, and if its content is empty or not. The Ajax call is made only if the menu is closed and empty. Remember, the content is closed if the ui-collapsible-content-collapsed CSS class is present in it, while the content is open otherwise.

action.php file

<?
$menu = $_REQUEST["menu"];

$html = "";
if ($menu == "id1")
{
  $html .=   "<p> Paragraph 1.1 </p>";
  $html .=   "<p> Paragraph 1.2 </p>";
  $html .=   "<p> Paragraph 1.3 </p>";
}
else
{
  $html .=   "<p> Paragraph 2.1 </p>";
  $html .=   "<p> Paragraph 2.2 </p>";
  $html .=   "<p> Paragraph 2.3 </p>";
}
echo utf8_encode ($html);
?>

Dynamically change the accordion menu title

The accordion menu title is currently fixed. It is possible to dynamically change, eg change the title as the menu is open or closed.

Change the menu title as the menu is open or closed

<!DOCTYPE html> 

<html> 

<head> 

  <meta name=viewport content="user-scalable=no,width=device-width" />

  <link rel=stylesheet href=jquery.mobile/jquery.mobile.css />

  <script src=jquery.js></script>

  <script src=jquery.mobile/jquery.mobile.js></script>

</head> 


<body> 


<div data-role=page id=home>

  <div data-role=header>

    <h1>Home</h1>

  </div>


  <div data-role=content>

    <div id=id1 data-role=collapsible>

      <h1>Menu: closed</h1>

      <p> Paragraph 1.1 </p>

      <p> Paragraph 1.2 </p>

      <p> Paragraph 1.3 </p>

    </div>

    <div id=id2 data-role=collapsible>

      <h1>Menu: closed</h1>

      <p> Paragraph 2.1 </p>

      <p> Paragraph 2.2 </p>

      <p> Paragraph 2.3 </p>

    </div>

  </div>

</div>


</body>

</html>


<script>


$("#id1, #id2").bind ("collapsiblecreate", function ()

{

  $("a.ui-collapsible-heading-toggle").bind ("vclick", function (event)

  {

    var h1 = $(this).closest ("h1");

    if (h1.is (".ui-collapsible-heading-collapsed")) 

      $("span.ui-btn-text", h1).first ().text ("Menu: open");

    else 

      $("span.ui-btn-text", h1).first ().text ("Menu: closed");

  });

});


</script>

The menu title is inserted in a <span> element of ui-btn-text class, located in the element that corresponds to the title (here <h1>). As many <span> elements of class ui-btn-text can be found in the title, it only changes the first (using the first () method). The content is modified by the text () method of jQuery.

Producing an effect at the opening and closing the accordion menu

Rather than open or close all at once the contents of the accordion menu, we want to do using a visual effect available in jQuery. For example the effect show (1000) to open and the effect hide (1000) to close. The parameter 1000 in this case is the duration of the effect in milliseconds.

Effect at the opening and closing accordion menus

<!DOCTYPE html> 

<html> 

<head> 

  <meta name=viewport content="user-scalable=no,width=device-width" />

  <link rel=stylesheet href=jquery.mobile/jquery.mobile.css />

  <script src=jquery.js></script>

  <script src=jquery.mobile/jquery.mobile.js></script>

</head> 


<body> 


<div data-role=page id=home>

  <div data-role=header>

    <h1>Home</h1>

  </div>


  <div data-role=content>

    <div id=id1 data-role=collapsible data-collapsed=true>

      <h1>Menu 1: Click to open / close</h1>

      <p> Paragraph 1.1 </p>

      <p> Paragraph 1.2 </p>

      <p> Paragraph 1.3 </p>

    </div>

    <div id=id2 data-role=collapsible data-collapsed=true>

      <h1>Menu 2: Click to open / close</h1>

      <p> Paragraph 2.1 </p>

      <p> Paragraph 2.2 </p>

      <p> Paragraph 2.3 </p>

    </div>

  </div>

</div>


</body>

</html>


<script>


$("#id1, #id2").bind ("collapsiblecreate", function ()

{

  $("a.ui-collapsible-heading-toggle").bind ("vclick", function (event)

  {

    var h1 = $(this).closest ("h1");

    var content = h1.siblings (".ui-collapsible-content");

    if (content.is (".ui-collapsible-content-collapsed")) content.show (1000);

    else content.hide (1000);

  });

});


</script>

We handle the click on the title of each accordion menu, especially the <a> link contained in the title. From this <a> link, we navigate into the DOM to get the contents of the menu corresponding to the <div> element of ui-collapsible-content class. Once it got, we can call the show () or hide () methods depending on whether the content is open or closed.


Copyright Eric Sarrion (ericsarrion@gmail.com)