フレームワーク習得メモ (1) AngularJS

環境構築メモ (3) RequireJS

RequireJSは、JSファイルの依存関係・実行順を、設定ファイル内に予め定義しておくことで、
html上のsourceタグでは先頭のJSファイル1つだけを読み込んで、
残りのJSファイルの非同期ロード・処理順をRequireJSに一任できるライブラリ。

これをワークフローに組み込んだものが、 soohei/grunt-requirejs-seed

今までからの変更点として、開発用のディレクトリをwwwとして、
デプロイ用のディレクトリはwww-buildを別に作るようにした。(www-buildはgitignoreしてある)
またjQueryはCDNからロードしたり、自分が書くコードはRequireJS関係も含め、
全てsource/coffee内でCoffeeScriptで書けるようにした。

この www-build の書き出しは、デプロイ時の処理の中に新たに導入した、RequireJS Optimizer が行う。
(toolsディレクトリの中、Gruntのタスク名は、 exec:build )

また、Optimizerの設定に、”removeCombined”: true を追記することで、
結合に使ったファイルをビルド先にコピーしないようにしたり、
(参考: Do not copy source file when optimizing using requirejs – Stack Overflow)
前回紹介した、Gruntでのugilify時のオプション preserveComments: ‘some’ も採用してます。

それとRequireJSの書き方の話で、requireとdefineの違いについては、
「javascript – RequireJs – Define vs Require – Stack Overflow」 に書かれているベストアンサーが的確。

そもそものRequireJSの理解は、bouzeの 「Require.jsを試してみた | blog.bouze.me」 に超救われた。

最後に、gruntのコマンドなどを整理すると、
人間が作業するのはwww/source以下。Gulpが頑張るのがwww/assets以下。
デプロイに使うのは、www-buildに残ったファイル一式
普段は、grunt
デプロイ前に、grunt -dist
www-buildをルートにしてサーバーを立てて、動作チェックをするとは、 grunt prod-serve

次回は、JSのライブラリの管理をする「パッケージマネージャー」、Bowerです。

コーディング環境 (2014.08)

日々進化している最近のコーディング (フロントエンド開発) 環境について、
ここ1年くらいのノウハウを詰めて、
極端には新しいもの好きではない視点で整理します。

目次:
1. psdからのスライス
2. Compass
3. スプライトイメージの自動化
4. CoffeeScript
5. Gruntによる自動化
6. ローカルサーバー
7. その他

1. psdからのスライス

slicy

Slicyというアプリを使い始めてからは、ずっとこれです。
Photoshopのレイヤーやグループ名に画像のファイル名(+拡張子)を付けることで、
ワンクリックで画像を書き出せます。

ファイル名に@2xと付けることで、Retina用画像(@2x)と、
等倍画像を同時に書き出すこともできます。

その他に@boundsというレイヤーをつくって矩形領域を指定したり、
@slicesというフォルダをつくって、その中にファイル名と
領域指定用のレイヤーをつくることも出来ます。

公式サイトのヘルプを読んだり、
サンプルをダウンロードすると理解が早いです。

 

2. Compass

CSSにネスト構造をつくって記述できるフレームワーク。
インストールの方法などは公式ドキュメント参照。
このエントリーでは、”gem install compass –pre” でインストール出来る、
Compass 1.0.0.rc.1を使って、SCSSの文法で書いています。

必要なコマンドは
compass watch

 

3. スプライトイメージの自動化

Compassのmixinを使えば、関数や変数を使ってCSSを記述できます。
一番の恩恵として、スプライトイメージの生成の自動化について、
色々試行錯誤した結果、現時点で使っているmixinを紹介します。
参考: Using Compass Generated Sprite Sheets in Responsive Sass, by Jayson Jacobs.

・サーバーにアップするファイルはassetsフォルダにまとめる
・サーバーにアップしないファイルはsourceフォルダにまとめる
・Source Mapを有効にするとChromeでscssファイルのデバッグができる
参考: Working with CSS Preprocessors – Google Chrome
・ロゴ用の画像として、
/resource/images/sprite/logo.png
/resource/images/sprite_2x/logo_2x.png (2倍サイズ)
の2枚が用意されているとする。

