Coding guide: Multi select

Important: This information is out of date; for up-to-date information on using these design patterns, as implemented in Firefox OS building blocks, go to our Firefox OS building blocks page.

Download resources

Here you can find examples of how to implement the multi-select mode (also known as "edit mode") on Firefox OS, as well as downloads for the CSS and image resources used by the built-in apps on Firefox OS. You can copy these resources into your app and make use of them to build apps that match these apps' appearances.

Implementing multi select

To implement multi-select mode using the style shown here, place the CSS and media files into your app and then include the CSS using <link> in your HTML <head> block:

<link href="resources/edit_mode.css" rel="stylesheet" type="text/css" media="all">

Make sure the media files are in the location expected by the CSS (either by placing them in a corresponding location or by revising the CSS).

Example

This very simple example shows how to create the multi-select frame around your content. You will need to actually implement the body of your interface as well, of course, to appear between the toolbar at the top of the screen and the button bar at the bottom.

Note: This example needs to actually have some items to select and deselect; otherwise, this UI isn't very useful a demonstration! Also need to sort things out into an edit mode example and a multi-select example.

HTML

Our example consists of an app interface with a list of items, above which is a header that offers some buttons to add and delete items from the list. Let's look at the HTML in sections.

First, we pull in the CSS for all the UX elements we're using, as well as for the fonts we use.

<head>
  <link href="resources/lists.css" rel="stylesheet" type="text/css" media="all">
  <link href="resources/edit_mode.css" rel="stylesheet" type="text/css" media="all">
  <link href="resources/headers.css" rel="stylesheet" type="text/css" media="all">
  <link href="resources/switches.css" rel="stylesheet" type="text/css" media="all">
  <link href="resources/fonts.css" rel="stylesheet" type="text/css" media="all">
</head>

Header and toolbar

The first thing the HTML does is create a wrapper for the UX content and the list panel, then offers a <section> containing the header, which contains a title (simply "Item List" for this example) and a toolbar with two buttons: a "compose" button, which will not do anything in our example, and an "edit" button, which will bring up our delete form.

<article id="main-wrapper" class="wrapper">
  <section id="item-list-panel" class="panel" role="region">
    <section role="region" class="item-list-header">
      <header class="view-header regular-header">
        <menu type="toolbar">
          <a href="#new" id="icon-compose">
            <span class="icon icon-compose"></span>
          </a>
          <a href="#delete" id="icon-edit">
            <span class="icon icon-edit">Delete</span>
          </a>
        </menu>
        <h1>Item List</h1>
      </header>
    </section>

Item list

The item list is a fairly standard Firefox OS style list building block. Each item has a checkbox on it that uses the "danger" class; this class causes the checkboxes to be the red style used for delete switches. The CSS for these checkboxes causes them to not be visible, however, until we enter edit mode. We'll see more about how this works below.

    <div id="item-list-container" class="item-list-scrollouter">
      <article data-type="list">
        <ul id="item-list">
          <li><p><label class="danger"><input type="checkbox"><span></span></label>Item One</p></li>
          <li><p><label class="danger"><input type="checkbox"><span></span></label>Item Two</p></li>
          <li><p><label class="danger"><input type="checkbox"><span></span></label>Item Three</p></li>
          <li><p><label class="danger"><input type="checkbox"><span></span></label>Item Four</p></li>
          <li><p><label class="danger"><input type="checkbox"><span></span></label>Item Five</p></li>
          <li><p><label class="danger"><input type="checkbox"><span></span></label>Item Six</p></li>
          <li><p><label class="danger"><input type="checkbox"><span></span></label>Item Seven</p></li>
          <li><p><label class="danger"><input type="checkbox"><span></span></label>Item Eight</p></li>
          <li><p><label class="danger"><input type="checkbox"><span></span></label>Item Nine</p></li>
          <li><p><label class="danger"><input type="checkbox"><span></span></label>Item Ten</p></li>
        </ul>
      </article>

Delete dialog

The HTML below creates the <form> for the app's delete dialog. It consists of a header containing a close button (which simply, as discussed in the Characteristics of edit mode, is a bit of a misnomer) and a "Done" button. The header also includes a title indicating what type of action the user is about to perform; in this case, deleting items from a list.

Below the list is a button bar with the buttons that perform actions on the selected items.

      <form id="item-delete-dialog" role="dialog" data-type="edit">
        <section>
          <header>
            <button formaction="#deleteCancel"><span class="icon icon-close">close</span></button>
            <menu type="toolbar">
              <button formaction="#deleteDone">done</button>
            </menu>
            <h1>Delete items</h1>
          </header>
        </section>
        <menu>
          <button formaction="#deleteAll">Delete all</button>
          <button formaction="#deleteSelected">Delete selected</button>
        </menu>
      </form>
    </div>
  </section>            
