Make Listview Dividers Collapsible


The listview widget in the jQuery Mobile (jQM) library allows for divider listitems which can be inserted manually or for sorted lists even be created automatically by the library.  The dividers organize larger sets of listitems  into smaller groups making it easier for the user to view the desired information. In this article I will implement a small trick that uses clicks/taps on the divider items to collapse expand the listitems in the divider group. This works particularly well for autodivider lists saving you the need to manually create collapsible widgets with separate contained lists.

For the example I will use a longish list of names with autodividers enabled. The markup for the list looks like this:

<ul data-role="listview" data-autodividers="true" data-inset="true" data-divider-theme="a" >
  <li><a href="#">Aaron</a></li>
  <li><a href="#">Adam</a> </li>
  <li><a href="#">Alexander</a></li>
  <li><a href="#">Alice</a></li>
  <li><a href="#">Andrew</a></li>
  <li><a href="#">Anna</a></li>
  <li><a href="#">Anthony</a></li>
  <li><a href="#">Audrey</a></li>
  <li><a href="#">Benjamin</a></li>
  <li><a href="#">Brandon</a></li>
  ...
  <li><a href="#">Tristan</a></li>
  <li><a href="#">Tyler</a></li>
  <li><a href="#">Violet</a></li>
  <li><a href="#">Vivienne</a></li>
  <li><a href="#">William</a></li>
  <li><a href="#">Xavier</a></li>
  <li><a href="#">Zachary</a></li>
  <li><a href="#">Zoe</a></li>
</ul>

To indicate that clicking the dividers collapses the group, I will add a MINUS icon at the left of the divider (later when we collapse the group, I will add code that switches the icon to a PLUS). During the pagecreate event, I create the markup for an inline icon span and prepend it to all dividers so the icon appears to the left of any text. jQM assigns a class of ui-li-divider to all divider listitems:

$(document).on("pagecreate", "#page1", function () {
    var ic = '<span class="ui-icon-minus ui-btn-icon-notext inlineIcon"></span>';
    $(".ui-li-divider").prepend(ic);
});

To make the icon appear inline with the text, I add the following CSS rule to override the jQM default settings for icons:

.inlineIcon {
    display: inline-block;
    position: relative;
    vertical-align: middle;
    margin-right: 6px;
}

listWithIcon

Once the icon is in place, I add a click handler for the divider listitems. This handler uses a while loop to step through subsequent listitems using the jQuery next() method, toggling their visibility  until the next divider is reached or the list ends. By using the jQuery slideToggle() method, the group expands/collapses with a sliding animation. Then to switch the icon on the divider, I determine if the group is collapsed or not by the CSS display attribute of the listitems in the group. With that, I simply remove one icon class and add the other:

$(document).on("click", '.ui-li-divider', function (e) {
    var IsCollapsed = false;
    var TheDivider = $(this);
    var li = TheDivider.next(':not(.ui-li-divider)');
    while (li.length > 0) {
        IsCollapsed = li.css('display') == 'none';
        li.slideToggle(300);
        li = li.next(':not(.ui-li-divider)');
    }
    var $icon = TheDivider.find('.inlineIcon');
    if (!IsCollapsed) {
        $icon.removeClass('ui-icon-minus').addClass('ui-icon-plus');
    } else {
        $icon.removeClass('ui-icon-plus').addClass('ui-icon-minus');
    }
    e.stopPropagation();
    return false;
});

If the list is pretty long and you have many dividers, it is a good idea to provide the user with ‘Expand All’ and Collapse All’ buttons. For this example I add the 2 buttons in a jQM grid and give them both a class of collapseExpand:

<div class="ui-grid-a ui-mini">
  <div class="ui-block-a"><a id="btnExpand" class="ui-btn ui-corner-all collapseExpand">Expand All</a></div>
  <div class="ui-block-b"><a id="btnCollapse" class="ui-btn ui-corner-all collapseExpand">Collapse All</a></div>
</div>

expandCollapseButtons

Then I use the common class to create a single event handler. If  ‘Collapse All’ is clicked, I trigger the click event on all dividers with ui-icon-minus class assigned to a child; otherwise I trigger the click event on all dividers with the ui-icon-plus class assigned to a child:

$(document).on("click", ".collapseExpand", function () {
    var collapseAll = this.id == "btnCollapse";
    if (collapseAll) {
        $(".ui-li-divider .ui-icon-minus").click();
    } else {
        $(".ui-li-divider .ui-icon-plus").click();
    }
});

This technique can be enhanced by using the jQM page transitions to show and hide lisitems during the collapse/expand events. This is a way to add some ‘sizzle’ to your projects. I create a new function that takes the listitem jQuery object and a boolean indicating whether we are showing or hiding the item. If we are showing the item, I make the item visible and then add the animation classes, ‘flow’ and ‘in’, then I attach an event handler for the end of the animation where these classes are removed.  The event is attached with the one() method as it only needs to run once. If we are hiding the item, I add the animation classes, ‘flow’ and ‘out’, then I attach the end of animation handler where the classes are removed and item is hidden:

function ApplyTransition(li, IsCollapsed) {
    if (IsCollapsed) {
        li.show(0, function () {
            $(this).addClass('flow in').one('webkitAnimationEnd oanimationend msAnimationEnd mozAnimationEnd animationend', function (e) {
                $(this).removeClass('flow in');
            });
        });
    } else {
        li.addClass('flow out').one('webkitAnimationEnd oanimationend msAnimationEnd mozAnimationEnd animationend', function (e) {
            $(this).hide();
            $(this).removeClass('flow out');
        });
    }
}

To use this animation, I change the divider click handler from above to use this function instead of the slideToggle:

$(document).on("click", '.ui-li-divider', function (e) {
    var IsCollapsed = false;
    var TheDivider = $(this);
    var li = TheDivider.next(':not(.ui-li-divider)');
    while (li.length > 0) {
        IsCollapsed = li.css('display') == 'none';
        ApplyTransition(li, IsCollapsed);
        li = li.next(':not(.ui-li-divider)');
    }
    var $icon = TheDivider.find('.inlineIcon');
    if (!IsCollapsed) {
        $icon.removeClass('ui-icon-minus').addClass('ui-icon-plus');
    } else {
        $icon.removeClass('ui-icon-plus').addClass('ui-icon-minus');
    }
    e.stopPropagation();
    return false;
});

 

I have created a jsFiddle demonstrating the collapsible listview dividers. The fiddle includeds a list with autodividers and one with manually created dividers. It also provides a checkbox to switch between the default slideToggle() animation and the jQM flow transition animation:

jQM 1.4 Collapsible Listview Dividers

Buy Me A Coffee :) @ ko-fi.com

Advertisements

2 thoughts on “Make Listview Dividers Collapsible

  1. So, do you want a separate action for selecting vs expanding the “Major” item? What does selecting mean in your case, an immediate action, or just marking the item as selected. You can certainly put listviews inside collapsibles and some custom css/js could be used on the collapsible…

  2. Hi,

    Is it possible to have a collapse button in a list item?

    The idea is to present the possibility to select a “Major” option in the list, or “Refine” the list by selecting a list item in the collapsable list.

    Thanks.

Drop me a line or two ;)

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s