config.rb

css_dir = "assets/css"
sass_dir = "source/sass"
images_dir = "source/images"
generated_images_dir = "assets/images"
javascripts_dir = "assets/js"
fonts_dir = "assets/fonts"
output_style = :nested
relative_assets = true
line_comments = true
preferred_syntax = :scss
sass_options = { :sourcemap => true }

HTML

<div class="logo">
<div class="sprite">Logo</div>
</div>

→ .sprite部分に、background-imageが適用される

scssファイル

// スプライト画像
.sprite {
display: block;
overflow: hidden;
background-repeat: no-repeat;
text-align: left;
text-indent: -10000px;
font-size: 1px;
line-height: 1px;
}

@mixin sprite-background($sprites, $sprites-img, $name, $scale, $skip-base-params:false) {
background-image: $sprites-img;
@include background-size(image-width(sprite-path($sprites)) / $scale auto);

//retina時に重複するパラメータをスキップするための分岐
@if not($skip-base-params){
height: image-height(sprite-file($sprites, $name)) / $scale;
width: image-width(sprite-file($sprites, $name)) / $scale;
$ypos: nth(sprite-position($sprites, $name), 2) / $scale;
background-position: 0 $ypos;
}
}

$sprites: sprite-map("sprite/*.png", $spacing: 2px);
$sprites-img: sprite-url($sprites);
$sprites_2x: sprite-map("sprite_2x/*.png", $spacing: 4px);
$sprites-img_2x: sprite-url($sprites_2x);

@mixin sprite-image ($name) {
.sprite {
@include sprite-background($sprites, $sprites-img, $name, 1);
}
.backgroundsize.retina_2x & {
.sprite {
@include sprite-background($sprites_2x, $sprites-img_2x, $name, 2, true);
}
}
}

→ /assets/images/ フォルダ内にスプライトイメージが書き出される。

 

また、Slicyからの作業の効率化を考えて、一つ、自作のRubyのスクリプトを使っています。
ruby mv_sprite.rb
で実行すると、実行したディレクトリ以下の@2xという名前がついた画像を、
_2xにリネームして、さらに_2x付きのディレクトリを作ってその中へ移動します。
@がついたファイル名が、Compassではコンパイルエラーを起こすことへの対処です。
(Compass 0.13.alpha.12時点)
上記mixinも、Slicy書き出し→このRubyの処理、が前提になっています。

例:
assets/images/sprite/logo.png
assets/images/sprite/logo@2x.png

assets/images/sprite/logo.png
assets/images/sprite_2x/logo_2x.png

mv_sprite.rb

# ディレクトリを再帰的に抽出する
require 'pp'
require 'FileUtils'

root = File.expand_path(File.dirname(__FILE__))
root = ARGV[0] if ARGV[0]

# フォルダの一覧を取得
dir_entries = Dir.glob(root + "/" + "**/*")
# pp dir_entries

for filePath in dir_entries do
# pp file
ext = File::extname(filePath) # 拡張子取得

# 画像のみに処理
if( ext == '.jpg' || ext == '.png' || ext == '.gif' )
dir = File::dirname(filePath) # ディレクトリ
base = File::basename(filePath,ext) # 拡張子除去
twox_index = base.index('@2x')

# @2xを含む
if( twox_index && twox_index >= 0 )
p "#{dir}/#{base}#{ext}"
parent_dir = File::dirname(dir) # e.g. path_to_htdocs/common/img
my_dir = File::basename(dir) # e.g. sprite
twox_my_dir = "#{my_dir}_2x" # e.g. sprite_2x
twox_dir = "#{parent_dir}/#{twox_my_dir}" # e.g. path_to_htdocs/common/img/sprite_2x
twox_base_ext = "#{base.sub('@2x', '_2x')}#{ext}" # e.g. hoge_2x.png

