Help us understand the problem. What is going on with this article?

0行から始めるクライアントサイドJavaScript入門

More than 5 years have passed since last update.

0行〜

準備すること

Modernizr.js

ブラウザの機能判定プラグイン。
読み込むだけでhtmlタグにブラウザの機能クラス名がつけられる。↓

<html lang="en" class="js no-touch postmessage history multiplebgs boxshadow opacity cssanimations csscolumns cssgradients csstransforms csstransitions fontface localstorage sessionstorage svg inlinesvg no-blobbuilder blob bloburls download formdata">

クラス名の最初にno-がつくと非対応、そうでないものは対応。

  • no-touch: タッチイベント非対応
  • postmessage: postMessage対応

のような感じ。

JS API 使用例

if(Modernizr.touch){
//タッチイベント対応ブラウザ用の処理
} else {
//タッチイベント非対応ブラウザ用の処理
};
//CSSファイルの非同期読み込み
Modernizr.load(
    [{
        test:Modernizr.svg,
        yep:'css/icons.data.svg.css',
        nope:'css/icons.data.png.css'
    }]
);

cssua.js

ユーザーエージェント判定プラグイン。
読み込むだけでhtmlタグにユーザーエージェントクラス名がつけられる。↓

<html class="ua-chrome ua-chrome-38 ua-chrome-38-0 ua-chrome-38-0-2125 ua-chrome-38-0-2125-122 ua-desktop ua-desktop-macintosh ua-mac_os_x ua-mac_os_x-10 ua-mac_os_x-10-10 ua-mac_os_x-10-10-1 ua-webkit ua-webkit-537 ua-webkit-537-36 js">

CSSハックが不要になります。
また、htmlタグにIEのクラス名をつけるのに↓のような記述が出回ってますが

<!--[if lt IE 8]> <html class="no-js ie7 oldie" lang="en"> <![endif]-->
<!--[if IE 8]>    <html class="no-js ie8 oldie" lang="en"> <![endif]-->
<!--[if IE 9]>    <html class="no-js ie9 oldie" lang="en"> <![endif]-->
<!--[if gt IE 9]><!--> <html class="no-js" lang="en"> <!--<![endif]-->

この記述によってmetaで互換表示ボタンを非表示に設定しても表示されるようになってしまいます。cssuaを使えば記述の必要がなくなります。

JS API 使用例

if(cssua.ua.ie && cssua.ua.ie <= 8){
//IE8以下
} else {
//それ以外
};

1行〜

準備すること

underscore.js

ユーティリティライブラリ。jQueryがカバーしていない機能をカバーしているので一緒にいれるとよいです

サンプル

サムネイル画像非同期読み込み(1行)

_.each([1,2,3,4,5], function(el, i){ document.getElementById("swap-img" + el).src = "thumb" + el + ".jpg"})

4行〜

準備すること

即時関数

コードは即時関数で囲み、その中にコードを記述する。始めに"use strict";を記述

(function(){
  "use strict";
  //ここにコードを記述
})();

jQuery

DOM要素(表示されている要素)にコードを実行させたい場合
即時関数の代わりにjQueryのready関数を使用

$(function(){
  "use strict";
  //ここにコードを記述
});

サンプル

  • ユーザーエージェントをチェックしてスマホの場合スマホサイトに移動(8行)
(function () {
    "use strict";
   var ua = navigator.userAgent,
        isSp = ua.indexOf('iPhone') > 0 || ua.indexOf('iPod') > 0 || ua.indexOf('Android') > 0 && ua.indexOf('Mobile') > 0;
    if (isSp) {
        location.href = "./sp";
    }
})();
  • アンカーリンクの移動をアニメーション(9行)
$(function(){
  "use strict";
  $("a[href^=#]").click(function(){
    var hash = $(this.hash);
    var hashOffset = $(hash).offset().top;
    $("html,body").animate({scrollTop: hashOffset}, 1000);
    return false;
  });
});

100行〜

コードが読みにくくならないよう適切に分割・整理します。

準備すること

関数

function hoge(){}

名前空間パターン

Objectを使って名前空間を作ります。

var namespace = namespace || {}; //Object上書きを防ぐ
namespace.View = {};
namespace.Model = {};

モジュールパターン

簡単にクラスを作れます。

var mod = mod || {};

mod.Model = function() {
 "use strict";
 var that = {//外部からアクセス可能
  start : function(){_start();} 
 };
 function _start() { //外部からアクセス不可
 }
 return that;//thatをインスタンスとして渡す
}

(function(){
 "use strict";
 var model = mod.Model();
 model.start();
});

jQueryカスタムイベント

クラス間のやりとりができる

var jqe = jqe || {};

jqe.Event = {
 START:"start"
};

jqe.Model = function(Event){
 "use strict";
 var that = {
  start : function(){$(Event).trigger("start")}//送出
 };
 return that;
}

