Boczne menu

Dzisiaj zajmiemy się stworzeniem bocznego menu, potocznie zwanego „off canvas”.
Mniej więcej takie właśnie menu możesz zobaczyć na tej stronie po kliknięciu w ikonke menu w prawym górnym rogu.
Nie ma co przedłużać, zapraszam do lektury.

Aby stworzyć takie menu skorzystamy z 3 wariantów:

  • wariant najprostszy – menu jak na tej stronie. Takie menu ma tylko jeden wygląd, niezależnie od szerokości ekranu
  • wariant drugi – menu które w zależności od rozdzielczości zmienia się z tradycyjnego na „off canvas”

Tworzymy menu off canvas

W zasadzie jest to bardzo proste zadanie. Wystarczy stworzyć jakiś kontener, który domyślnie jest wysunięty poza ekran, oraz ikonę menu, która pokaże nasze menu. Po kliknięciu na taką ikonę dodamy stosowne klasy do elementów, które sprawia że menu wjedzie na ekran.

Zaczynamy od html:

<div class="wrap-all">	
	<span class="navigation-toggle">
		<span></span>
		<span></span>
		<span></span>	
	</span>

	<nav class="navigation" role="navigation">
		<h1 href="" class="logo">
			<img src="logo.png" alt="Company Name" />
		</h1>
		<ul>
			<li><a href="">Start</a></li>
			<li><a href="">O nas</a></li>
			<li><a href="">Oferta</a></li>
			<li><a href="">Cennik</a></li>
			<li><a href="">Galeria</a></li>		
			<li><a href="">Kontakt</a></li>		
		</ul>
	</nav>

	<div role="main" class="page-main">
		Lorem ipsum sit dolor...
	</div>
</div>

Prosta sprawa. Mamy .navigation-toggle który pokazuje i ukrywa element z navigacją. Mamy też .navigation – nawigację. Całość okrywamy elementem .wrap-all, który potzrebny jest, by podczas przesuwania menu nie pojawiały się boczne paski przewijania. Do całości dodajmy proste stylowanie:

