Code

2014年7月26日土曜日

cross-browser IE8 ボタン クリック問題

 最近仕事が変わりましたので、IE8 も対応ブラウザになりました。。。
 以前から、出て来た IE8 ボタンのクリック問題をまとめておきます。
1 まずは、radio ボタンをクリックしたら、何か動作を自動的に実行する場合、IE8 だと、クリックだけでは onchange イベントハンドラーが呼び出されない。これは、radio ボタンを focus を失うとき、onchange が trigger されます。。。まぁ、理解できますけど。。。
 直し方としては、radio ボタンにもう一個の onclick イベントハンドラーをバインドします。そのイベントハンドラーに
    $("radio button の selector").focus().blur()
を実行します。これで、onchange が自動的に呼ばれるようになります。

2 display:none のボタンの label をクリックしても、ボタンの状態が変わらない。。。
 これは display:none だから。直す方法が二つあります。display:none をやめて、opacity を 0 に設定したり、position を absolute に設定して、top: -9999px, left:-9999px を設定したりすることで、ボタン要素が見えないが、label のクリックは効くようになります。
 もう一つは Label に click ハンドラーをバインドして、もし label が click したら、for attribute で設定したボタンの click 関数を呼び出します。

 それでは。

2014年7月24日木曜日

WCF + Angular で Sever と Client の Decouple

 最近、C# ASP.NET に突入。Back end は C# ASP.NET で、Front End は主に AngularJS を使います。以前 Microsoft の Web Application サンプルはほとんど knockout.js を使っていたため、古いシステムはほとんどそれでした。が、AngularJS から Dependency Injection などのデザインパタンが導入されて、Unit Test や End to End Test が簡単にできるようになったので、だんだん AngularJS に移行する動きがあります。
 今のプロジェクトも移行中です。新しい Web Site は全部 Responsive Design を使っていて、AngularJS ベースに作っています。ただ、やはり cshtml の中に C# Razor のコードと HTML コードがごちゃごちゃになって、みにくいです。それで、WCF を使って、JSON オブジェクトを Front End に返して、View に関してのすべてのロジックは AngularJS をベースに JavaScript で実装することはいいような気がしてます。
 まず、Action を WCF の End Point に変更して、Model はそんなに大きくないので、$http を使って、AJAX でロードして、その後、もう Front End の世界です。新しいブラウザだと、もっと速いような気がしています。サーバーの負荷を考えると、もっと低いような気がしてます。なぜなら、View の parse が必要なくなり、すべて C# コードになるからです。 Ajax コールが多くなりますが、データを組み立てるだけなら、全然軽いと思います。
 Front End でも、Dependency Injection を使っているので、サーバーと隔離するできるし、サーバー側もデータをメインでテストできるから、すべてはスムーズに分けるようになります。Decouple も MVC だけでなく、Server と Client の間でもできるようになります。
 これは多分いいことです。次回の会議でちゃんと話しましょう。
 それでは。

2014年7月19日土曜日

画像を真ん中にする -> CSS or JavaScript

 以前発覚した一つの問題ですが、タブレットで Ads の画像をスクリーンの真ん中にできるだけ大きく表示するには、CSS でやるか、JavaScript で動的に設定するか。。。
 CSS の場合、下記のように HTML を書いて
  <div id="container">
    <div id="wrapper">
        <div>何かのコンテンツ or img タグ</div>
 </div>
  </div>
  #container には {display: table; height:100%; margin: 0 auto;} を設定して、#wrapper に {display: table-cell; vertical-align: middle; } スタイルを設定すれば、真ん中に表示されるはずです。-> Body の height も 100% に設定する必要がある。
    サンプル
 画像の場合、さらに width と height を設定する必要があるが、基本的に width を 100% にして、height を auto に設定すれば、同じ ratio で表示される。
 
 ただ、タブレットだから、orientation を変更する場合、height に合わせる場合があるので、考えた結果、JavaScript で画像の naturalWidth と naturalHeight を取得して、スクリーンサイズを比較すれば、naturalWidth / width, naturalHeight /  height の大きい方に合わせて、さらに position を relative に設定して、top と left を (width - img.offsetWidth) / 2 , (height - img.offsetHeight) / 2 に設定する必要がある。もちろん body の width と height は 100% を設定する必要がある。

 実際比べてみれば、多分 JavaScript のほうがやりやすいと思う。処理は簡単だから、jQuery を使う必要もないし、全部 native の JS で実現できる。
 注意すべきところは Android は resize で、iOS は orientationchange イベントをそれぞれハンドラーをアタッチする。
 var eventStr = navigator.userAgent.match(/iPhone|iPad|iPod/i) ? 'orientationchange' : 'resize';

 それでは。

2014年7月15日火曜日