# ディレクトリがなければ作る
FileUtils.mkdir_p(twox_dir) unless FileTest.exist?(twox_dir)

# ファイルを移動する
File.rename filePath, "#{twox_dir}/#{twox_base_ext}"

p "#{twox_dir}/#{twox_base_ext}"
p '--'
end
end
end

 

4. CoffeeScript

JavaScripterの間ではすっかり浸透したCoffeeScript を使います。
コードの行数、文字数が減らせるので、その分コメントを充実させられます。

必要なコマンドは
coffee -o assets/js -cw assets/coffee

 

ここまでの内容をまとめたものは、
https://github.com/soohei/compass-coffee-template
にあります。

 

5. Gruntによる自動化

注) 最新の状況はこちら

Gruntはこれまでに書いた、CompassやCoffeeScriptのコンパイル処理、
ソースの結合やMinify、ファイルの監視、簡易サーバーの起動などを
全て一つのコマンドで実行してしまう脅威の仕組みです。
他にGulpというのがあるようなのですが、まだ試していないです..
セットアップ方法は省略します。普段使っている基本的な設定・プラグインの構成は以下。

package.json (使用しているパッケージの一覧)

{
"name": "sample",
"version": "0.0.0",
"author": "Sohei Kitada",
"devDependencies": {
"grunt": "~0.4.5",
// ベンダープレフィックス補完
"grunt-autoprefixer": "~1.0.0",
// メディアクエリーをまとめる
"grunt-combine-media-queries": "~1.0.19",
// CoffeeScript
"grunt-contrib-coffee": "~0.11.0",
// Compass
"grunt-contrib-compass": "~0.9.1",
// ファイルの結合
"grunt-contrib-concat": "~0.5.0",
// 簡易サーバー
"grunt-contrib-connect": "~0.8.0",
// Minify JS
"grunt-contrib-uglify": "~0.5.1",
// ファイルの更新検知
"grunt-contrib-watch": "~0.6.1",
// CSSのプロパティをソートする
"grunt-csscomb": "~3.0.0",
// Minify CSS
"grunt-csso": "~0.6.3",
// シェルコマンドの実行
"grunt-exec": "~0.4.6"
}
}

 

Gruntfile.coffee (タスク)

module.exports = (grunt) ->
# load package.json
grunt.initConfig
pkg: grunt.file.readJSON 'package.json'

compass:
dist:
options:
config: 'config.rb'
outputStyle: 'compressed'
environment: 'production'
dev:
options:
config: 'config.rb'

coffee:
compile:
options:
join: true
files:
'source/js/script.js': ['source/coffee/**/*.coffee']

exec:
mv_sprite:
cmd: 'ruby mv_sprite.rb'

autoprefixer:
options:
browsers: ['last 2 version', 'ie 8', 'ie 9']
default:
src: 'assets/css/style.css'
dest: 'assets/css/style.css'

csso:
default:
src: 'assets/css/style.css'
dest: 'assets/css/style.css'

cmq:
default:
src: 'assets/css/style.css'
dest: 'assets/css/style.css'

csscomb:
default:
src: 'assets/css/style.css'
dest: 'assets/css/style.css'

concat:
jsdefault:
src: [
'source/jslib/**/*.js'
'source/js/**/*.js'
]
dest: 'assets/js/script.js'

license: {
src: [
'source/jslib/_license.js'
'assets/js/script.js'
]
dest: 'assets/js/script.js'
}

uglify:
default:
src: 'assets/js/script.js'
dest: 'assets/js/script.js'

connect:
uses_defaults: {}

watch:
options: # enable livereload
livereload: true
compass: # watch scss files
files: 'source/sass/**/*.scss'
tasks: ['compass:dev']
coffee: # watch scss files
files: 'source/coffee/**/*.coffee'
tasks: ['coffee:compile']
js: # watch js files
files: ['source/js/**/*.js', 'source/jslib/**/*.js']
tasks: ['concat:jsdefault']
image: # watch image files
files: 'source/images/**'
tasks: ['exec:mv_sprite']
html: # watch html files
files: '**/*.html'

