Bootstrap3でレスポンシブなレイアウトを組むときの要点覚え書き

Bootstrapグリッドシステムを採用しているので、スクリーンサイズに合わせてレイアウトが可変するレスポンシブデザインが簡単に作れるのですが、実際にどんな仕組みになっているか理解するのが割と手間だったので、忘れないように要点をまとめてみました。

サンプル:要素が4つあり、スマフォ表示で横2列、パソコン表示で横4列にレイアウトしたい場合。

<div>コンテンツ</div>
<div>コンテンツ</div>
<div>コンテンツ</div>
<div>コンテンツ</div>

要素に可変レイアウト用のclass「.col-**-**」を与える

横並びにしたい要素に、「.col-**-**」というclassを与えるだけなのですが、横に並べたい要素の数とレスポンシブの条件(ブレイクポイント)によって、**の部分に入る文字や数字が変わります。

上記のサンプルのように、4つdiv要素をスマフォで表示するときは横2列、パソコンでは横4列にしたい場合、.col-xs-6と.col-md-3というclassを4つのdiv要素それぞれに与えます。

<div class="col-xs-6 col-md-3">コンテンツ</div>
<div class="col-xs-6 col-md-3">コンテンツ</div>
<div class="col-xs-6 col-md-3">コンテンツ</div>
<div class="col-xs-6 col-md-3">コンテンツ</div>

.colの合計を12にする

.col-xs-6や.col-md-3といったclassが出てきたので、まず、これの意味をご説明します。

.col-xs-6 を「col」、「xs」、「6」の3つに分解してみてみます。

「col」はカラム(コラム)。単純に列という意味ですね。
「xs」はBootstrapのブレイクポイントのひとつ、768px以下を示しています。(ブレイクポイントについては後ほど詳しく解説します。)
「6」は、「6列分の幅」となります。

Bootstrapはデフォルトでは1行の中で、1~12個の要素を並べる(グリットを作る)ことができますが、そのルールを適用するためには「.col-**-**」に要素の数やブレイクポイントを組み込む必要があります。

要素の数が1個であれば12(12×1=12)、2個であれば6(2×6=12)、3個であれば4(3×4=12)というように、要素数で掛け算したときに、答えが12となる数値横並びになる要素の数で、それをclassに割り当てる必要があるのですね。

なので、ウィンドウサイズが768px以下の時、「6列分の幅」を与えて要素が2個並ぶ状態にするには、.col-xs-6を付与します。「6列分の幅」を持つ要素がふたつで12列分になります。余った残りふたつの要素は、改行されてやはり2個並びます。

ウィンドウサイズが992px以上~1200px未満の時、3列分の幅を与えて4個並べるには.col-md-3を付与します。これも合計で12になりますね。

.containerと.rowの中に入れる

説明が前後してしまいましたが、上記の設定は.rowというclass名を与えた要素の中に入れ子にすることで機能するようになります。

また、.containerというclassを持った要素でさらに入れ子にすると、ブレイクポイントに対応して幅のサイズが切り替わる文字通りコンテナとなる要素が作れます。

<div class="container">
    <div class="row">
        <div class="col-xs-6 col-md-3">コンテンツ</div>
        <div class="col-xs-6 col-md-3">コンテンツ</div>
        <div class="col-xs-6 col-md-3">コンテンツ</div>
        <div class="col-xs-6 col-md-3">コンテンツ</div>
    </div>
</div>

ブレイクポイントが3つある

768px、992px、1200pxの3つのブレイクポイントが設定されていて、4種類のレイアウトが可能です。詳しくは下記表を御覧ください。

画面サイズ 768px未満 768px以上~
992px未満
992px以上~
1200px未満
1200px以上
class名の接頭辞 col-xs- col-sm- col-md- col-lg-
containerの幅 設定なし(自動) 750px 970px 1170px

xsはスマフォ用、smはタブレット用、mdはノートパソコンや小さいデスクトップパソコンモニタ、lgは大きいサイズのデスクトップパソコンモニタを想定していると思われます。

まとめ

今までの説明を踏まえたコードが下記になります。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width" /> 
<link href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet">
<script src="http://code.jquery.com/jquery-1.11.2.min.js"></script>
<style type="text/css">
	.block{
		border: 5px solid #000000;
		padding-top: 50px;
		padding-bottom: 50px;
	}
</style>
</head>