AngularJS Scope と $apply

 今日時間ができたので、Angular の動きをちょっと追ってみました。module の登録は一つの難しいところですし、injector という概念も難しいと思います。ただ、実際使うとき、Scope というのはかなり重要です。
 基本的にAngularJS は Compile 段階で、必要に応じて、Scope を作ります。さらに ng-scope というクラスを該当要素に追加されます。Scope を取得するには、angular.element(el).scope() で scope object が取得できます。ng-app の要素に対して、rootscope が作られます。
 Scope は一つの object ですので、その prototype 中に、$apply という関数はあります。これはどういうとき使うかというと、Angular の世界に何かイベントを引き起こす時使います。イベントを引き起こすというのは model を変更したり、メッセージを $emit, $broadcast したりする場合です。
 さらに遡ると、JavaScript の中に、イベントハンドラーは、jQuery の proxy などの bind 関数を使わない限り、基本的に window Scope で実行されるので、window Scope は Angular の Scope と違うため、単純にその中のでデータを修正したりすると、Angular の $digest 処理が実行されません。そうすると、$watch のデータのリフレッシュなどは Angular が検知できなくて、画面のリフレッシュもできません。
 一つの例として、もし directive の link 関数で、要素に何かイベントハンドラーを bind したら、さらにこのイベントが発生したことを、 Angular の世界にそれを通知する場合、該当要素の Scope を取得して、$apply 関数を呼び出す必要があります。
    if (ctrl.$viewValue !== value) {
      scope.$apply(function() {
        ctrl.$setViewValue(value);
    });
 
 実際のソースコードを見てみると、Angular は compile 段階で、HTML のテンプレートを保存したり、$$watcher に expression を追加したりしています。その後、 link 関数を返して、呼び出されます。その link 関数の中では、イベントハンドラーの追加や、jQuery UI の呼び出すことなどができます。さらに、何か変化がありましたら、Scope の $apply が呼び出されますので、 Angular の loop に入って、HTML のリフレッシュが実行されます。
 例えば、ng-model directive の実行順番は:
 Angular が bootstrap するとき、すべての directive を compile して、link 関数を呼び出します。このとき、ng-model の入っている要素の change イベントにイベントハンドラーをバンドルされます。キーボードのキーを押したとき、この change イベントが発生しますので、ハンドラー関数が呼び出されます。
 その後、 interpolation がこの directive を $watch リストに追加します。-> すべての directive とモジュールを初期化完了したら、Angular の loop から抜き出します。

 もしユーザーが何かのキーを input に押したら、ng-model のイベントハンドラーが呼ばれて、$apply 関数で model の value を変更します。
 $apply 関数の中に $digest 関数が呼ばれて、すべての登録した $watch 関数を一通り実行されます。
 これが終わったら、interpolation が実行されますので、HTML の変更などが実施されます。
 次、キーを押したイベントハンドラーの実行が終わりますので、Browser がリフレッシュされます。
 
 ちょっと複雑のように見えますが、実際どの directive もこのようなループが実行されます。一つ理解すれば、Angular はどのように動くかはたいてい明確になります。
 それでは。

2014年7月6日日曜日

Responsive web design の実施

 かなり前ですが、一回会社の UX, Designer チームと食事会があって、そこでこれからはどういう方針でデザインをやって行こうかと少し話しました。基本的には上層部に方針があって、ただ、我々はなんか新しいものを入れたいと思っていました。いろいろチャレンジしたいだそうです。
 それで、デザインのレビュー会議で、Responsive で行こうと、みんな決めました。まぁ、会社からそういう要求がないけど、やっぱり新しいものをやってみたかったです。まず UX とDesigner チームの方達はデスクトップとスマートフォンー向けの PSD ファイルを見せてくれました。その間は、もう私たち自由でやって、完成したらレビューさせることになります。
 デザインを持ち帰ったら、サポートするブラウザーのバージョンより、media query が使えたり、使えなかったりしていますので、方針を決めるにはちょっと時間がかかりました。JS のライブラリを使うか、IE7 を捨てるか、またはライブラリを自作するのかと。その中で、複雑にはやりたくない本音があるので、自作で簡単なライブラリを作りました。それは簡単で、ただブラウザーの幅をチェックして、親要素にクラスを追加するだけにしました。そうすると、media query が使えなくても、追加されたクラスでいろいろやって行けるようになります。
 次、Mobile First というベストプラクティスがあって、私たちはまずモバイル向けのサイトを作りました。media query でまず必要な要素の CSS を書いて、ほとんど親要素は 100% にして、その中のものは適切に幅を設定するようにしました。次、メニューとかどうするかとか、画像は小さいすぎるとか、実際にモバイルで動かしてみて、いろいろ調整しました。UX チームからはいい評価をもらったので、デスクトップ版の作成に入りました。
 こちらも同様、サイトのボディーは100%にして、すべての要素を emか、パーセンティジで細かくセットしました。
 まぁ、実際振り替えてみると、そんなには時間かからなかったんです。以外とすぐ行けました。感覚的には、いい HTML Structure が必要です。HTML が混乱すると、作業が急速に増えます。今、デバイスなどのサイズはかなり変な数字になっていますので、将来的にはもっとサイズを細かく設定する必要があると思われます。
 その後、みんなレビューして、これでオーケーだと。楽しかったです。
 後日また、経験を追加しておきます。