# load Grunt Plugins
grunt.loadNpmTasks('grunt-autoprefixer')
grunt.loadNpmTasks('grunt-combine-media-queries')
grunt.loadNpmTasks('grunt-contrib-coffee')
grunt.loadNpmTasks('grunt-contrib-compass')
grunt.loadNpmTasks('grunt-contrib-concat')
grunt.loadNpmTasks('grunt-contrib-connect')
grunt.loadNpmTasks('grunt-contrib-uglify')
grunt.loadNpmTasks('grunt-contrib-watch')
grunt.loadNpmTasks('grunt-csscomb')
grunt.loadNpmTasks('grunt-csso')
grunt.loadNpmTasks('grunt-exec')

# tasks

# defalt
grunt.registerTask('default', ['connect', 'watch']);

# development
grunt.registerTask('dev', ['exec:mv_sprite', 'compass:dev', 'coffee:compile', 'concat:jsdefault']);

# distribution
grunt.registerTask('dist', ['exec:mv_sprite', 'compass:dist', 'autoprefixer', 'cmq', 'csscomb', 'csso', 'coffee:compile', 'concat:jsdefault', 'uglify', 'concat:license']);

return

 

「grunt」の処理内容
・connect
簡易Webサーバーの立ち上げ (プロジェクトによっては使わない)

・ファイルの更新の監視
1) images内が更新されたら…「3. スプライトイメージの自動化」で紹介したRubyコマンドを実行
2) scssが更新されたら…Compassを再コンパイル
3) coffeeが更新されたら…CoffeeScriptを再コンパイル → jsファイルが書き出される
4) jsが更新されたら…JSファイルを一つのファイルに結合

「grunt development」の処理内容
・上記 1),2),3),4)を順番に実行

「grunt distribution」の処理内容
・1)を実行
・2)をdist用の設定 (outputStyle: ‘compressed’) で実行
・ベンダープレフィックスを追加
・メディアクエリーをまとめる
・CSSのプロパティをソート
・CSSをMinify
・3)を実行
・4)を実行
・JSをMinify
・上部にライブラリ関係のライセンスを追加

ここまでの内容をまとめたものは、
https://github.com/soohei/grunt-template
にあります。

 

6. ローカルサーバー

本気で開発する場合は、MAMPでローカルサーバーを立てています。
詳しくは、
MAMP 2 → MAMP 3
MAMPでバーチャルホストの設定 (2種類)
あたりに書かれています。

 

7. その他

最後に、ここまでのワークフローで生まれた、
本番環境にアップ不要のファイルはサーバーに転送しないように、
Transmitを設定しています。

transmit

 

coffeescriptでprivate

あらためて復習。
CoffeeScriptのクラスではprivateな変数・関数はつくれないっぽい。
(継承のオーバーライドを想定しない場合は、できないことはないっぽい →参考欄の1つ目)
最初に比較用のASのコードを載せています。

 

– AS脳で理解するための比較用 ActionScript
→ private var, private function がcoffeeだと使えない

 

package{
class SampleClass{
public static var staticVar:String = "staticVar";
public static var staticVar2:String = "staticVar2";

private static var _staticVar3:String = "_staticVar3";

public var publicVar2:String= "publicVar2";

/* CONSTRUCTOR */
function SampleClass(){
// インスタンスのプロパティ
this.publicVar = "publicVar";
}

// PUBLIC STATIC FUNCTION
public static staticPublicFunction(){
trace("\n**** from staticPublicFunction");
trace(this);
trace(_staticVar3);
trace(SampleClass.staticVar);
trace(publicVar);
trace(publicVar2);
}

/* PRIVATE FUNCTION */
private function _privateFunction(){
trace("\n**** from _privateFunction");
trace(this);
trace(_staticVar3);
trace(SampleClass.staticVar);
trace(publicVar);
trace(publicVar2);
}

// PUBLIC FUNCTION
public function publicFunction(){
trace("\n**** from publicFunction");
trace(this);
trace(_staticVar3);
trace(SampleClass.staticVar);
trace(publicVar);
trace(publicVar2);
}

public function callPrivateFunction(){
_privateFunction();
}

public function updateStaticVar3(){
_staticVar3 = "_staticVar3:changed";
}
}
}

 