<body>

	<div class="container">
		<div class="row">
			<header class="col-xs-12 block">
				ヘッダー
			</header>
			<div class="col-xs-6 col-md-3 block">
				コンテンツ
			</div>
			<div class="col-xs-6 col-md-3 block">
				コンテンツ
			</div>
			<div class="col-xs-6 col-md-3 block">
				コンテンツ
			</div>
			<div class="col-xs-6 col-md-3 block">
				コンテンツ
			</div>
			<footer class="col-xs-12 block">
				フッター
			</footer>

		</div>
	</div>

</body>
</html>

ブレイクポイントとcol-**-**の組み合わせで様々なパターンのレイアウトが組めると思います。

[WordPress][wp_localize_script()]WordPressで外部JSファイルにテーマファイルのURLを渡す方法。

WordPressで、ブラウザのウィンドウサイズを判定して、サイズにより異なるjsファイルをロードさせる方法を調べてみました。

その際、wp_localize_script()という関数の存在を知り、調べたことをまとめてみます。

global.js

global.jsというjsファイルを作り、以下の記述でウィンドウサイズを判定してjsフィアイルをロードします。

// global.js
jQuery(function() {

    //PC環境の場合
    if (window.matchMedia( '(min-width: 769px)' ).matches) {
        jQuery.ajax({
            url: pc.url,
            dataType: 'script',
            cache: false
       });

    //モバイル環境の場合
    } else {
        jQuery.ajax({
            url: mobile.url,
            dataType: 'script',
            cache: false
        });
    }
});

参考:レスポンシブデザイン対策!デバイスのサイズに応じてjava scriptを呼び変える

window.matchMedia()でウィンドウサイズを判定(IE9以下は未対応のようです)。

ajax()を使い、ロードしたいjsファイルのパスを記述するのですが、WordPressだと通常はテーマフォルダの中にjsファイルを置くと思ので、動的に記述する必要がありますよね。

jsファイルの中にパスを呼び出すテンプレートタグは記述できないので、以下のようにhead要素でwp_localize_script()を使ってglobal.jsに渡します。

head要素

<?php // ※head要素内に記述
    wp_deregister_script('jquery');
    wp_enqueue_script('jquery', get_template_directory_uri().'/js/jquery-2.1.1.min.js','2.1.1');
    wp_enqueue_script('global', get_template_directory_uri().'/js/global.js',array('jquery'));
    wp_localize_script('global', 'pc', array('url' => get_template_directory_uri().'/js/pc.js'));
    wp_localize_script('global', 'mobile', array('url' => get_template_directory_uri().'/js/mobile.js'));
?>

参考:Javascriptでサイトのデータを扱う方法

wp_localize_script()とは?

WordPressには、PHPからJavaScriptにデータを渡すwp_localize_scriptという関数がある。元々は多言語対応のために、JavaScriptに翻訳文字列を渡すための関数だった。現在では翻訳文字列にかぎらず、さまざまなデータをPHPからJavaScriptに渡す用途で用いられる。

引用:wp_localize_scriptの生成するJSONはHTMLエスケープされない

今回初めて知りましたが、なかなか便利な関数ですね。JavaScriptに渡したいデータをJSON形式に変換するそうです。エスケープされていないので注意する必要があるようです。

JSONはあまり使ったことが無いので詳しくないのですが、フォームから入力されたデータをJavaScriptに渡すような場合、変換されたJSONデータをを無害化した方がよい、ということでしょうか。

[jQuery]レスポンシブに対応したナビゲーションを作ってみました。

ウィンドウの幅に応じて、動作とデザインが切り替わるナビゲーションを作ってみました。ウィンドウ幅が広ければヘッダーに横一列で表示し、スマフォなどの場合はボタンで操作でナビゲーションが開閉します。

html

<header class="header">
	<h1>sample-responsive_menu</h1>
	<button class="menu_btn none">メニュー</button>
	<nav class="navigation">
		<ul>
			<li>メニュー1</li>
			<li>メニュー2</li>
			<li>メニュー3</li>
			<li>メニュー4</li>
			<li>メニュー5</li>
		</ul>
	</nav>
</header>

PCなどではヘッダーに横一列に、スマフォでは縦に並べて必要なときにボタンで開閉させる、オーソドックスなナビゲーション。

css

.none{
	display: none;
}

.menu_btn{
	cursor: pointer;
	padding: 10px;
}

