Telerik blogs

I was perusing the Kendo UI Forums the other day, as I do from time to time, and I noticed a very interesting post that went something like this...

I love to use the PanelBar for navigation. Is there a way for me to slide it off the screen when it isn't being used?

I'm paraphrasing the actual question which you can read for yourself. This is an interesting post to me because what the poster is actually asking for - I think - is an off-canvas strategy.

Off-Canvas

Off-canvas is a term that refers to a navigation strategy in which navigation content is literally moved off the screen (hence off canvas) and out of view. When it is needed, a button of some sort (usually the 3 horizontal lines UI cliche) is clicked and the main content is pushed off canvas, while the sidebar content is moved on. This was made popular by Facebook in their iOS application, and has since been adopted by the RWD community as an accepted strategy.

PanelBars As Navigation

Here is a Kendo UI PanelBar used as a navigation element. The links don't go anywhere, but you can see how you might use said PanelBar as a collapsible sidebar for navigation.

I am using Bootstrap as my grid system here. If you were to take a look at the two column definitions, you would see that I've specified them as such...

Bootstrap Columns

<!-- Textbook Bootstrap Navbar -->
<div class="navbar navbar-static-top navbar-default">
  <div class="container">
      <a href="" class="navbar-brand">Off Canvas Example</a>
    </div>
  </div>
</div>

<!-- Application Body -->
<div class="container">
  <div class="row">
    <!-- lefthand column - occupies 1/3 of the screen on tablets
         and larger.  Occupies 100% the screen on mobile -->
    <div class="col-xs-12 col-sm-4">
      <!-- Kendo UI PanelBar -->
      <ul id="panelbar"></ul>
      ...
    </div>
    <!-- righthand column - occupies 2/3 of the screen on tablets
         and larger.  Occupies 100% the screen on mobile -->
    <div class="col-xs-12 col-sm-8">
      <!-- content omitted for berevity -->
      <p>...</p>
    </div>
  </div>
</div>
<script>
  $(function() {
    // initialize the panelbar
    $("#panelbar").kendoPanelBar();
  });
</script>

Now on it's own, this doesn't really look all that bad when you get it down to phone size. The PanelBar gets stacked on top of the content. In fact, one RWD strategy is to move the PanelBar to the bottom on phones and provide a button at the top that says "Menu", which is just an anchor to the bottom. You can see this strategy at work on jqueryuivskendoui.com. It's perfectly fine to handle navigation that way.

However, we set out to implement an off canvas strategy here, and by god, that's what we're going to do.

Great Artists Steal

Steve Jobs used to say this a lot, and it's wonderfully poetic considering he stole that saying from Picaso. I like to believe it's true though since it assuages so much of my guilt.

Since off canvas is going to require some CSS trickery on our part, I stole the Bootstrap 3 off-canvas sample as a starting point. It's almost what we need, but it slides the content out and brings it back in at the same level as the body content. I want it to behave more like the Kendo UI Mobile Drawer where the entire left hand side becomes a home for the menu.

View Finished Demo

Resize the window until the screen is less than 768 pixels to see the off-canvas kick in.

First, we are going to switch to an off-canvas strategy whenever the browser drops below 768 pixels. Bootstrap identifies this as the "small" sized breakpoint. We're going to add classes to the container, and left/right columns inside of a CSS 3 media query.

Pull Sidebar Offscreen

@media screen and (max-width: 767px) {

  /* outer div */
  .row-offcanvas {
    position: relative;
  }

  /* sidebar */
  .sidebar-offcanvas {
    left: -80%;
    width: 80%;
    position: absolute;
    top: 0;
    background-color: #F7F7F7;
    padding: 0;
    height: 100%;
  }

  /* content */
  .row-offcanvas.active {
    left: 80%;
  }

}

Lets talk about what just happened there.

  • .row-offcanvas
    • position: relative; The outer <div> has a static position by default.  We change it to relative because we want to be able to move it over to the right when we summon the off-canvas content. Relative positioning allows you to move items on the page by specifying top/bottom/left/right positions. Static positioning would simply ignore these settings.
  • .sidebar-offcanvas The sidebar is the most complex part of this equation since it's the component that has to be moved offscreen and back on again.
    • width: 80% Specifies that we only want this element to ever occupy 80% of the screen.
    • left: -80% Specifies that we want to move this element to the left 80% of the width of the screen. These two settings in conjunction are what moves this element "off canvas"
    • position: absolute Removes the element from the flow of the document so that it can be moved all the way to the top with...
    • top: 0 .. whcih gives us maximum use of the already precious real estate on a mobile device.
    • background-color: #F7F7F7; Not really necessary, but provides nice visual segmentation between the sidebar and the app.
    • padding: 0; This overrides the Bootstrap padding on columns so that our PanelBar fills all the available space in the sidebar.
    • height: 100% Causes the sidebar to fill the entire height of the browser window furthering the illusion that the page has been pushed off the viewport
  • .row-offcanvas.active
    • left: 80%; Moves any element with this class to the right 80% of the screen width

Where do these classes go in the markup? Obviously the column containing the PanelBar gets the sidebar-offcanvas class. The row-offcanvas goes on the element which we want to move out of the way. That's really the entire page. To do that, I'm going to wrap all the HTML in a wrapper <div> and apply the class there.

row-offcanvas div

<div class="wrapper row-offcanvas">
  <div class="navbar navbar-static-top navbar-default">
    ...
  </div>
  <div class="container">
    <div class="row">
      <div class="col-xs-12 col-sm-4 sidebar-offcanvas">
        <div id="sidebar"></div>
      </div>
      <div class="col-xs-12 col-sm-8">
        <p>...</p>
      </div>
    </div>
  </div>
</div>

All there is left to do is toggle the active class on and off when the button is pressed. It's times like these that I just love jQuery.

