AngularJS jqLiteオブジェクト

AngularJSには, jQueryの主要なメソッドを実装したオブジェクトである, jqLiteオブジェクトを生成するメソッドがあり, jQueryを利用しなくても, ある程度簡単にDOM操作をすることが可能になります (もっとも, AngularJSを利用する場合, DOM操作をする必要性は限られてきますが…).

// DOMから生成
var elements = angular.element(document.querySelectorAll('body > div'));
// HTML文字列から生成
$scope.elements = angular.element('<div></div>');

ただし, jqLiteという名前がついていることからも想像できるように, jQueryのメソッドすべてを備えているわけではありません.

また, 同じメソッド名でも引数の指定ができないなど注意点もあります. このあたりは, リファレンスなどを参照するのがいいでしょう.

そして, どうしてもjqLiteでは役不足な場合は, 断念して素直にjQueryを読み込み利用するのがベターでしょ.

jQueryプラグイン作成のテンプレート

jQueryのプラグインといっても, 大きくはクラスメソッド型とインスタンスメソッド型に分類されます.

作成方法としては, クラスメソッド型のほうが簡単なのでまずはそちらのテンプレートから

クラスメソッド型

クラスメソッド型とは, 具体的には, $.ajaxや$.eachなど, DOMに依存しないメソッドのことです.

(function($) {
    $.myplugin = function() {
        // プラグインの処理
    };
})(jQuery);

以上がクラスメソッド型のテンプレートです.

インスタンスメソッド型

インスタンスメソッド型とは, 具体的には, htmlやaddClassなど, DOMに依存するメソッドのことです.

インスタンスメソド型のテンプレートはクラスメソッドよりは少々複雑になります.

