「CSS」と一致するもの

突然ですが、angular.jsを使うときは、jquery.jsを読み込まないことをおすすめします。

angular.jsとjquery.jsはコンフリクトはしません。ライブラリ的には共存します。しかし、簡単に言えば、angular.jsを使う場合は、jquery.jsを使う必要がないのです。jquery.jsを読み込まない場合、angular.jsはjqLiteというサブセットを使います。ちゃんと検証したわけではありませんが、コードを眺める限りではjqLiteは軽量そうです。

jquery.jsを読み込まないほうがいい理由は軽量化よりも、マインドの問題の方が大きいです。angular.jsのコーディングスタイルとDOM操作は合いません。angular.jsでは、$scopeを使ってDOMをコンパイル(という用語でいいのかな)させればいいのです。こっちの方がパワフルでシンプルです。angular.jsを使いつつDOM操作するというのは、車に乗っているのに自転車を漕いでいるような感じです。ちょっと違うか。

というわけで、jquery.jsを読み込むからDOM操作をしたくなるわけで、それなら読み込まなければいいのです。と思っていつつ、ところが、既存ライブラリの多くはjquery.jsに依存していたりするのです。angular.jsでは、そういうときは、directiveにしてwarpするようにということのようですが、そうすると、jquery.jsは読み込むことになってしまいます。それでもいいのですが、なんか負けた気がします。

jquery.jsを使わないほうがいいと思っている人は、きっと他にもいるはず、ということで検索しました。

http://joelhooks.com/blog/2013/07/27/using-angularjs-stop-using-jquery-as-a-crutch/

そうですよ、乗り換えるならすっぱりと乗り換えましょうよ。

stackoverflowへのリンクがありました。

http://stackoverflow.com/questions/14994391/how-do-i-think-in-angularjs-emberjsor-other-client-mvc-framework-if-i-have-a/15012542#15012542

Voteが3200を超えています。初めて見ました、そんな投稿。

Don't even use jQuery. Don't even include it. It will hold you back.

いいこと書いてあります。

同じ人のメールでの書き込み(上記ブログでも言及されている)では、

You can wire up some callbacks and $apply calls to make a jQuery plugin work but as Pawel said, rewriting something in AngularJS often takes less work. jQuery doesn't have any of the binding or scope magic. When we cut out all of the jQuery code that makes up for that, we're often left with very little code. And when we put those few lines of code in an AngularJS directive, everything will work out of the box. So in balancing levels of effort, rewriting makes sense more often than it doesn't.

と、あります。書き直しちゃったほうが結果的に簡単ということですね。同じことをやるのに大したコーディングの量はないということと、テストできるコードになるということがメリットのようです。トータルでみたら生産性があがるということでしょう。

これからは、angular.jsを読み込むときは、jquery.jsを読み込まないようにしようと思います。これまでもそうしてきたのですが、さらにがんばって、jqueryライブラリを移植するとかですね。CSSは再利用できるでしょう。

ところで、jqueryベースのライブラリ資産ってどれくらいあるんでしょうか。それと同レベルのangularライブラリをそろえるのは至難の業でしょうね。

http://ngmodules.org/

というサイトがあるようです。今後の発展に期待します。

以前の記事で紹介した、 connect-cache-manifestですが、 実は自分でちゃんと使ったことがありませんでした。

今回、一通り動くところまで使ってみたので、参考までに紹介します。 Expressと Jadeを使う前提です。

多くの場合、ディレクトリの構造は次のようになるのではないでしょうか。

|-- app.js
|-- public
|    |-- js
|    |    |-- foo.js
|    |    |-- bar.js
|    |-- css
|    |    |-- foo.css
|    |    |-- bar.css
|    |-- images
|         |-- foo.png
|         |-- bar.png
|-- views
     |-- xxx.jade
     |-- yyy.jade

このような場合での、connect-cache-manifestの設定は次のようになります。

var express = require('express');
var cacheManifest = require('connect-cache-manifest');
var app = express();
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(cacheManifest({
  manifestPath: '/application.manifest',
  files: [{
    dir: __dirname + '/public',
    prefix: '/static/'
  }, {
    file: __dirname + '/views/xxx.jade',
    path: '/html/xxx.html'
  }, {
    file: __dirname + '/views/yyy.jade',
    path: '/html/yyy.html'
  }],
  networks: ['*'],
  fallbacks: []
}));
app.use('/static', express.static(path.join(__dirname, 'public')));
app.get('/html/:name', function(req, res) {
  res.render(req.params.name);
});

これで、/application.manifestが生成されるようになります。 使ってみて不満が二つありました。一つは、emacsやvimで*~のバックアップファイルができた場合にそれもマニフェストに入ってしまうこと、もう一つは、jadeファイルが複数ある場合にリストアップしなければならないことです。そのうち気が向いたら改良しようかな。