– CoffeeScript

 

class SampleClass

### PUBLIC STATIC VAR ###
@staticVar: '@staticVar'
@staticVar2 = '@staticVar2'

###
# PRIVATE STATIC VAR
# クロージャー内の変数扱い? インスタンスの変数ではない
###
_staticVar3 = '_staticVar3'

### PUBLIC INSTANCE VAR ###
publicVar2: 'publicVar2'

###
# ここまでのまとめ:
# ・頭に@がつく場合
# 無条件で静的なプロパティになる
# ・@がない場合
# =を使うと静的なプロパティに、
# :を使うとインスタンス(prototype)のプロパティになる
###

### CONSTRUCTOR ###
constructor: ->
# インスタンスのプロパティ
@publicVar = '@publicVar'

### PUBLIC STATIC FUNCTION ###
@staticPublicFunction = ->
console.log('\n**** from staticPublicFunction')
console.log(@)
console.log _staticVar3 #OK
console.log SampleClass.staticVar #OK
console.log @publicVar #NG
console.log @publicVar2 #NG
@

###
# PRIVATE FUNCTION の出来損ない
# インスタンスのメンバー変数にアクセスできない!
# クロージャー内の関数扱い?
###
_privateFunction = ->
console.log('\n**** from _privateFunction')
console.log(@)
console.log _staticVar3 #OK
console.log SampleClass.staticVar #OK
console.log @publicVar #NG
console.log @publicVar2 #NG
@

### PUBLIC FUNCTION ###
publicFunction: ->
console.log('\n**** from publicFunction')
console.log(@)
console.log _staticVar3 #OK
console.log SampleClass.staticVar #OK
console.log @publicVar #OK
console.log @publicVar2 #OK
@

callPrivateFunction: ->
_privateFunction()
@

updateStaticVar3: ->
_staticVar3 = '_staticVar3:changed'
@

### 1つ目のインスタンス sample をつくって色々テスト ###

sample = new SampleClass()
console.log('\n**** from global')
console.log SampleClass.staticVar #OK
console.log sample.publicVar #OK
console.log sample.publicVar2 #OK
console.log sample._staticVar3 #NG

sample.publicFunction()
sample.callPrivateFunction()
SampleClass.staticPublicFunction()

### _staticVar3を更新する ###
sample.updateStaticVar3()

### 2つ目のインスタンス sample2 をつくって色々テスト ###
console.log '\n#### sample2 ####'

sample2 = new SampleClass()
console.log('\n**** from global')
console.log SampleClass.staticVar
console.log sample2.publicVar
console.log sample2.publicVar2
console.log sample2._staticVar3

sample2.publicFunction()
sample2.callPrivateFunction()
SampleClass.staticPublicFunction()

###
# sample2をつくっての実験のまとめ:
# _staticVar3 が変わってしまっていることからも、
# インスタンス変数ではなく、スタティックな変数であることがわかる。
###

 

– コンパイル結果 JavaScript

 