.navigation li{
	list-style: none;
}

@media screen and (min-width: 641px) {
	.navigation li{
		float: left;
		margin: 10px;
	}
}

jQuery

jQuery(function() {


	var $btn = jQuery('.menu_btn');
	var $nav = jQuery('.navigation');

	/*
	 * ブラウザのウィンドウ幅を取得してメニューボタンとナビゲーションの表示/非表示を切り替え
	 */

	jQuery(window).resize(function(){

		var w = jQuery(window).width(); //ブラウザの幅を取得
		var x = 768; //ブレイクポイントを設定

		if (w <= x) { // ウィンドウサイズが768px以下

			jQuery($btn).removeClass('none');
			jQuery($nav).addClass('none');

			} else { //ウィンドウサイズが768以上
				jQuery($btn).addClass('none').text('メニュー'); //非表示・文字をメニューにする
				jQuery($nav).removeClass('none').removeAttr('style'); //表示・styleを初期化する
			}

	});

	/*
	 * クリックイベントによるスライドダウン/アップ
	 */

	jQuery($btn).on('click',(function(){

		if (jQuery($nav).css('display')=='none'){
			jQuery($nav).slideDown('slow');
			jQuery($btn).text('メニューを閉じる');
		} else {
			jQuery($nav).slideUp('fast');
			jQuery($btn).text('メニュー');
		}

		})
	);


});

demo

開閉ボタンとナビゲーションは、ウィンドウ幅に応じて.noneを付けたり取ったりしてdislplayの状態をコントロールします。ナビゲーションが閉じているときはボタンの文字が「メニュー」、開いているときは「メニューを閉じる」になります。

PC閲覧時にウィンドウ幅をリサイズした場合も考慮していて、ウィンドウ640px以下にしてナビゲーションを開いた後、ウィンドウサイズを641px以上にした場合、ナビゲーションはいったん初期化。非表示になります。ボタンの文字もメニューに戻ります。

高機能なjQueryプラグインがいろいろあるので使ってもいいのですが、自分で作ったほうがカスタマイズが簡単だったり、設置が手軽だったりするかと。

なにより自分で作ってみるといろいろと勉強になります。

※なお、jQuery(window).resize(function(){/*何らかの処理*/});という記述でウィンドウ幅を取得しているのですが、リサイズし続ける間中取得し続けるので、発火する処理によってはブラウザに負荷がかかるようです。注意が必要かと思います。

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

[gulp]ベンダープレフィックスとソースマップを同時に出力できない?

gulp始めました。が、初心者なため、まだわからないことも多いです。

今の悩みは「ソースマップの出力」と「CSS3のベンダープレフィックスの付与」を同時に実行するのが上手くいかない、というものです。

ひとまず基本的なglupfileの記述。ソースマップは出ます。

以下の様なタスクをglupfileに記述しています。(※WindowsのFirefoxで開発してます。)

var gulp = require('gulp'); // gulpを利用可能にする。
var sass = require('gulp-ruby-sass'); //gulp-ruby-sassを利用可能にする。


//指定したディリクトリ/ファイルのsassファイルを指定したディリクトリにコンパイルする。
gulp.task("sass", function() {

	gulp.src("sass/style.scss")
		.pipe(sass({
				style : 'expanded'
			}))
		.pipe(gulp.dest('./'))

});

これでstyle.css.mapが出力され、FireFoxの開発ツールで閲覧できます。

ベンダープレフィックスをつけるには

ベンダープレフィックスを付けたいときは、専用のプラグインをインストールし、タスクを記述する必要があります。
自分はgulp-pleeeaseというものを使っています。
glupfileは以下のようになります。

var gulp = require('gulp'); // gulpを利用可能にする。
var sass = require('gulp-ruby-sass'); //gulp-ruby-sassを利用可能にする。
var pleeease = require("gulp-pleeease"); //ベンダープレフィックスプラグイン。

//指定したディリクトリ/ファイルのsassファイルを指定したディリクトリにコンパイルする。
gulp.task("sass", function() {

	gulp.src("sass/style.scss")
		.pipe(sass({
			style : 'expanded'
		}))
		.pipe(pleeease({
			minifier: false // 圧縮しない
		}))
		.pipe(gulp.dest('./'))

});

エラーが出てしまう…

が、しかし!これだとコマンドプロンプトがエラーを吐いてコンパイルを停めてしまいます…

