さぁ!検索しよう!

スマートフォンサイト等で実装されているドロワーメニューですが、今回はレスポンシブに対応したものです。

DEMO

PCの表示では、各アイテムがヘッダー内に配置されます。

イメージ33659
イメージ33660

スマホやタブレットの表示では、各アイテムがページ全体の裏側に配置され、バーガーアイコンを押すことでページ全体が右へスライドされることで各アイテムが姿を現します。

イメージ33658

HTML

<body>
<div id="spwrap"></div>
	<div id="bodyinner">
		<header class="clearfix">
			<div class="wrapper-top">
				<div class="inner clearfix" id="innertop">
					<nav id="social">
						<ul>
							<li><a class="tw-li" href="https://twitter.com/mm_manual"><i class="fa fa-twitter"></i></a></li>
							<li><a class="fb-li" href="https://www.facebook.com/marumarumanual"><i class="fa fa-facebook"></i></a></li>
							<li><a class="g-li" href="#"><i class="fa fa-google-plus"></i></a></li>
							<li><a class="rss-li" href="http://marumarumanual.net/feed/"><i class="fa fa-rss"></i></a></li>
						</ul>
					</nav>
					<div id="search">
						<form name="searchform" id="searchform" method="get" action="#">
							<input id="keyword" type="text" />  
							<input type="submit" value="&#xf002;" id="submit" />
						</form>
					</div>
				</div>
			</div> 
			<div class="wrapper-center">
				<div class="inner clearfix">
					<a id="btn" href="#"><i class="fa fa-bars fa-lg"></i></a>
					<div class="logo"><a href="#">LOGO</a></div>    
					<div class="adsense">Adsense</div> 
				</div>
			</div>
			<div class="wrapper-bottom">
				<div class="inner" id="innerbottom">
					<nav id="nav" class="clearfix">
						<ul>
							<li>Home</li>
							<li>About</li>
							<li>Contact</li>
							<li>Category</li>
							<li>Link</li>
						</ul>
					</nav>
				</div>
			</div>
		</header>
		<div class="container clearfix"></div>
	</div>
</body>

ポイント

  • 各アイテムであるソーシャルボタン、メニュー、検索フォームは、<header></header>内に定義しています。
  • <div id="spwrap"></div>は、ブラウザウィンドウの幅が768pxより小さくなったときの、各アイテムの移動先となります。

CSS

*,
*:before,
*:after {
	box-sizing: border-box;
}

.clearfix:after {
	content: "";
	clear: both;
	display: block;
}

i {
	color: #fff;
}

html,
body,
#bodyinner {
	overflow-x:hidden;
}

body {
	background:#dfdfdf;
	color:#333;
	margin:0;
}

#bodyinner {
	background:#ccc;
	height:100%;
	position:relative;
	-webkit-transition:-webkit-transform 400ms ease;
	transition:transform 400ms ease;
	width:100%;
	z-index:2;
}

#bodyinner.visible {
	-webkit-transform: translate3d(80%,0,0);
 	transform: translate3d(80%,0,0);
 }
 @media screen and (min-width: 768px) {
 	#bodyinner.visible {
	 	-webkit-transform: translate3d(0,0,0);
		 transform: translate3d(0,0,0);
	}
}

header { 
	background:#fff;
	box-shadow:rgba(0, 0, 0, .15) 0 1px 2px;
	padding:5px;
	width:100%;
}
@media screen and (min-width: 768px) {
	header {
		background:none;
		box-shadow:none;
	  	padding:0;
	}
}

#social {
	border-bottom:1px solid #777;
	padding:5px;
}
@media screen and (min-width: 768px) {
	#social {
		border:none;
		float: left;
		padding:0;
	}
}

#social ul {
	list-style: none;
	margin: 0;
	padding: 0;
	text-align:center;
}

#social li {
	display: inline-block;
	padding-right: 5px;
}

#social li a {
	background: #FBA848;
	border-radius:5px;
	display: block;
	height: 32px;
	line-height: 32px;
	text-align: center;
	width: 32px;
}

#search {
	padding:5px;
	text-align:center;
}
@media screen and (min-width: 768px) {
	#search {
		float: right;
		padding:0;
		text-align:right;
		width:300px;
	}
}

	#keyword,
	#submit {
		border:none;
		border-radius:5px;
		padding:10px;
	}

	#keyword {
	  	background:#ccc;
	}

	#submit {
		background:#FBA848;
		color:#fff;
		font-family:FontAwesome;
	}