Toggle Active Class

<script>
  $(function{
    // initialize the kendo ui panelbar
    $("#panelbar").kendoPanelBar();

    // handle the #toggle click event
    $("#toggle").on("click", function() {
      // apply/remove the active class to the row-offcanvas element
      $(".row-offcanvas").toggleClass("active");
    });
  });
</script>

At this point, everything is working! It still doesn't look quite though. The skinny menu doesn't really look right up against the header, and I'm pretty sure that the side bar still isn't 100% of the height of the page. Also, some browsers will get a horizontal scrollbar when the sidebar slides out which is killing the wonderful illusion we've worked so hard to create.

Fortunately, we can fix all of these things with some additional CSS tweaks.

Enlarge PanelBar Menu Headers

The skinny menu items can be changed by altering the k-header class. The k-header class is used in a lot of places in Kendo UI. To make sure that we aren't messing up other widgets, we'll scope the selector to the PanelBar only based on it's data-role property. The Bootstrap NavBar gets a min-width of 50px. We'll just apply that same setting to our PanelBar item headers. I'm only doing this inside the media query so that the widgets deminsions are default on desktops, and then made larger for smaller screens.

Increase PanelBar Menu Header Size

@media screen and (max-width: 768px) {

  ....

  [data-role='panelbar'] .k-header {
    min-height: 50px;
  }
}

Fix 100% Height Issue

The reason why the sidebar isn't 100% height of the page is because the html and body are not 100%, and it's parent container (wrapper) is not 100%. It cannot extend past the bounds of its containers. We can add a few simple CSS settings OUTSIDE the media query to set the page and wrapper to 100% which will cause the sidebar to extend all the way to the bottom of the page.

Set 100% Page Height

html, body, .wrapper {
  height: 100%;
}

Fix Horizontal Scrollbars

OSX doesn't show scrollbars anymore, but Windows sure does. It looks rather nasty too. We can actually fix this very easily by specifying that any content on the x axis (horizontal) that overflows it's parent container should be hidden. This tells the browser not to bother giving us a scrollbar.

Overflow Hidden

html {
  overflow-x: hidden;
}

Sliding Animation

Our sliding draw wouldn't be complete if it didn't - well - slide. We could animate it's sliding in with jQuery, but that's kind of a bad idea. jQuery provides cross-browser supported animation by actually incrementing the property to animate. This causes the browser to repaint the screen and can cause your app to be really janky - especially on mobile devices.

CSS 3 on the other hand is perfectly suited for this task. CSS transitions are hardware accelerated by most browsers. This means that the sidebar actually gets promoted to a layer and offloaded to the GPU which handles the animation giving us ONE browser paint and a sexy smooth animation.

Slide Animation With CSS 3

@media screen and (max-width: 767px) {

  /* outer div 
     animate changes to left positioning using CSS 3 transitions
  */
  .row-offcanvas {
    position: relative;
    -webkit-transition: left .3s ease-in;
    -most-transition: left .3s ease-in;
    transition: left .3s ease-in;
  }

  ....

This adds a nice animation with some neat easing that makes the sidebar appear to snap into place. This works great on Chrome, Opera and Safari. Both FireFox and IE completely ignore these transitions. I was baffled by this. After looking at it for quite a while, I enlisted the help of TJ VanToll.

TJ closes jQuery UI issues when he's not working here at Telerik as a Developer Advocate, so he's no stranger to running down wierd bugs like this. It didn't take him long to find the answer.

As it turns out, both IE and FireFox require you to specify a starting left position to animate from. Chrome, Safari and Opera figure this out for you based on how the page gets rendered. All it takes to make this animation start working everywhere is a left: 0 on the .row-offcanvas class in the media query. Don't you just love CSS?

Note that IE 9 and below don't support CSS 3 transitions so you won't get the animation. Also IE 8 and below don't support media queries, although you can add them in with respond.js.

Here is the complete CSS for the off-canvas demo.

CSS

/* hide the horizontal scrollbar */
html {
  overflow-x: hidden;
}

/* make all parent containers 100% height */
html, body, .wrapper {
  height: 100%;
}

/* move the toggle button to the left and give it
   some breathing room */
.navbar-toggle {
  float: left;
  margin-left: 10px;
}

@media screen and (max-width: 767px) {

  /* outer div */
  .row-offcanvas {
    position: relative;
    left: 0;
    -webkit-transition: left .3s ease-in;
    -most-transition: left .3s ease-in;
    transition: left .3s ease-in;
  }

  /* sidebar */
  .sidebar-offcanvas {
    left: -80%;
    width: 80%;
    position: absolute;
    top: 0;
    height: 100%;
    background-color: #F7F7F7;
    padding: 0px;
  }

  /* content */
  .row-offcanvas.active {
    left: 80%;
  }

  /* Make the menu headers the same size as the
     bootstrap navbar */
  [data-role='panelbar'] .k-header {
    min-height: 50px;
  }
}

View Completed Demo

Like Responsive Design?

I DO! I LOVE IT! Did I say that too loud?

Sidebars are easy to handle, but what about really complex widgets like a grid or a scheduler? What about data visualization? Responsive web design gets really tricky when you start talking about real world apps and serious enterprise solutions. We've spent the last 4 months hard at work researching what it takes to make all of our widgets fully responsive. You don't want to miss what we've come up with!


Burke Holland is the Director of Developer Relations at Telerik
About the Author

Burke Holland

Burke Holland is a web developer living in Nashville, TN and was the Director of Developer Relations at Progress. He enjoys working with and meeting developers who are building mobile apps with jQuery / HTML5 and loves to hack on social API's. Burke worked for Progress as a Developer Advocate focusing on Kendo UI.

Comments

Comments are disabled in preview mode.