いろいろ試した結果、gulp-ruby-sassを使っているのが原因みたいです。
gulp-ruby-sassを使うのをやめて、gulp-sassに切り替るとするとエラーを吐きませんでした。(うーん何でだろう?)

とりあえずできたからいいか、と喜んだのも束の間、今度はソースマップが出力されなくなりました…
ソースマップを出力するプラグインがあったので使ってみたのですが、なぜかFireFoxの開発ツールで閲覧できず、解決に至りませんでした。

原因が解るまでの、ひとまずの対策

現状では

(1)ソースマップは出せるが、ベンダープレフィックスは出力できない。
(2)ソースマップは出せないが、ベンダープレフィックスは出力できる。

という二者択一の状態になってしまいました。

差し当たって、開発時は(1)でコードを書き、公開時に(2)でプレフィックスを付与する、というやり方で凌ぐことにします。

以下のようなgulpfileにして、開発と公開時によって切り替えるようにしてみました。

(1)開発フェーズ

var gulp = require('gulp'); // gulpを利用可能にする。
var sass = require('gulp-ruby-sass'); //gulp-ruby-sassを利用可能にする。
//var sass = require('gulp-sass'); //gulp-sassを利用可能にする。
var pleeease = require("gulp-pleeease"); //ベンダープレフィックスプラグイン。

//指定したディリクトリ/ファイルのsassファイルを指定したディリクトリにコンパイルする。
gulp.task("sass", function() {

	gulp.src("sass/style.scss")
		.pipe(sass({
			style : 'expanded'
		}))
//	.pipe(pleeease({
//		minifier: false // 圧縮しない
//	}))
	.pipe(gulp.dest('./'))

});

(2)公開フェーズ

var gulp = require('gulp'); // gulpを利用可能にする。
//var sass = require('gulp-ruby-sass'); //gulp-ruby-sassを利用可能にする。
var sass = require('gulp-sass'); //gulp-sassを利用可能にする。
var pleeease = require("gulp-pleeease"); //ベンダープレフィックスプラグイン。

//指定したディリクトリ/ファイルのsassファイルを指定したディリクトリにコンパイルする。
gulp.task("sass", function() {

	gulp.src("sass/style.scss")
		.pipe(sass({
			style : 'expanded'
		}))
		.pipe(pleeease({
			minifier: false // 圧縮しない
		}))
		.pipe(gulp.dest('./'))

});

どこが良くないのか、現状残念ながらわかりませんが、いずれは解決したいですね。

[jQuery]タップで開閉するメニューをif文で作る。

マウスをhoverさせて表示/非表示を切り替えるメニュー(ドロップダウンメニューなど)はよくありますが、iPadなどタッチデバイスではhoverは動作しません。

なので、タッチデバイス向けにタップすると表示され、もう一度タップすると非表示になるメニューを作成してみました。

あまり洗練された動きではないですが、jQueryの勉強として公開してみます。

サンプルコード

<!DOCTYPE html>
<html lang="jp">
<head>
	<meta charset="UTF-8">
	<title>example</title>
	<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>

	<style type="text/css">
	
		.menu{
			float: left;
			margin-left: 30px;
		}

		li{
			list-style: none;
		}

		button{
			padding: 5px 30px;
		}

		ul{
			padding: 0;
			display: none;
		}

	</style>

    <script type="text/javascript">

		jQuery(function() {

			jQuery('.menu').on('click', function(){
				var content = jQuery('ul',this);
				if(jQuery(content).css('display')=='none'){ //contentのdisplayがnoneの場合
					jQuery(content).css('display','block');
				}else{//contentのdisplayがnoneではない場合
					jQuery(content).css('display','none');
				}

			});

		});

    </script>

</head>
<body>
<div>
	
	<div class="menu">
		<button type="button">ボタン1</button>
		<ul>
			<li>メニュー1</li>
			<li>メニュー2</li>
			<li>メニュー3</li>
			<li>メニュー4</li>
			<li>メニュー5</li>
		</ul>
	</div>
	<div class="menu">
		<button type="button">ボタン2</button>
		<ul>
		<li>メニュー6</li>
		<li>メニュー7</li>
		<li>メニュー8</li>
		<li>メニュー9</li>
		<li>メニュー10</li>
		</ul>
	</div>
	<div class="menu">
		<button type="button">ボタン3</button>
		<ul>
		<li>メニュー11</li>
		<li>メニュー12</li>
		<li>メニュー13</li>
		<li>メニュー14</li>
		<li>メニュー15</li>
		</ul>
	</div>

