Objective
Front-End
JavaScript
自己紹介
me = {
name: "muyuu",
twitter: "@anticyborg",
belongs: "freelance",
job: "Web Front-End Engineer"
};
jQuery plugin
GOOD
• 早い
• 簡単
• 何だったらJS書けなくても何とかなる
BAD
• 変更に弱い
• プラグインにない機能を求められると詰む
• いつまで経ってもJS覚えられない
割と辛い思いをすることも多い
自分で作る
BAD
• 面倒
• 果たして望む機能を作れるのか
• プラグインみたいにすげーの作れるのか
GOOD
• すげーのはいらない。必要な機能だけあれば良い
• 追加機能が欲しくなったらその時足せば良い
• 変更時も理解しやすい(理解しやすいように書けば)
GOOD
• すげーのはいらない。必要な機能だけあれば良い
• 追加機能が欲しくなったらその時足せば良い
• 変更時も理解しやすい(理解しやすいように書けば)
JSの理解も深まるよ!
いいことだらけ
さぁ作ろう
いま作ろう
今回作るもの
Tab
何も考えずにタブを実装
$(function() {
// 最初の要素以外は非表示
$(".body li").not(":first").hide();
$(".tab a").click(function(){
// 表示するコンテンツのIDを取得
var target = $(this).attr("href").replace("#", "");
// コンテンツ部分を全部隠す
$(".body li").hide();
// クリックしたタブのhretと同じIDを持つ箇所だけを表示する
$(".body").find('#' + target).show();
return false;
});
});
problemas
パターンが増える度に
コードが倍増
$(function() {
// アニメーションするパターン
$(".body2 li").not(":first").hide();
$(".tab2 a").click(function(){
var target = $(this).attr("href").replace("#", "");
$(".body2 li").hide('slow');
$(".body2").find('#' + target).show('slow');
return false;
});
// ページ表示時は2つ目が開いてるパターン
$(".body3 li").not(":first").hide();
$(".tab3 a").click(function(){
var target = $(this).attr("href").replace("#", "");
$(".body3 li").hide('slow');
$(".body3").find('#' + target).show('slow');
return false;
});
});
solution
解決法
• タブの原型となるオブジェクトを作る
• 原型を元に実体を生成する
• 実体はパターン毎に生成する
object
オブジェクトとは、名前
と値を持つプロパティを
格納するコンテナにすぎ
ない
— 開眼!JavaScript
object sample
// コンストラクタ関数を使用して僕オブジェクトを生成
var muyuu = new Object();
//僕オブジェクトにプロパティを追加
muyuu.name = "muyuu";
muyuu.age = 33;
muyuu.gender = "male";
console.log(muyuu.age); // 33 と表示される
object sample
//オブジェクトに関数を追加する
muyuu.hello = function(){
console.log("Hello! I'm " + muyuu.name);
};
muyuu.hello();
// "Hello! I'm muyuu" と表示される
• プロパティが関数の場合は「メソッド」とも呼ばれる
コンストラクタ関数?
コンストラクタ関数
コンストラクタ関数とは、あらかじめ決められたオブジェクト
を生成するテンプレート、クッキーの抜き型のようなものだと
思ってください
— 開眼!JavaScript
コンストラクタ関数
var obj = new Object(); // オブジェクトを変数objに生成
var num = new Number(123); // 数値オブジェクトを変数numに生成
var str = new String('aaa'); // 文字列オブジェクトを変数strに生成
コンストラクタ関数
こんな感じのが実装されている
Object = function(){
//新しいオブジェクトを作るための処理
};
コンストラクタ関数で
独自オブジェクトを作る
コンストラクタ関数
僕オブジェクトのテンプレートとして人コンストラクタを作る
var Human = function(name, age, gender){
this.name = name;
this.age = age;
this.gender = gender;
this.hello = function (){
console.log("Hello! I'm " + this.name);
};
};
コンストラクタ関数
人コンストラクタで僕オブジェクトを作成
// Humanコンストラクタを使用して僕オブジェクトを生成
// 生成する際に僕のデータを引数で渡してあげれば
// コンストラクタの設定通りに生成される
var muyuu = new Human("muyuu", 33, "male");
// 挨拶をする
muyuu.hello(); // Hello! I'm muyuu と表示される
ちょっと寄り道して
も1歩深く
通常の関数の挙動
• 関数は通常、何かしらの処理をして何かしらの値を返す
• 何もreturnしない場合はfalse相当の値を返す
//return がある関数
function add(a,b){
return a + b;
}
console.log(add(1, 2)); // 3
// return がない関数
function alertAdd(a, b){
var add = a + b;
}
console.log(alertAdd()); // undefined
コンストラクタ関数の挙動
• オブジェクトを返している
var muyuu = new Human("muyuu", 33, "male");
console.log(muyuu);
// {
// name:"muyuu",
// age:33,
// gender: "male",
// hello: function
// }
Why?
new 演算子
• 関数はnew演算子を使って呼び出された場合、コンストラク
タ関数のthisの値として、生成されたオブジェクトとして設定
する
• new演算子を使って呼び出された場合にreturnを宣言してい
ない場合は通常falseを返すところを新たに生成されるオブジ
ェクト(this)を返すようになる
new 演算子
var Human = function(name, age, gender){
//new演算子で呼び出した場合、実はこの処理が入っている
// var this = new Object();
this.name = name;
this.age = age;
...
//new演算子で呼び出し、returnを行っていない場合
//実はthisを返している
//return this;
};
閑話休題
タブコンストラクタ作って
パターン毎に生成するぜ!
タブの実装に必要な機能
コンストラクタ呼び出し時に以下を指定
• タブのルートのクラス
• タブ部分のクラス
• コンテンツ部分のクラス
タブの実装に必要な機能
必要な機能
*今表示している要素の番号(何番目)
*今表示している要素が何番目かを知る機能
*タブを切り替える機能
*タブ切り替え時にクラスを付与する機能
コンストラクタを作成
// constructor
var Tab = function(param){
};
Tabコンストラクタを作成
コンストラクタ呼び出し時
var tab1 = new Tab({
root: ".tab", // タブのルート要素を指定
item: ".tabHead li", // タブ部分の他所を指定
body: ".tabBody li" // コンテンツ部分の他所を指定
});
コンストラクタ関数呼び出し時に、実際ページあるタブの要素
を新たに作るオブジェクトに設定するため、オプションとして
オブジェクトを渡す。
コンストラクタを作成
var Tab = function(param){
this.$root = $(param.root);
this.$item = this.$root.find(param.item);
this.$body = this.$root.find(param.body);
...
};
コンストラクタ実行時に渡された引数からjQueryオブジェクト
を保存
コンストラクタを作成
var Tab = function(param){
...
//カレントのタブとコンテンツに付与するclassを設定
this.adClass = 'current';
// カレントのコンテンツが何番目かを保存する変数を作成
this.currentIndex;
...
};
コンストラクタを作成
var Tab = function(param){
this.param = param // オプションを追加
...
};
コンストラクタを作成
var Tab = function(param){
...
this.init(); //ページ表示時に実行した処理を実行
//タブをクリックした際の挙動を設定
var self = this; // alias
this.$item.on("click", "a", function(){
self.setCurrent(this); // カレントをセット
self.change(); // タブを切り替え
});
};
コンストラクタを作成
// constructor
var Tab = function(param){
this.param = param;
this.$root = $(param.root);
this.$item = this.$root.find(param.item);
this.$body = this.$root.find(param.body);
this.adClass = 'current'; // class
this.currentIndex = 0; // current tab index
this.init(); // 初期化
var self = this; // alias
this.$item.on("click", "a", function(){ // evnet
self.setCurrent(this);
self.change();
});
};
必要な機能をコンストラクタに追加
ページ表示時に実行する関数
Tab.prototype.init = function(){
this.setCurrent(); // カレントをセット
this.change(); // タブを切り替え
};
必要な機能をコンストラクタに追加
カレントが何番目かを取得する関数
Tab.prototype.setCurrent = function(ele){
this.currentIndex = 0;
if (ele){
// 引数がタブの何番目の要素かを取得
var index = $(ele).parent().index();
// オブジェクト変数にセット
this.currentIndex = index;
}
};
必要な機能をコンストラクタに追加
タブを切り替える関数
Tab.prototype.change = function(){
this.changeTab(); // タブ部分を変更
this.changeBody(); // コンテンツ部分を変更
};
必要な機能をコンストラクタに追加
タブ部分の変更処理をまとめた関数
Tab.prototype.changeTab = function(){
this.$item
.removeClass(this.adClass) // 全部のタブからクラスを外して
.eq(this.currentIndex) // カレントのタブのみ
.addClass(this.adClass); // クラスを付与する
};
必要な機能をコンストラクタに追加
コンテンツ部分の変更処理をまとめた関数
Tab.prototype.changeBody = function(){
this.$body
.removeClass(this.adClass) // 全部のコンテンツからクラスを外して
.hide() // 非表示にして
.eq(this.currentIndex) // カレントのコンテンツのみ
.addClass(this.adClass) // クラスを付与して
.show(); // 表示する
};
完成
後は必要な機能を
必要になったら足すだけ
コンテンツの表示で
アニメーション足したい
該当関数を変更するだけ
Tab.prototype.changeBody = function(){
this.$body
.removeClass(this.adClass) // 全部のコンテンツからクラスを外して
.hide('slow') // 非表示にして
.eq(this.currentIndex) // カレントのコンテンツのみ
.addClass(this.adClass) // クラスを付与して
.show('slow'); // 表示する
};
アニメーションは
オプションにしたい
呼び出し時にオプションを追加
var tab1 = new Tab({
root: ".tab", // タブのルート要素を指定
item: ".tabHead li", // タブ部分の他所を指定
body: ".tabBody li", // コンテンツ部分の他所を指定
duration: true // アニメーションの指定
});
オプションによって挙動を変える
Tab.prototype.changeBody = function(){
this.$body
.removeClass(this.adClass) // 全部のコンテンツからクラスを外して
.hide(param.duration) // 非表示にして
.eq(this.currentIndex) // カレントのコンテンツのみ
.addClass(this.adClass) // クラスを付与して
.show(param.duration); // 表示する
};
呼び出し
var tab1 = new Tab({
root: ".tab", // タブのルート要素を指定
item: ".tabHead li", // タブ部分の他所を指定
body: ".tabBody li", // コンテンツ部分の他所を指定
});
var tab2 = new Tab({
root: ".otherTab", // タブのルート要素を指定
item: ".otherTabHead .tab", // タブ部分の他所を指定
body: ".otherTabBody .content", // コンテンツ部分の他所を指定
duration: 400
});
ステキ

Objective Front-End JavaScript