これで、HTML5のマニフェストを使いつつ、ほとんど気にせずに普通にコーディングができます。

そうそう、ついでにもう一つ。ファイルを新規に追加したときは、nodeを再起動しないといけません。これは、node-supervisorやnodemonを使えば解決できるかもしれません。試していませんが。

nodeで、というかexpressで、より正確にはconnectで、Railsのアセットパイプラインのようなことをする方法についてです。

意外と知られてないようなのですが、 connect-assets というモジュールがあります。

npm install connect-assets

でインストールできます。 Railsに慣れている人はいいのかもしれませんが、ちょっとexpressっぽくないというかconventionがあって戸惑うかもしれません。 個人的にはわかりにくいと思いました。 connect-cachifyの方がAPIとしては分かりやすい気がします。

さて、簡単な使い方です。expressには慣れているものとします。

var assets = require('connect-assets');
...
app.use(assets());

このオプション無しデフォルトだと、assetsディレクトリにjsファイルやcssファイルを置くことになります。さらに、jadeファイルに

!= css('hoge')
!= js('hoge')

と書いておくと、それぞれ、assets/hoge.cssとassets/hoge.jsが読み込まれることになります。

一応これだけなのですが、concatenationは直接はサポートされていなくて、lessのimport機能やjsの snoketsを使う必要があります。ここがややこしい。

具体的には、assets/hoge.lessに

@import 'foo.css';
@import 'bar.less';

のように書いたり、assets/hoge.jsに

//= require foo.js

のように書いたりするようです。統一感ないですね。

NODE_ENV=productionの時はminifyしてくれます。これは便利。

現状不満があるconnect-assetsですが、v3 branchの開発が進められているようです。クリーンに書き直したり、依存ライブラリを減らす計画のようですので、期待できそうです。

jsdomへのpull request: XHRサポート

  • 投稿日:
  • by

connect-prerendererをちゃんと動かすためにやったjsdomの修正その2をpull requestにしました。

https://github.com/tmpvar/jsdom/pull/654

実はXHR自体は、 node-XMLHttpRequest をそのまま使うだけでほとんど苦労はありませんでした。

大変だったのは、クッキーを引き継ぐところでした。 そもそもXHRを使わない場合も、jsdomはクッキーの引継ぎをサポートしていませんでした。XHRを使わない場合というのは、JavaScriptやCSSや画像ファイルなどを読み込む場合です。

まず、JavaScriptのロード時にもクッキーを引き継ぐようにするコーディングをしてから、XHRでもクッキーを引き継ぐようにしました。XHRはnode-XMLHttpRequestの内部のコードまで理解しなくてはならず、苦労しました。ちょっと強引に実装したため、もしかしたら将来のバージョンのnode-XMLHttpRequestでは動かないかもしれません。

pull requestは無事マージされて、jsdom v0.8.1がリリースされました。興味がある方はお試しください。

https://npmjs.org/package/jsdom

connect-prerenderer もこのバージョンを使うように修正しました。

https://npmjs.org/package/connect-prerenderer

普段、Webアプリを開発するときはPCでChromeやFirefoxを使うのですが、その後iPhoneのMobile Safariで表示させると、Chromeと比べてレイアウトがずれることがありました。

ボックスの高さをemで指定してもそれに収まる行数が変わってしまうのでなぜかと思っていたのですが、やっと分かりました。

行間のデフォルト値が違うんですね。まあ、当たり前というかなんと言うか。 というわけで、CSSでline-heightを指定することで解決しました。

以前紹介した、 InkというCSSフレームワークを使ってたのですが、<p>を使うとline-heightが指定されるので、気付くまでに時間がかかりました。<div>で書いていたところだけサイズが違っていたので。

それだけですが、備忘メモとして残しておきます。

AngularUIのui-calendarを使ってみた

  • 投稿日:
  • by

AngularUIにCalendarというサブプロジェクトがあるので、使ってみました。

https://github.com/angular-ui/ui-calendar

これは、FullCalendarというjQueryプラグインをAngularのディレクティブ化するもののようです。 ちょっと依存ライブラリが多くて使うのをためらったのですが、他に良い候補も見つからず、AngularUIに入っているのだからうまくラップされているのだろうということで。

READMEを読むと、bowerを使って依存関係を管理せよと書いてあるのですが、まだ理解していないものを使いたくなかったので、手動で設定しました。

分かってしまえばそれほど難しいものではありません。次のものを読み込めばいいだけです。

  • fullcalendar.css
  • jquery.min.js
  • jquery-ui.min.js
  • jquery-ui-i18n.min.js
  • angular.min.js
  • angular-ui.min.js
  • fullcalendar.js
  • calendar.js