#btn {
	border:1px solid #eee;
	border-radius:5px;
	display: block;
	float:left;
	height: 44px;
	line-height: 44px;
	text-align: center;
	width: 44px;
}
@media screen and (min-width: 768px) {
	#btn {
		display: none;
	}
}

#btn i {
  	color: #FBA848;
}


.logo {
	font-size:0;
	text-align:center;
}
@media screen and (min-width: 768px) {
	.logo {
		float: left;
		margin:0;
	}
}

.logo a {
	background:#333;
	border-radius:5px;
	color:#fff;
	display:inline-block;
	font-size:16px;
	height: 44px;
	line-height:44px;
	text-decoration:none;
	width:100px;
}

#spwrap {
	background:#555;
	position:fixed;
	top:0;
	left:0;
	visibility:hidden;
  	-webkit-transition: visibility 0s 0.3s;
	transition: visibility 0s 0.3s;
	width:80%;
	z-index:1;
}

#spwrap.visible {
	visibility:visible;
	-webkit-transition: visibility 0s 0s;
	transition: visibility 0s 0s;
}
@media screen and (min-width: 768px) {
	#spwrap.visible {
		visibility:hidden;
	}
}

#nav ul{
	list-style: none;
	margin: 0;
	padding: 0;
}

#nav li {
	border-top: 1px dotted rgba(0, 0, 0, 0.2);
	border-bottom: 1px dotted rgba(255, 255, 255, 0.15);
	color:#fff;
	padding:5px;
	text-align: center;
}
@media screen and (min-width: 768px) {
	#nav li {
		color:#333;
		border: none;
		float: left;
		padding:10px 20px;
	}

	#nav li:nth-child(odd){
		background:#FBA848;
		color:#fff;
	}
}

.adsense {
	border-radius:5px;
	color:#fff;
	display: none;
	line-height:44px;
	text-align:center;
}
@media screen and (min-width: 768px) {
	.adsense {
		background:#333;
		display: block;
		float: right;
		height:44px;
		width: 70%;
	}
}

.container {
	background: #fff;
	border-radius:5px;
	height:1000px;
	margin-top:10px;
}
@media screen and (min-width: 768px) {
	.container {
		margin:0;
	}
}

@media screen and (min-width: 768px) {
	.wrapper-top,
	.wrapper-center,
	.wrapper-bottom {
		margin-bottom: 10px;
	}

	.wrapper-top {
		padding:10px;
	}

	.wrapper-top,
	.wrapper-bottom {
		background:#fff;
		box-shadow:rgba(0, 0, 0, 0.15) 0 1px 2px;
		width:100%;
	}
}

@media screen and (min-width: 1024px) {
	.container,
	.inner {
		margin-left:auto;
		margin-right:auto;
		width:1000px;
	}
}

ポイント

#bodyinnerがスライドインしたときに、横スクロールが出現しないようにしています。

html,
body,
#bodyinner {
	overflow-x:hidden;
}

#bodyinnerに、jQueryで.visibleを追加・削除することで、スライドイン・アウトします。
そして、ブラウザウィンドウの幅が768pxより大きくなると、強制的にスライドアウトさせます。

#bodyinner.visible {
	-webkit-transform: translate3d(80%,0,0);
 	transform: translate3d(80%,0,0);
 }
 @media screen and (min-width: 768px) {
 	#bodyinner.visible {
	 	-webkit-transform: translate3d(0,0,0);
		 transform: translate3d(0,0,0);
	}
}

translate3dの第一引数に80%を指定した訳は、スライドインと同様にボタンでスライドアウトさせるため、そのボタンの幅を残すためです。

img2

また、transform: translate3d();はGPUで処理されるため、jQueryのanimateよりもスマートフォンで押した際に、チラつくことなくヌルヌルとスライドされます。

スマホでアニメーションするときはcss3の「translate3d」を使おう

#spwrap {
	background:#555;
	position:fixed;
	top:0;
	left:0;
	visibility:hidden;
  	-webkit-transition: visibility 0s 0.3s;
	transition: visibility 0s 0.3s;
	width:80%;
	z-index:1;
}

#spwrapに、position:fixed;z-index:1;を指定することで、#bodyinnerよりも下の階層に固定配置しています。

img1

また、visibility:hidden;#bodyinnerがスライドインされるまでは非表示にしています。

CSS – display_none と visibility_hidden の違い – Qiita