@import url(http://fonts.googleapis.com/css?family=Source+Sans+Pro:400,300,700&subset=latin,latin-ext);
	html,body {
		font-family:'Source Sans Pro', sans-serif;
		height:100%;
		margin:0;
		padding:0;
	}
	.wrap-all {
		overflow: hidden;
		width:100%;
		position: relative;
	}
	.page-main {
		padding:20px;
		position: relative;
		color:#666;
		font-size:16px;
		line-height: 1.4em;	
	}

/* navigation toggle */
	.navigation-toggle {
		position:fixed;
		top:10px;
		right:10px;
		width:40px;
		height:40px;
		background: #222;
		border-radius:2px;
		display: block;
		z-index: 100;
		cursor: pointer;
	}
	.navigation-toggle span {
		width:20px;
		height:3px;
		margin:4px 10px;
		display: block;
		background: #fff;
		border-radius:1px;
	}
	.navigation-toggle span:first-child {
		margin-top:12px;
	}

/* navigation */
	.navigation {
		z-index: 2;
		min-height: 100%;
		font-family:'Source Sans Serif', sans-serif;
		font-weight:300;
		font-size:14px;
		position: fixed;
		top:0;
		left:-250px;
		width:250px;
		background: #1A2124;
		height:100%;
		overflow: hidden;
		overflow-y:auto;
		color:#42505A;
		-webkit-transition: 0.5s left;
		-moz-transition: 0.5s left;
		-ms-transition: 0.5s left;
		-o-transition: 0.5s left;
		transition: 0.5s left;		
	}	

	.navigation a {
		color:#42505A;
	}
	.navigation ul {
		list-style:none;
		padding:0;
		margin:20px 0;
	}
	.navigation ul li {
		text-transform: uppercase;
		border-bottom:1px solid #090B0D;
		font-weight:600;		
	}	
	.navigation ul li:last-child {
		border-bottom:0;
	}
	.navigation ul li a {
		display: block;
		height:50px;
		line-height: 50px;
		text-decoration: none;
		padding:0 20px 0 30px;
		-webkit-transition: 0.3s;
		-moz-transition: 0.3s;
		-ms-transition: 0.3s;
		-o-transition: 0.3s;
		transition: 0.3s;
		
	}
	.navigation ul li a:hover {
		background:#3BB4BC;
		color:#fff;
		border-left:6px solid #329EA5;
	}
	.navigation .logo {
		text-align: center;
		margin:20px 0;
	}
	.navigation .logo img {		
		max-width:120px;
		height:auto;		
	}

Element .navigation-toggle zawsze będzie w prawym górnym rogu ekranu dzięki pozycjonowaniu fixed i pozycji [top, right] ustawionej na 10px.
Navigacja także ma pozycjonowanie fixed. Ustawiliśmy ją po lewej stronie (left), chociaż to już kwestia doboru. Domyślnie nawigacja jest wysunięta -320px w lewą stronę poza ekran.

Jeżeli zmniejszysz ekran do naprawdę małych rozmiarów, zauważysz, że po wysunięciu menu gdy wszystkie jego elementy się nie mieszczą na ekranie, nasze menu dostanie swój własny pasek przewijania (overflow:auto). Kwestią wyboru jest czy tak chcemy czy nie. Jeżeli nie chcemy tego paska, wtedy musimy wprowadzić następujące zmiany:

  • Ustawiamy layout na minimum 100% wysokości czyli [html, body] dostają height:100%,
  • Element .navigation powinien mieć pozycjonowanie absolute, oraz wysokość 100%
  • Element .navigation-toggle powinien byś na sztywno ustawiony na górze strony

Wystarczy teraz po kliknięciu na element .navigation-toggle dodać do body klasę, która wsunie navigację na ekran:

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script>
	$(function() {
		$('.navigation-toggle').on('click', function(e) {
			$('body').toggleClass('navigation-show');
		});
	});
</script>

Oraz proste style:

.navigation-show .page-content {		
	left:250px;
}
.navigation-show .navigation {
	box-shadow:0 2px 10px rgba(0,0,0,0.2);
	left:0;
}

Koniec. W wielu przypadkach tak proste podejście w zupełności wystarczy (chociaż na tej stronie). Jeżeli chcemy by wraz z menu przesuwała się także treść strony, wystarczy tak samo przesunąć ją w lewo. Dodajemy style:

.page-main {	
	...
	left:0;				
	-webkit-transition: 0.5s left;
	-moz-transition: 0.5s left;
	-ms-transition: 0.5s left;
	-o-transition: 0.5s left;
	transition: 0.5s left;	
}

/* toggle anim */	
	.navigation-show .page-main {		
		left:250px;
	}
	.navigation-show .navigation {
		box-shadow:0 2px 10px rgba(0,0,0,0.2);
		left:0;
	}

Demo 1

Widok dla większych ekranów

Osobiście bardzo lubię tego typu nawigacje. Wiem też, że sporo ludzi zabiło by mnie gdybym im na stronie zrobił podobne menu. Właśnie dla nich w równie łatwy sposób co powyżej możemy dodać widok bardziej tradycyjny, w którym menu nie jest schowane poza ekranem.

Włączenie nawigacji dla tego widoku nie jest nam potrzebne. Wszystkie przesunięcia nawigacji i treści strony zerujemy, oraz lekko zmieniamy wygląd elementów menu:

@media screen and (min-width:45em) {
	.navigation-toggle {
		display: none;
	}
	.navigation-show .page-content {
		left:0;				 
	}
	.navigation-show .navigation,
	.navigation {		
		position: relative; 
		left:0; 
		width:100%;
		height:auto;
		text-align: center;
	}
	.navigation ul li {
		display: inline-block; 
		vertical-align: middle; 
		border:0;
		margin:0 2px;
	}
	.navigation ul li a {
		padding:0 30px;
	}		
}

Demo 2

Poprawka dla pedantów

Nasze menu jest gotowe. Maniacy detali mogą się przyczepić małego szczegółu. Gdy zmieniamy widok, menu płynnie przeskakuje z jednego wyglądu na drugi. Gdy taki widok zmieniany jest z dużego na mały, menu płynnie ucieka za ekran. Jest to spowodowane tym, że nasze menu w widoku dla dużych ekranów jest widoczne na ekranie, natomiast domyślnie dla ekranów mobilnych jest wysunięte poza ekran (schowane).
Aby to naprawić, płynne przejścia (transition) powinniśmy włączać tylko na czas wjeżdżania/wyjeżdżania z ekranu menu. Po wysunięciu się/schowaniu menu wszystkie transition() powinny być wyłączane.

Pierwszą rzeczą jaką zrobimy, to usunięcie wszelkich transition dla elementów .navigation i .page-content. Przenosimy je do oddzielnej klasy .anim, którą będziemy im aplikować.

.page-content {
	/* tutaj usuneliśmy transition */
}
.navigation {
	/* tutaj usuneliśmy transition */
}
.anim {
	-webkit-transition: 500ms;
	-moz-transition: 500ms;
	-ms-transition: 500ms;
	-o-transition: 500ms;
	transition: 500ms;
}

Kolejnym krokiem jest przerobienie zdarzenia click(), które podpięliśmy pod .navigation-toggle.

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script>
$(function() {
	$('.navigation-toggle').on('click', function() {
		$('body').toggleClass('navigation-show');
		$('.page-content, .navigation').addClass('anim').each(function() {
			//...
			//dla każdego elementu któremu przypisaliśmy klasę .anim
			//musimy wykryć kiedy przejście transition się zakończy
			//i wtedy usunąć tą klasę
			//...
		});
	});
});
</script>

Aby wykryć zakończenie takiego przejścia, możemy skorzytać z gotowca – np. korzystając z niezastąpionej strony http://stackoverflow.com/questions/5023514/how-do-i-normalize-css3-transition-functions-across-browsers. Pierwsza odpowiedź bardzo nam pasuje, wystarczy ją przerobić na wersję jQuery (w sumie cały skrypt spokojnie można by napisać w czystym JS).

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script>
function whichTransitionEvent(){
    var t;
    var el = document.createElement('fakeelement');
    var transitions = {
      'transition':'transitionend',
      'OTransition':'oTransitionEnd',
      'MozTransition':'transitionend',
      'WebkitTransition':'webkitTransitionEnd'
    }

    for(t in transitions){
        if( el.style[t] !== undefined ){
            return transitions[t];
        }
    }
}
	
$(function() {
	$('.navigation-toggle').on('click', function() {
		$('body').toggleClass('navigation-show');
		$('.page-content, .navigation').addClass('anim').each(function() {
			var transitionEnd = whichTransitionEvent();
			$(this).on(transitionEnd, function() {
				$(this).removeClass('anim')
			});
		});
	});
});
</script>

Demo 3

Komentarze

  • AlikStudio

    Trochę nie rozumiem sensu takiego menu.

    • AlikStudio

      No, może urządzenia mobilne.

      • Ale nie tylko urządzenia mobilne. Patrz np ta strona. Domyślnie ukryłem menu, by nie odciągało uwagi od moich super wypasionych najlepszych na świecie tekstów… Ostatnimi czasy to całkiem często spotykana metoda – szczególnie na blogach gdzie autorom zależy na czytelności treści a nie na wodotryskach bocznych belek.

      • papke

        Kartofelek007 ma absolutna racje. Jest to ciekawy trend w tworzeniu stron internetowych. Dzieki takiemu podejsciu do menu pozostaje na prawde duzy obszar na przekazanie tresci, co mnie (jako uzytkownika ceniacego estetyke) bardzo cieszy).

        Swietny tutorial – cenie sobie prostote wytlumaczenia oraz liczne „dema” obrazujace uzysany efekt. Jest to niezbedne dla osob, ktore sie ucza. Gratulacje!