</article>

CSS

In addition to all the CSS we've imported from the standard UX, we add some styles to customize the appearance and behavior of our interface.

Background

This first bit of CSS sets the background to be white, and indicates that the main interface shouldn't scroll vertically.

body, html {
  background: white;
  overflow-y: hidden;
}

Delete dialog

Next is the CSS for the delete dialog; this is our multi-select interface for choosing items to delete. Initially, the edit form should be invisible, since we don't start out in edit mode.

#item-delete-dialog {
  display:none;
}

Item list

The item list needs CSS for two modes—its regular display mode and for when it's presented in edit mode. The edit mode versions, indicated by the addition of "-edit" to their names, adjust the height of the list based on whether or not the edit mode bottom toolbar is visible, and toggle the visibility of the checkboxes that are used to select items to be deleted. They also indicate that the list should be scrollable vertically but not horizontally.

.item-list-scrollouter {
  height: calc(100% - 5rem);
  overflow-y: scroll;
  overflow-x: hidden;
}
.item-list-scrollouter label {
  display: none;      // Hide checkboxes unless in edit mode
}
.item-list-scrollouter-edit {
  height: calc(100% - 11.5rem);
  overflow-y: scroll;
  overflow-x: hidden;
}
.item-list-scrollouter-edit label {
  display: inline-block;
}

Wrapper

The content is wrapped in an <article> and <section> which have the following CSS applied to indicate that the main user interface occupies the full height of the screen.

#main-wrapper, #item-list-panel {
  height: 100%;
}

JavaScript content

Then we need some JavaScript code to handle our mode switches as well as to handle the buttons to delete items from the list.

Showing and hiding edit mode

The "delete dialog" is an edit mode interface applied to our list view. The two methods below toggle edit mode on and off by switching the delete dialog form's display mode between none (not in edit mode) and block (in edit mode).

function showDeleteDialog() {
  document.getElementById("item-delete-dialog").style.display = "block";
  document.getElementById("item-list-container").className = "item-list-scrollouter-edit";
}
function hideDeleteDialog() {
  document.getElementById("item-delete-dialog").style.display = "none";
  document.getElementById("item-list-container").className = "item-list-scrollouter";
  location.hash = "";
}

Removing an element from the DOM

Next is the removeItem() method, which is a utility method that simply removes the specified element from the DOM. The deleteSelected() method will make use of it. It works by simply looking at the element's parentNode and calling removeChild() on that node.

function removeItem(el) {
  if (el.parentNode) {
    el.parentNode.removeChild(el);
  }
}

Deleting the selected items

The deleteSelected() method, shown here, uses element.querySelectorAll() to get a NodeList containing all the checkbox elements in the item list. It then scans that list, looking for elements that are checked. For each checked item, we walk up through the DOM tree to find the nearest surrounding <li> element and remove it from the DOM. This deletes the entire list item from the DOM.

function deleteSelected() {
  var itemList = document.getElementById("item-list").querySelectorAll('input[type="checkbox"]');
  var numItems = itemList.length;
 
  for (var i=0; i<numItems; i++) {
    var item = itemList[i];
    
    // If the item is checked, scan up the DOM tree to find the
    // parent <li> element and remove it.
    
    if (item.checked) {
      while (item.nodeName.toLowerCase() != "li") {
        item = item.parentNode;
      }
      removeItem(item);
    }
  }
}

Deleting all items

Deleting all of the items in the list is really easy. We just call set element.innerHTML to the empty string. This immediately removes every <li> from the list, emptying it.

function deleteAll() {
  document.getElementById("item-list").innerHTML = "";    // BOOM!
}

Event handling

Each button clicked by the user asks the Gecko runtime to load an anchor; for example, clicking the cancel button when in edit mode loads "#deleteCancel". To handle these button clicks, we use the hashchange event. When this event is called, we look at location.hash to see which button was clicked, and handle it appropriately.

function handleHashChange() {
  switch(location.hash) {
    case "#add":
      break;
    case "#delete":
      showDeleteDialog();
      break;
    case "#deleteCancel":
      hideDeleteDialog();
      break;
    case "#deleteSelected":
      deleteSelected();
      hideDeleteDialog();
      break;
    case "#deleteAll":
      deleteAll();
      hideDeleteDialog();
      break;
  }
}
window.onhashchange = handleHashChange;

Working demo

You can try out edit mode in this live demonstration. Click the "edit" button at the right end of the header bar to enter edit mode.


Firefox OS live demos generally require a Gecko-based browser, and work best in recent builds of Firefox.

Document Tags and Contributors

 Contributors to this page: chrisdavidmills, Sheppy
 Last updated by: chrisdavidmills,