#spwrapに、.visibleをjQueryで追加・削除することで、表示・非表示させます。更に、ブラウザウィンドウの幅が768pxより大きくなると、強制的に非表示にします。

#spwrap.visible {
	visibility:visible;
	-webkit-transition: visibility 0s 0s;
	transition: visibility 0s 0s;
}
@media screen and (min-width: 768px) {
	#spwrap.visible {
		visibility:hidden;
	}
}

jQuery

$(document).ready(function() {
	var $body = $('#bodyinner'),
	$nav = $('#nav'),
	$search = $('#search'),
	$social = $('#social'),
	$spWrap = $('#spwrap');

	function moveItem() {
		if(window.matchMedia('(min-width:768px)').matches){
			//PC
			$nav.detach();
			$nav.appendTo($('#innerbottom'));
			$social.detach();
			$social.prependTo($('#innertop'));
			$search.detach();
			$search.prependTo($('#innertop'));
		}else{
			//SP
			$nav.detach();
			$nav.prependTo($spWrap);
			$social.detach();
			$social.prependTo($spWrap);
			$search.detach();
			$search.appendTo($spWrap);
		}
	}

	moveItem();

	var timer = false;
	$(window).resize(function() {
		if (timer !== false) {
			clearTimeout(timer);
		}
		timer = setTimeout(moveItem, 300);
	});
	
	$('#btn').click(function(event) {
		event.preventDefault();
		$body.toggleClass('visible');
		$spWrap.toggleClass('visible');
	});
});

ポイント

moveItem()は、ブラウザウィンドウの幅の変化に応じて、各アイテムの配置を切り替える関数です。

function moveItem() {
		if(window.matchMedia('(min-width:768px)').matches){
			//PC
			$nav.detach();
			$nav.appendTo($('#innerbottom'));
			$social.detach();
			$social.prependTo($('#innertop'));
			$search.detach();
			$search.prependTo($('#innertop'));
		}else{
			//SP
			$nav.detach();
			$nav.prependTo($spWrap);
			$social.detach();
			$social.prependTo($spWrap);
			$search.detach();
			$search.appendTo($spWrap);
		}
	}

処理の流れは下記の通りです。

  1. ブラウザウィンドウの横幅が768pxより大きくなったら、#spwrapからメニューは#innerbottom内の一番下へ移動し、ソーシャルボタンと検索フォームは#innertop内の一番上へ移動します。

    	if(window.matchMedia('(min-width:768px)').matches){
    			//PC
    			$nav.detach();
    			$nav.appendTo($('#innerbottom'));
    			$social.detach();
    			$social.prependTo($('#innertop'));
    			$search.detach();
    			$search.prependTo($('#innertop'));
    		}
    	
  2. ブラウザウィンドウの横幅が768pxより小さくなったら、メニュー、ソーシャルボタン、検索フォームが#spwrap内に移動します。

    	else{
    			//SP
    			$nav.detach();
    			$nav.prependTo($spWrap);
    			$social.detach();
    			$social.prependTo($spWrap);
    			$search.detach();
    			$search.appendTo($spWrap);
    		}
    	

window.matchMedia('(min-width:768px)').matchesは通常、IE9以下を始め、Opera Mini,Android Browser 3以下では対応していませんが、matchMedia.jsを読み込むことで解決するそうです。

[JS] window.matchMedia をつかって JavaScript のコードをレスポンシブに分ける方法 | memocarilog

resizeは、ブラウザウィンドウの幅が変化している間に連続して実行されるメソッドなので、そのまま書くとブラウザに負荷がかかってしまいます。

そこで、下記のように記述するとリサイズが終わったときにのみ実行されます。

$('#btn').click(function(event) {
		event.preventDefault();
		$body.toggleClass('visible');
		$spWrap.toggleClass('visible');
	});

[jQuery] ウインドウのリサイズ操作が終わった時にだけ処理を実行する | CreativeStyle

#btnをクリックすると、toggleClass()によって#bodyinner#spwrapにvisibleクラスが追加され、#bodyinnerはスライドインされて#spwrapが表示されます。
この状態で、再び#btnをクリックすると、#bodyinner#spwrapからvisibleクラスが削除され、#bodyinnerはスライドアウトされて#spwrapが非表示となります。

※functionの引数にeventを渡し、続けてevent.preventDefault();と書くことで、ボタンを押したときのリンク動作を無効にしています。

以上で、ヌルヌルとスライドするレスポンシブなドロワーメニューの作り方の解説を終わります。