必要であればjQueryUIのテーマのcssも読み込みましょう。

さて、ui-calendarを使うとng-modelが使えるようです。

<div ui-calendar="calendarOptions" ng-model="eventSources">

READMEにはこのように書いてありましたが、ここで、はまりました。ng-modelはeventSourceオブジェクトを指定するのではなく、events配列を指定するようです。READMEがソースの改変に追いついていないようです。もっと言うと、READMEではng-modelはオプション扱いですが、書かないとエラーになりました。

実際にevents配列を使ってみるのはこれからです。


追記。

ng-modelに指定するのは、events配列ではなく、eventSources配列であってました。でも、eventSourceオブジェクトの配列ではありません。READMEはeventSourceオブジェクトのリンクを指しているので分かりにくいです。

最近、connect-prerendererのIssueを報告してもらって、少しずつ不具合が見つかっては修正しています。

うまく解決したのは、

の二つ。前者は、

<div>
  count={{count}}
  <span>hoge</span>
</div>

のように、TextNodeとHTMLElementが並んでいるときに、 ng-bind-templateで上書きしてしまう問題でした。

<div>
  <span>count={{count}}</span>
  <span>hoge</span>
</div>

とコンパイル時になるように、Angular.jsをいじりました。 ただ、CSSがそれを想定しない書き方だと、表示に不具合がでるかもしれません。

後者は、doctypeがjsdomで消えている問題でした。こっちはすぐに直りました。

https://github.com/dai-shi/connect-prerenderer/issues/3 がなかなか手ごわく、まだ解決の糸口が見つかりません。 以前書いた、jsdomからAngularJSの不具合二つ目の問題です。

なんにしても、興味を持ってくれる人が増えるのはうれしいことです。 connect-prerendererはほとんど宣伝していないのに、Star数が増えてきました。それはそれで不思議。

突然ですが、Markdownでプレゼンスライドを作りたいと思いました。LibreOffice Impressを使うのは楽しくありません。最近使い慣れてきたMarkdownでさらっと作りたいです。そう、このブログもMarkdownで書いています。

Markdownで手軽に書く代わりに、見た目はそれなりに見えるものがいい、ということで、最近流行り(?)のHTML/CSSのプレゼンツールを調べてみました。

よさそうだなと思ったのは、

です。

プレゼンをmarkdownで書いたらええやん

にmarkdown2impress.plというのが紹介されています。Markdownを変換してHTMLにするようで、スライドの位置座標の指定にも対応しているようです。

さらに、

markdown2impress.plでついでにpdfも出したらええやん

という別のブログでPDFに出力する拡張が紹介されています。

これでやりたいことがすべてできると思って喜んでいたのですが、Reveal.jsを見たらもっとすごかった。

  • Markdownをそのまま読み込める
  • PDFの出力も標準サポート(Chrome only)

それだけでなく、Perl依存なしになるし、使ってみたところimpress.jsよりReveal.jsの方が動作が軽い気がしました。 impress.jsの方が表現力は豊かだと思いますが、もともとの発想がMarkdownで楽したいというものなのでReveal.jsを使うことにしました。