(function($) {

    // プラグイン本体
    $.fn.myplugin= function() {
        // プラグインメソッド内のthisは,
        // セレクタのDOMではなく, 
        // セレクタのjQueryオブジェクト
        var self = this;
        // プラグインメソッドのハッシュ
        var methods = {
              // プラグインの初期化処理
             create : function(options) {
                 // オプションをマージ
                 self.fn.myplugin.settings = $.extend(true, {}, self.fn.myplugin.defaults, options);

                  // セレクタで選択した要素が複数ある場合を考慮して,
                  // 初期化処理をイテレーションする
                  // いわゆる, コンポジットパターンを実現するために
                  // 必要な処理
                  // また, jQueryのメソッドと同様に,
                  // メソッドチェーンで記述可能なように, 
                  // (ゲッターでなければ) this参照を返す
                  return self.each(function(index, element) {
                      // プラグインの初期化処理
                  });
             },
             // オプションのゲッター・セッター
            option : function(key, value) {
                 if (value === undefined) {
                     // ゲッター
                     return self.fn.myplugin.settings[key];
                 } else {
                     // セッター
                     self.fn.myplugin.settings[key] = value;
                     return self;
                 }
            }
             // 初期化処理以外のメソッドが必要な場合
             something : function() {
                 // do something ...
             }
        }

        if (String(arguments[0]) in methods) {
            // 初期化以外のメソッド
           methods[String(arguments[0])].apply(this, Array.prototype.slice.call(arguments, 1)); } else if ($.isPlainObject(arguments[0]) || (arguments[0] === undefined)) { //初期化処理 methods.create(arguments[0]); } else { // 引数エラー $.error(); } }; $.fn.myplugin.defaults = { // デフォルトのセッティングのハッシュ }; // プラグインユーザーが指定したオプションとデフォルトのオプションをマージした結果を格納するハッシュ. $.fn.myplugin.settings = {} })(jQuery);

クラスメソッド型 / インスタンスメソッド型共通の重要なポイントは,

  • 名前空間の汚染をできるだけ防ぐためにクロージャを使う
  • クロージャの実引数には, jQueryオブジェクトのエイリアスである$ではなく, jQueryのほうを渡します (noConflictメソッドが実行されてもプラグインを利用可能にするため). ただし, クロージャ内部では, タイプ量を少なく, かつ, jQueryオブジェクトだとわかりやすいように, $で参照します.

aタグのdownload属性

aタグのdownload属性を利用すると, 例えば, リンク先 (href属性) にファイルを指定していても, ダウンロードのダイアログを出すことが可能になります.

つまり, わざわざサーバー側にリクエストを送って, Content-Disposition: attachment; のレスポンスヘッダーを返す必要がなくなります.

// download属性がないと, 別タブでファイルが開いてしまいます.
<a href="http://xxx/sample.pdf">PDFダウンロード</a>

// しかたがないので, 一度サーバーサイドにリクエストを送り,Content-Disposition: attachment; のレスポンスヘッダーを返す…

download属性を利用すれば, 簡単にダウンロードダイアログを出すことが可能になります.

<a href="http://xxx/sample.pdf" dowload="sample.pdf">PDFダウンロード</a>

ちなみに, download属性にはダウンロード時のファイル名を指定します (Content-Disposition: attachment; filename=”ファイル名” と同じ要領です) .

欠点としては, IEなど一部のブラウザは対応していないことです. その場合は属性指定していない場合と同様に, 別タブでファイルが開きます. まあ, 最低限のアクセシビリティは確保できるのである程度利用環境が限定できる (IEは対応していないHTML5 APIを利用したWebアプリケーションなど) のであれば, 積極的に使いたいですね.

Object.observeを試してみた

Chromeでは既に実装されているObjectクラスのobserveメソッド. AngularJSで言うところの, $scope.$watchのようなメソッドです.

// 監視対象のオブジェクト
var obj = {};

// オブジェクトの監視
Object.observe(obj, function(changes) {
    // 監視対象のオブジェクトが変更された場合に実行されるコールバック関数

    // 第1引数は変更情報を格納したオブジェクトの配列
    changes.forEach(function(element) {
         console.log(element.name);  // -> 変更されたプロパティ名
         console.log(element.type);  // -> 追加なら'add', 更新なら'update', 削除なら'delete'
         console.log(element.oldValue);  // -> 変更前の値
    });
});

プロパティの追加

obj.a = 'add';

// 出力
// a
// add
// undefined

プロパティの更新

obj.a = 'update';

// 出力
// a
// update
// add

プロパティの削除

delete obj.a;

// 出力
// a
// delete
// update

Object.observe()

git / GitHub masterブランチの削除

GitHubのリポジトリで, Webサイトだけ欲しい場合, つまり, gh-pagesのブランチのみが欲しい場合, masterブランチは削除しておきたい場合も多々あると思います.

ローカルリポジトリ, つまり, gitでmasterブランチを削除するのは簡単です.

まずは, masterブランチ (削除したいブランチ) 以外のブランチにチェックアウトします.

$ git branch
* gh-pages
   master

あとは, branchのd (delete) オプションを利用して削除するだけです.

$ git branch -d master

次は, GitHub (リモートリポジトリ側) のmasterブランチを削除します.
まず, GitHubの対象リポジトリのsettingsメニューでデフォルトのブランチをgh-pages (あるいは, 削除対象のブランチ以外) に設定しておきます.

GitHub settings
GitHub settings
GitHub settings Default branch
GitHub settings Default branch

あとは, ローカルリポジトリからGitHubに対してmasterブランチを削除するように指示する必要があります.

$ git push origin :master

 

このコマンドでGitHubのmasterブランチが削除されます.
ちなみに, このコマンドの意味は,

$ git push [プッシュ先のリポジトリ] [ローカルのブランチ]:[リモートのブランチ]

対応関係は以下のようになります.

  • [プッシュ先のリポジトリ] -> origin
  • [ローカルのブランチ]            -> 空のブランチ
  • [リモートのブランチ]            -> (GitHubの) masterブランチ

つまり, ローカルの空のブランチを, (GitHubの) masterブランチにシンクロさせることによって, GitHubのmasterブランチを削除しているわけですね.

参考 : 復習 Git: GitHub のブランチを削除する.

jQueryプラグイン フリップスイッチを作成してみた

jQueryのプラグインでフリップスイッチを検索すると, どうしてもjQuery Mobileのフリップスイッチばかりヒットしてしまい….

もしかしたら, 意外とjQueryプラグインとしてのフリップスイッチでないのかな (まあ, jQuery UI Sliderをむりやりフリップスイッチにできなくはないですが…)〜と思ったので自分で作成しました.

jQuery プラグイン フリップスイッチ

フリップスイッチとしては, とりあえず最低限の機能を実装下という感じなので, 今後もバージョンアップ (オプションやイベント, メソッドの追加) していく予定です.

余力があれば, Web Componentsとしても実装したいと思っています (まあ, Web Componentsではすでに誰かが作成してそうですが…).

Sass でユーティリティCSSを作成してみた

Sassの第2作目ということで, CSSで汎用的に利用するスタイル (clearfix, オフレフト, 不可視化, remのフォールバック設定) や, ベンダープレフィックスの変数定義とCSS3のプロパティ一括設定, animation の keyframes の定義などのためのSasです.

  • ネスト
  • 変数と演算
  • 制御文
  • 継承
  • ミックスイン
  • ビルトイン関数 / 自作関数

など, Sassの基本的な機能はだいだい利用した感じになっています. 前回に紹介した, リセットスタイルのためのSassと合わせて, importする側のCSSはこんな感じです.

このSassをコンパルしたCSSを利用したテスページはこちらです.

AngularJS 外部モジュールによるビューの更新

AngularJSでは, 双方向データバインディングによって, モデルを更新すれば即時にビューも更新されます

ところが, AngularJSが管理していない外部モジュールのイベントでモデルを更新しても即時にビューが更新されません. 更新されるのは次にAngularJSが管理しているモジュールでモデルを更新した場合です.

$scope.value = 0;

//jQuery UI Slider
$('#slider').sider({
    slide : function(event, ui) {
        //Update Model -> Not Update View ...
       $scope.value = ui.value;
    }
});

このままでは, jQuery UIやjQuery プラグインなどサードパーティ製モジュールが使いづらくなってしまいます.

しかしAngularJSにはこれを解決する手段が2つ定義されています.

  • $scope.$applyメソッドを利用する
  • $timeoutサービスを利用する
$scope.value = 0;

//jQuery UI Slider
$('#slider').sider({
    slide : function(event, ui) {
        $scope.$apply(function() {
            //Update Model -> Update View
            $scope.value = ui.value;
        });
    }
});
$scope.value = 0;

//jQuery UI Slider
$('#slider').sider({
    slide : function(event, ui) {
        $timeout(function() {
            //Update Model -> Update View
            $scope.value = ui.value;
        }, 0);
    }
});

$timeouサービスも内部的には$scope.$applyを利用しているので, 根本的にはどちらの方法も同じです.

しかし, $scope.$applyでは, 別のイベントによって更新処理が実行されているときに$scope.$applyメソッド呼び出しがあり, 更新処理が多重実行されてしまうと,

Error : {$rootScope:inprog] $digest already in progress

というエラーが出てしまいます.

$timeoutサービスではタイムアウト時間を0ミリ秒に設定することで, 更新処理の多重実行を回避して$scope.$applyを実行します.

SassのStyleコンパイルオプションをまとめてみた

Sass Styleコンパイルオプション
Option Description Command
Nested Sassの階層構造 (インデント) を残してコンパイル. これはデフォルトのSytleオプションでもある sass input.scss:output.css –style nested
Expanded ルールセットとプロパティを1行ずつ改行したスタイル (Dreamweaverのコードフォーマットに似ている) sass input.scss:output.css –style expanded
Compact セレクタとプロパティが1行で記述される. ルールセットごとに改行が入る. sass input.scss:output.css –style compact
Compressed いわゆる圧縮スタイル. インデントや改行, /*! で始まるコメント以外はすべて削除される sass input.scss:output.css –style compressed