// Generated by CoffeeScript 1.4.0
(function() {
var SampleClass, sample, sample2;

SampleClass = (function() {
/* PUBLIC STATIC VAR
*/

var _privateFunction, _staticVar3;

SampleClass.staticVar = '@staticVar';

SampleClass.staticVar2 = '@staticVar2';

/*
# PRIVATE STATIC VAR
# クロージャー内の変数扱い? インスタンスの変数ではない
*/

_staticVar3 = '_staticVar3';

/* PUBLIC INSTANCE VAR
*/

SampleClass.prototype.publicVar2 = 'publicVar2';

/*
# ここまでのまとめ:
# ・頭に@がつく場合
# 無条件で静的なプロパティになる
# ・@がない場合
# =を使うと静的なプロパティに、
# :を使うとインスタンス(prototype)のプロパティになる
*/

/* CONSTRUCTOR
*/

function SampleClass() {
this.publicVar = '@publicVar';
}

/* PUBLIC STATIC FUNCTION
*/

SampleClass.staticPublicFunction = function() {
console.log('\n**** from staticPublicFunction');
console.log(this);
console.log(_staticVar3);
console.log(SampleClass.staticVar);
console.log(this.publicVar);
console.log(this.publicVar2);
return this;
};

/*
# PRIVATE FUNCTION の出来損ない
# インスタンスのメンバー変数にアクセスできない!
# クロージャー内の関数扱い?
*/

_privateFunction = function() {
console.log('\n**** from _privateFunction');
console.log(this);
console.log(_staticVar3);
console.log(SampleClass.staticVar);
console.log(this.publicVar);
console.log(this.publicVar2);
return this;
};

/* PUBLIC FUNCTION
*/

SampleClass.prototype.publicFunction = function() {
console.log('\n**** from publicFunction');
console.log(this);
console.log(_staticVar3);
console.log(SampleClass.staticVar);
console.log(this.publicVar);
console.log(this.publicVar2);
return this;
};

SampleClass.prototype.callPrivateFunction = function() {
_privateFunction();
return this;
};

SampleClass.prototype.updateStaticVar3 = function() {
_staticVar3 = '_staticVar3:changed';
return this;
};

return SampleClass;

})();

/* 1つ目のインスタンス sample をつくって色々テスト
*/

sample = new SampleClass();

console.log('\n**** from global');

console.log(SampleClass.staticVar);

console.log(sample.publicVar);

console.log(sample.publicVar2);

console.log(sample._staticVar3);

sample.publicFunction();

sample.callPrivateFunction();

SampleClass.staticPublicFunction();

/* _staticVar3を更新する
*/

sample.updateStaticVar3();

/* 2つ目のインスタンス sample2 をつくって色々テスト
*/

console.log('\n#### sample2 ####');

sample2 = new SampleClass();

console.log('\n**** from global');

console.log(SampleClass.staticVar);

console.log(sample2.publicVar);

console.log(sample2.publicVar2);

console.log(sample2._staticVar3);

sample2.publicFunction();

sample2.callPrivateFunction();

SampleClass.staticPublicFunction();

/*
# sample2をつくっての実験のまとめ:
# _staticVar3 が変わってしまっていることからも、
# インスタンス変数ではなく、スタティックな変数であることがわかる。
*/

}).call(this);


参考:
CoffeeScriptでstatic/private/publicなメンバ/メソッドをもったクラスのつくりかた « DevJamMemo
[改訂]CoffeeScriptでstatic/private/publicなメンバ/メソッドをもったクラスのつくりかた « DevJamMemo
JavaScriptでstatic/private/publicなメンバ/メソッドをもったクラスのつくりかた | ALUMICAN.NET
JavaScript のスコープチェーンとクロージャを理解する – tacamy memo
AcrionScript3やってた自分からみたCoffeeScript | 宇都宮ウエブ制作所

アルミ缶先生さすがっす

hetemlでpjax

定例mtgをしていても、バスに乗っていても、話題はpjaxなので、
pjaxを勉強し直したまとめ。

 

 クライアントサイド

・pjaxをするには、jQueryと、jQueryのpjaxライブラリを使う。

サーバーサイド

・環境はhetemlサーバー
・hetemlのサーバーはApacheなので、PHP (5.3.16)を使う。
・PHP5.4未満なので、getallheaders関数が使えない。

 

最初に読み込むページ (open)