jqe.View = function($dom, Event) {
 "use strict";
 function construct() {
   $(Event).on("start", function(){console.log("hoge");});//監視
 }
  construct();
}

(function(){
 "use strict";
 var Event = jqe.Event,
     model = jqe.Model(Event);
 jqe.View($("body"), Event);
 model.start();
});

MVCパターン

上記4つを利用してMVCパターンを実現させます。
コードをViewクラスとModelクラスに分割・整理します。
情報を受け取って見た目に反映させる部分をViewに振り分け、
それ以外の部分はModelとして振り分けます。
Viewの分け方は各UIパーツごとに、Modelの分け方は各機能ごとに。

var mvc = mvc || {};

mvc.Model = function(){
 "use strict";
 var that = {
  start : startHandler
 };

 function getDate() {
   var date = new Date();
   return date.getMonth() + "" + date.getDate() + ""
 }

 function startHandler() {
   $(that).trigger("ready", [getDate()])//送出
 }

 return that;
}

mvc.View = function($dom, model) {
 "use strict";

 function construct() {
   $(model).on("ready", render);//modelを監視
 }

 function render(e, dateStr) {
  $dom.html("今日は" + dateStr + "です。");
 }

 construct();
}

(function(){
 "use strict";
 var model = mvc.Model();
 mvc.View($("body"), model);
 model.start();
});

MVCパターンの詳しい説明は
参考 > 100行〜 > MVC
を参照ください。

サンプル

時計アプリ(244行)

デモ
ダウンロード
Github

1000行〜

1000行を超えると一つのJSファイルで開発を進めるのが辛くなってきます。
ファイルを分割して開発できる仕組みが必要になります。

準備すること

Grunt

JSファイル分割開発の仕組みとして下記いずれかを使用します。

[A] grunt-contrib-concat

単純に複数JSファイルをひとつに結合します。編集→プレビューの度コンパイルが必要になります。

Gruntfile.js

grunt.initConfig({

    /*
     * 初期設定
     * */
    dirs:{
        deploy:'deploy'
    },

    watch:{
        deploy:{
            files:['<%= dirs.src %>/**'],
            tasks:['deploy']
        }
    },

    /*
     * カスタム設定
     * */
    concat:{
        deploy:{
            src:[
                'src/js/qlock/TimerModel.js',
                'src/js/qlock/ClockView.js',
                'src/js/qlock/ContainerView.js',
                'src/js/qlock/MaskView.js',
                'src/js/qlock/App.js'
            ],
            dest:'<%= dirs.deploy %>/js/main.js'
        }
    }
});

grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-watch');

grunt.registerTask('deploy', ['concat:deploy']);
時計アプリ

デモ
ダウンロード
Github

[B] grunt-usemin

HTMLに記述してある一連のscriptタグを1つのJSにまとめ、
HTML内の記述も書き換えます。grunt-contrib-concatのようにプレビューの度のコンパイルが必要なくなります。

書き出し前のHTML

<!-- build:js js/main.js -->
<script src="../src/js/qlock/TimerModel.js"></script>
<script src="../src/js/qlock/ClockView.js"></script>
<script src="../src/js/qlock/ContainerView.js"></script>
<script src="../src/js/qlock/MaskView.js"></script>
<script src="../src/js/App.js"></script>
<!-- endbuild -->

書き出し後のHTML

<script src="js/main.js"></script>

Gruntfile.js

grunt.initConfig({
    dirs:{
        deploy:'deploy',
        release:'_release',
        src:'src'
    },
    copy:{
        toRelease:{
            files:[{
                expand:true,
                cwd:'<%= dirs.deploy %>',
                src:['**'],
                dest:'<%= dirs.release %>'
            }],
            dot:true
        }
    },
    useminPrepare:{
        html:'<%= dirs.deploy %>/index.html',
        options:{
            root:'<%= dirs.deploy %>/',
            dest:'<%= dirs.release %>/'
        }
    },
    usemin:{
        html:'<%= dirs.release %>/index.html'
    }
});

grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-usemin');

grunt.registerTask('release', ['copy:toRelease','useminPrepare', 'concat:generated', 'uglify:generated', 'usemin']);
時計アプリ

デモ
ダウンロード
Github

[C] grunt-requirejs(RequireJSを使用)

こちらに内容を準備中です。

2000行〜

2000行という行数はいい加減な設定ですが、1000行〜までの方法で作ることが難しくなったらAltJSやMVCフレームワークなどの使用を検討します。

参考リンク

4行〜

即時関数

“use strict”(厳格モード)を使うべきか?

100行〜

MVC

フロントエンドJavaScriptにおける設計とテスト
「MVCの勘違い」について、もう一度考えてみる

1000行〜

Grunt

Webサイト制作に便利なGruntプラグイン(その1)
Webサイト制作に便利なGruntプラグイン(その2)
勉強会資料シェア Getting Started with RequireJS

nowri
テクニカルディレクター
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away