Reveal.jsを使う時はMarkdownを読み込むためのHTMLが必要です。READMEを読めば分かると思いますが、参考までにサンプルを書いておきます。

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>slides</title>
    <link rel="stylesheet" href="css/reveal.min.css">
    <link rel="stylesheet" href="css/theme/default.css" id="theme">
    <script>
        document.write( '<link rel="stylesheet" href="css/print/' + ( window.location.search.match( /print-pdf/gi ) ? 'pdf' : 'paper' ) + '.css" type="text/css" media="print">' );
      </script>
  </head>
  <body>
    <div class="reveal">
      <div class="slides">
        <section data-markdown="content.md" data-separator="^\n\n\n" data-vertical="^\n\n"></section>
      </div>
    </div>
    <script src="lib/js/head.min.js"></script>
    <script src="js/reveal.min.js"></script>
    <script>
      Reveal.initialize({
        dependencies: [
          { src: 'plugin/markdown/showdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
          { src: 'plugin/markdown/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
        ]
      });
    </script>
  </body>
</html>

こんな感じでHTMLファイルを作っておいて、あとはcontent.mdを書けばそのままスライド表示されます。気をつけることは改行3つで区切ることくらいです。べんり~。

Bootstrapもいいのですが、ちょっと巨大すぎるので、代替品がないかなと思って探しました。

要件は、

  • スマホでも使えるようにレスポンシブ対応
  • あまりできることは多くなくてよい、ボタンとかフォームとかの見た目がきれいになればよい
  • できれば、CSSだけでJavaScript不要のフレームワークがよい

といったところです。

色々探しましたが、有名どころでJavaScript不要と言う要件を満たすものはあまりなく、結局見つけたのがこれです。

レスポンシブ対応のサイトをサクッと作れるフレームワーク『InK』

http://ink.sapo.pt/

この「InK」というネーミングですが、何が困るって、検索キーワードとして使えないことです。Google検索すると、"do you mean link?"とかなってしまう。そんなわけで、使用感などのレポートが見つからなかったです。どれだけ有名なのかもよく分かりません。

また、日本だと、

http://d.hatena.ne.jp/keyword/InK

と混同しそうです。

さて、話を戻すと、Inkは要件を満たしています。全体的な設計がシンプルで、CSSだけで構成されていていい感じです。早速ちょっと使ってみて感じたのは、レイアウトの指定が面白いということです。Bootstrapだと12 spanでfixedとfluidがありますが、Inkだとパーセント指定です。また、fixedはなくてfluidだけのようです(未確認)。fixedも使いたいケースがあるので、それは残念なところです。で、面白いのは、「Multiple Layouts」というレイアウトの指定方法です。これは、CSSのメディアクエリを3通り用意して、それぞれのレイアウトをCSSのクラス指定で定義するのです。

うまく説明できないので、サンプルを。

<div class="ink-l50 ink-m50 ink-s100">
  エリア1
</div>
<div class="ink-l50 ink-m50 ink-s100">
  エリア2
</div>

このようにすると、大画面と中画面では、エリア1とエリア2が横に並んで2カラムのレイアウトになり、スマホなどの小画面では、エリア1とエリア2が縦に並んで1カラムのレイアウトになります。ちなみに、大画面、中画面、小画面が、それぞれ、"l", "m", "s"に対応します。

あと、Bootstrapで困っていたレイアウト時のスペース追加も、Inkではink-vspaceとink-gutterで解決しそうです。もっとも、細かい制御をするときは結局CSSを書くことになりそうですが。

とりあえずは、レイアウトだけ試して満足しました。

RSS Pipesの一機能として、スマホ向けのRSSリーダーみたいなものを作ってみようかと思います。

まずは、KineticJSの勉強をしているところです。 HTML5 Canvas KineticJS Tutorials を参考にしながら作っていきます。

HTMLのコードを説明します。

初めに、KineticJSの読み込みます。

<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.4.2.min.js"></script>

試しに使う場合はこのままでいいでしょう。デプロイするときはローカルに持ってこようと思います。

次に、自分のスクリプトを読み込みます。

<script src="test.js" defer="defer"></script>

deferを指定しておくことで、HTMLのロード後に読み込まれます。

空のbodyにIDを指定しておきます。

<body id="container"></body>

こうしておくことで、KineticJSのstageをbodyの直下に作ります。

さて、ちょっとだけCSSが必要です。

html, body {
  margin: 0;
  padding: 0;
  overflow: hidden;
}

これを指定しないと、Canvasの周りにマージンができてしまって、Full Canvasになりません。また、overflowを指定しないと、ChromeやFirefoxでスクロールバーが出てしまいました。

続いて、本題のJavaScriptのコードを説明します。

KineticJSのステージを作ります。

var stage = new Kinetic.Stage({
  container: 'container',
  width: window.innerWidth,
  height: window.innerHeight
});

widthとheightを表示領域一杯にすることで、Full Canvasとなります。

レイヤーを作ります。

var layer = new Kinetic.Layer();

表示領域一杯の四角形オブジェクトを作ります。

var rect = new Kinetic.Rect({
  x: 0,
  y: 0,
  width: stage.getWidth(),
  height: stage.getHeight(),
  fill: 'black'
});

黒にしてみました。

Hello Worldの文字オブジェクトを作ります。

var text = new Kinetic.Text({
  x: stage.getWidth() / 2,
  y: stage.getHeight() / 2,
  text: 'Hello World!',
  fontSize: 30,
  fontFamily: 'Arial',
  fill: 'lightgray'
});

ステージのサイズから計算して中央に配置しています。x,yは文字オブジェクトの左上の座標です。そこで、ちょっとずらして文字オブジェクトの中心に合わせます。

text.setOffset({
  x: text.getWidth() / 2,
  y: text.getHeight() / 2
});

最後に、四角形オブジェクトと文字オブジェクトをレイヤーに追加して、そのレイヤーをステージに追加して、終わりです。

layer.add(rect);
layer.add(text);
stage.add(layer);

これで、Canvasのみを使ってHello Worldを表示するページを作ることができました。アプリと呼べるようになるまでにはまだまだかかりそうですね。

実行結果はこんな感じです。

コードはGistにアップしておきました。 https://gist.github.com/dai-shi/5404537