<!DOCTYPE HTML>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>pjax test</title>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
    <script>window.jQuery || document.write('<script src="jquery-1.8.2.min.js"><\/script>')</script>
    <script src="jquery.pjax.js"></script>
    <script type="text/javascript">
            $(function($){
                //時刻を出力
                $('#time').text(new Date().getTime());
                //pjaxのサポート状況を出力
                $('#pjax-support').text($.support.pjax ? 'true' : 'false');
                //リンクの動作
                $('a.pjax').click(function(e){
                    e.preventDefault();
                    $.pjax({
                        url: $(this).attr('href'),
                        container: '#content'
                    })
                });
            });
        </script>
</head>
<body>
    <div id="main">
        <h1>
            pjax test
        </h1>
        <p>
            time: <span id="time"></span><br/>
            pjax-support: <span id="pjax-support"></span>
        </p>
        <div>
            <ul>
                <li><a href="./index.php" class="pjax">top</a></li>
                <li><a href="./page1.php" class="pjax">page 1</a></li>
                <li><a href="./page2.php" class="pjax">page 2</a></li>
                <li><a href="./page3.php" class="pjax">page 3</a></li>
            </ul>
        </div>
        <div id="content">
            hello world.
        </div>
    </div>
</body>
</html>

 

あとから読み込むページ

<?php
$pjax = $_GET['_pjax'];
if (!$pjax) { ?>

<!DOCTYPE HTML>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>page 1</title>
        <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
        <script>window.jQuery || document.write('<script src="jquery-1.8.2.min.js"><\/script>')</script>
        <script src="jquery.pjax.js"></script>
        <script type="text/javascript">
            $(function($){
                //時刻を出力
                $('#time').text(new Date().getTime());
                //pjaxのサポート状況を出力
                $('#pjax-support').text($.support.pjax ? 'true' : 'false');
                //リンクの動作
                $('a.pjax').click(function(e){
                    e.preventDefault();
                    $.pjax({
                        url: $(this).attr('href'),
                        container: '#content'
                    })
                });
            });
        </script>
    </head>
    <body>
        <div id="main">
            <h1>
                page 1
            </h1>
            <p>
                time: <span id="time"></span><br/>
                pjax-support: <span id="pjax-support"></span>
            </p>
            <div>
                <ul>
                    <li><a href="./index.php" class="pjax">top</a></li>
                    <li><a href="./page1.php" class="pjax">page 1</a></li>
                    <li><a href="./page2.php" class="pjax">page 2</a></li>
                    <li><a href="./page3.php" class="pjax">page 3</a></li>
                </ul>
            </div>
<?php } ?>

            <div id="content">

                this is page 1.

            </div>

<?php if (!$pjax) { ?>
        </div>
    </body>
</html>
<?php } ?>

 

2行目の処理は、
getallheaders() が使える環境では

$header = getallheaders();
$pjax = !empty($header['X-PJAX']) && ($header['X-PJAX'] == "true");

としたい。

$pjaxがtrueの時は、if分で色々除外されて、
49-53行目だけがレスポンスとして返る。
受け取った側は、 #content を、新しい #content に置き換える。

 

参考: PHPでpjaxる – 私の昆布

 

ソース一式ダウンロード

hitTest in JS

何かをクリックしたりした時に、それが特定の要素の上からどうかを調べる式のメモ。
ASで言うhitTest的なもの。