</ul>
</body>
</html>

demo

コードをおさらい

jQeryの部分を抽出してひとつひとつ見ていきます。

clickイベントをon()を使って設定する。

サンプルコードでは、li要素をメニュー項目として、ul要素のdisplayのblock/noneを切り替えるという動作をさせます。

jQuery('.menu').on('click', function(){

とすることで、.menuに対してclickイベントを設定。
クリックした.menu直下のul要素だけを対象にするためにthisを使って以下のように

var content = jQuery('ul',this);

値を取得し、変数に入れて次の処理で使います。

ここまでの2行で開閉させたいul要素を絞り込むところまで出来ました。
次の工程では実際に開閉させるための処理を作っていきます。

if分岐でdisplayをコントロールする

ul要素のdisplayが「noneであればblock」にして表示、「blockならnone」にして非表示といったif分岐を作ります。

if(jQuery(content).css('display')=='none'){
//〜

== は「左右の値が同じ」ならtrueを返す演算子です。
「content(クリックした.menuの子要素のul要素)」のdisplayの値が「none」と同じかどうか比較しています。
同じ場合、つまりdislay:noneならば次の処理に進みます。

jQuery(content).css('display','block');

display:noneをblockに変えてul要素を表示します。
noneと同じではなかった場合(ul要素のdisplayがblockだった場合)、falseが返されてこの処理はスキップ。次の処理に進みます。

}else{
	jQuery(content).css('display','none');
}

displayをblockに変えます。開閉することが目的なので、メニューが閉じている状態でも開いている状態でも対応できています。
以上のスクリプトをタッチデバイスの時のみ読み込むようにするといいと思います。
WordPressであれば、is_mobile()とかですね。

以上で完了です。
間違いがあれば指摘いただけると幸いです。

[jQuery].each()とthisを使って要素ごとに繰り返し処理を行う。

繰り返し処理のサンプルコードを作ってみました。
span要素とa要素を含むli要素があったとして、li要素ごとにa要素の中にspan要素を移動させる、というような動作をさせます。繰り返しさせるために.each()を使います。

jQuery

jQuery(function(){

	jQuery('li').each(function(){
		var url = jQuery('a',this); //liの子要素のa要素を取得し、変数にセットする。
		jQuery('span',this).prependTo(url); //liの子要素のspan要素を変数urlの要素の内側先頭にに移動する。
	});

});

html

<ul>
	<li>
		<span>hoge1</span>
		<a href="">:リンク</a>
	</li>
	<li>
		<span>hoge2</span>
		<a href="">:リンク</a>
	</li>
	<li>
		<span>hoge3</span>
		<a href="">:リンク</a>
	</li>
	<li>
		<span>hoge4</span>
		<a href="">:リンク</a>
	</li>
	<li>
		<span>hoge5</span>
		<a href="">:リンク</a>
	</li>
	<li>
		<span>hoge6</span>
		<a href="">:リンク</a>
	</li>
</ul>

実行結果(html)


<ul>
	<li>
		<a href=""><span>hoge1</span>:リンク</a>
	</li>
	<li>
		<a href=""><span>hoge2</span>:リンク</a>
	</li>
	<li>
		<a href=""><span>hoge3</span>:リンク</a>
	</li>
	<li>
		<a href=""><span>hoge4</span>:リンク</a>
	</li>
	<li>
		<a href=""><span>hoge5</span>:リンク</a>
	</li>
	<li>
		<a href=""><span>hoge6</span>:リンク</a>
	</li>
</ul>

demo

実際の動作は以下のデモページよりご覧いただけます。
eachデモ

まとめ

jQuery(‘a’,this)とすることで”li要素の子要素のa”を取得し、それを変数”url”に入れる。
次にjQuery(‘span’,this)→li要素の子要素のspanを指定して、.prependTo(url)→変数urlに入ってるaの中に入れるよ。でもって.each()でくくってるからli要素無くなるまで繰り返すよ、というような理解でいいと思います。

参考URL

jQuery の $().each() で個別処理(ループ) | バシャログ。

【jQuery】要素を指定するセレクタの使い方 まとめ | Web制作会社スタイル