$(“外側”).click(function(e) {
e.preventDefault();
$(“当たり判定する要素”).each(function() {
var offset = $(this).offset();
var paddingTop = parseInt($(this).css(“padding-top”).split(“px”).join(“”));
var paddingBottom = parseInt($(this).css(“padding-bottom”).split(“px”).join(“”));
if( e.pageX > offset.left &&
e.pageX < offset.left + $(this).width() && e.pageY > offset.top &&
e.pageY < offset.top + $(this).height() + paddingTop + paddingBottom ){ //当たっている }else{ //当たっていない } }); }); [/javascript]

$(window).mousemove

jQueryでスライダーみたいなドラッグできるUIを作る時、
IE7,8では $(window).mousemove とか $(window).mouseup が動かず、
$(window.document).mousemove、$(window.document).mouseup にしないといけないらしい。

それとドラッグする要素が画像の時、imgタグでもbackground-imageでも問題無いけど、
aタグを使うと、IE7,8でやはり問題有り。(リンク先へのショートカットを運んでしまう)
divを使った方が良い。

あとは、
http://javascript123.seesaa.net/article/100851539.html
にあるように、return false をおまじない的に使う。

 

 

JS (1) : JavaScriptでのクラス定義

■ クラスの定義方法

例えばASだと、このような定義になると思いますが、

package{
	public class MyClass{
		
		public var myValue;
		
		//コンストラクタ
		public function MyClass(value1, value2){
			var newValue = value1 + value2;
			test1(newValue);
		}
		
		public fucntion test1(value3){
			myValue = value3;
			trace("myValue:", myValue);
		}
		
		public fucntion test2(){
		
		}
	}
}


JSではこのように書くと良いと思います。

var MyClass = function(){ 
	this.myValue;
	this.initialize.apply(this, arguments); 
}

MyClass.prototype = {
	//コンストラクタ
	initialize: function(value1, value2) {
		var newValue = value1 + value2;
		test1(newValue);
	},
	
	test1: function(value3){
		this.myValue = value3;
		console.log("myValue:", myValue);
	},
	
	test2: function(){
	
	}
}


説明 :
・JSに変数の型指定はありません。
・この書き方をすると、関数内で定義した変数を除いて、全てpublicになります。クラス内でprivate変数はつくれません。
・イベント処理っぽいことが入って来た時にさらに掘り下げますが、14行目の「this」は必須です。
・console.logはfirebugや、WebKitのコンソールで確認。


■ サンプル

できるだけASっぽく、1クラス1ファイルで考えてみます。
ドキュメントクラス風にMainクラスを実行して、SampleClassクラスのインスタンスをつくり、
その中の関数を呼んで終わり。という簡単なプログラムです。

まずHTMLファイルでmain.jsを読み込む。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
<title></title>
<script type="text/javascript" src="main.js"></script>
</head>

<body>
	<div id="content"></div>
</body>

</html>


main.jsの中身

document.write('<script type="text/javascript" src="sampleClass.js"></script>');

onload = function(){
	var main = new Main();
}

//Mainクラス
var Main = function(){
	this.initialize.apply(this, arguments); 
}
Main.prototype = {
	//コンストラクタ
	initialize: function() {
		var sampleInstance = new SampleClass("Hello World!");
		sampleInstance.talk();
	}
}


sampleClass.jsの中身

//SampleClassクラス
var SampleClass = function(){ 
	this.myMessage = "";
	this.initialize.apply(this, arguments); 
}
SampleClass.prototype = {
	//コンストラクタ
	initialize: function(message) {
		this.myMessage = message;
	},
	
	//this.myMessageを出力
	talk: function(){
		console.log(this.myMessage);
		document.getElementById("content").innerHTML = this.myMessage;
	}
}


説明 :
・main.jsの1行目はASでいうimportっぽい書き方をしてみたくてやっています。
(HTMLをいじらずJSだけで完結できるので便利)
・実際はJQueryのonLoadから処理を始めて行くと色々できるようになります。(次回以降)

ダウンロード :
http://soohei.net/blog/files/js_01.zip


参考 :
http://d.hatena.ne.jp/amachang/20060516/1147778600
http://d.hatena.ne.jp/vividcode/20090706/1246905260

JS (0) :

一年に一度のフェス日記と化していたブログですが、せっかく心新たにJavaScriptを覚えているので、Flashに本格的に足を踏み入れるキッカケとなった緒方さんのFLASH OOP 最終章のような素敵な内容を目指します。

■ Flashを覚えた時のように、JSを覚える

・(1) JavaScriptでのクラス定義
・(2) JSONデータの読み込み
・(3) 表示部分を準備
・(4) スライドショーのプログラム
・(5) アニメーションをつける

ActionScriptを使える人が脳内で仕組みを置き換えながら理解できるようにします。ベストな実装方法かはわかりません。逆に教えて頂きたい。今日から少しずつまとめます。