先週、作ってるサイトがバグ見つかった。問題は non-responsive のページは一画面に全部表示されなかったことです。期待としては responsive ページはそのまま表示、non-responsive ページは縮小して、一画面に収まれるようになることです。
調べてみたら、viewport の設定に問題があった。今のプロジェクトでは、responsive ページとnon-reesponsive ページは C# の属性を標識して、render engine で違う viewport を設定します。
viewport と言えば、携帯のピクセルは実際ページのどのぐらい当てるかの設定です。例えば、viewport の width を 800 に設定したら、携帯のスクリーンは 800 pixel となります。もしサイトの横サイズは 800 だったら、ちょうど一画面に収まれます。1024 に設定したら、800 pixel サイズのサイトはちょっと小さく表示されます。
一般的には responsive サイト、またはそのサイズのままで表示したい場合、 width を device-width を設定すれば良いです。これで、ディバイスの横幅をそのまま使われます。また、サイトを小さくしたい場合、init-scale を小さくすれば良いです。それに、minmum-scale, maximum-scale でサイトの拡大と縮小倍率はコントロールできます。user-scalable でユーザがサイトピンチで拡大できるかどうか設定できます(yes or no).
今回の問題は width を 800 に設定したら、initial-scale も 1.0 に設定したからです。これで、width は正しく 800 になっても、サイトのイニシャルスケールは 1.0 つまり、サイトはどのぐらい大きいか、もうそのまま表示されます。つまり、width の設定が上書きされました。今後注意しましょう。
ちなみに、古いバージョンの iPhone または Android のデフォルトブラウザーでは、width を指定した後、scale を指定すると、viewport が効かないようになりますので、ご注意ください。つまり、content="width=1024, user-scalable=no" は動きますが、content="width=1024, maximum-scalable=2" は動かないです。
後、Android と iOS の古いバージョンでは、viewport を正しく解釈できないことが発見しました。多分 CSS の影響だと思いますが。。。
最新の Chrome (Android) をダウンロードして、チェックしてよいです。iOS なら、できるだけ、iOS 7を使いましょう。iOS 6 の Safari はまだまだバグがあります。特に、transform に関して、正しく表示できない時があります。
それでは。
Code
2014年9月27日土曜日
2014年9月19日金曜日
リターンキーでフォーム Submit
昨日、変なバグが見つかった:ある input text にフォーカスを押して、リターンキーをクリックしたら、画面が更新されました。。。何でだろうと思った。。。
実際見てみると、input は form タグの中にあるので、フォームの action は "#" と書かれています。# だと、自分のアドレスに submit されますので。。。でも input は text type だし、submit タイプではないから、何で submit がトリッガーしただろう??
調べてみたら、もし Form の中に input text が一つだけある場合、リターンキーを押すだけで、 form が submit されます。これはブラウザーの固有動作です。しかも IE 5 からあります。
また、IE から変な動作を継承したな〜と思いました。
直す方法としては form の action を削除するか、display : none の input text をもう一個追加するです。
将来のブラウザーはこれを直さないだろう。メモしておこう。
それでは。
実際見てみると、input は form タグの中にあるので、フォームの action は "#" と書かれています。# だと、自分のアドレスに submit されますので。。。でも input は text type だし、submit タイプではないから、何で submit がトリッガーしただろう??
調べてみたら、もし Form の中に input text が一つだけある場合、リターンキーを押すだけで、 form が submit されます。これはブラウザーの固有動作です。しかも IE 5 からあります。
また、IE から変な動作を継承したな〜と思いました。
直す方法としては form の action を削除するか、display : none の input text をもう一個追加するです。
将来のブラウザーはこれを直さないだろう。メモしておこう。
それでは。
2014年9月8日月曜日
Angularjs $timeout 調べ
DI (Dependency Injection) は Javaや、ASP.NET などバックエンドには人気ですけど、AngularJS はそれを JavaScript に応用できるようにしました。具体的に言うと、モジュールをロードするときや、関数を呼び出す場合、$injector の invoke 関数で、呼び出す対象の引数を全部パースして、providerと instance キャッシュの中に探して行く、もし同じ名前の対象があったら、それを使います。
さらに、Ajax call を実行するとき、$http と $httpBackend を利用することで、$browser も内部で持つことで、すべての操作をライブラリ内部で intercept できるようになります。
この中に、面白いのは $timeout というものです。実際の timeout 関数は既にあるのに、何で $timeout を実装するのか?
理由としては、二つあります。まずは Unit test を書くとき、自分で timeout をトリッガーできるようになります。実際のコードを見ると、promise を使っていますが、promise の中には、window.timeout 関数が使われています。これで timeout を同じ機能が実行できるようになります。さらに、もし外部で同じ promise を resolve したら、同じ関数が呼ばれますので、timeout 発生時と同じ動作になります。これはかなりいいことです。もう timeout を待つ必要がなくなります。
もう一つは Angular Scope の $digest に関係あります。$timeout の中に $rootScope.apply() が呼ばれています。これを理解するには JavaScript 実行のサイクルを理解する必要があります。以前では setTimeout(function() {}, 0) を呼び出すことで、今までの JavaScript コールスタックをすべてクリアした後に、コードを実行する方法があります。$timeout も同じです。AngularJS に自分のサイクル、いわゆる $digest がありますの、この関数ですべての watcher を oldValue, newValue を計算して、listener をその後呼び出します。一つのサイクルを終わらないと、Angular は対象の変更が察知できないことがあります。例えば、ng-include でテンプレートの path を変更したい場合、関数の中に
/* templateUrl の値は abc とします。*/
templateUrl = "";
/* その他の操作*/
templateUrl = "abc"; // テンプレートをリロードしたい
をやると、$digest はこの関数終わった後に実行されますので、templateUrl は変更してないように見えます。つまり、変数の変更をAngularJS に察知させる為に、一つの $digest サイクルの終わりを待つ必要があります。このとき、$timeout は利用すれば、ちゃんと意図通りできます。
templateUrl = "";
/* 他の操作 */
$timeout(function() {templateUrl = "abc"})
これで、最初の templateUrl がクリアされて、$digest の実行で、ng-include directive がクリアされます。その後、次の $digest サイクルで、templateUrl がまた元に戻したので、すべての expression がもう一度更新されます。
もちろん、自分で setTimeout を実行して、scope.apply() を呼び出しても、同じ機能ができますの、そのショートカットとして、$timeout を使えば、簡単になります。
まぁ、dirty check にはいろいろ不利なところもありますが、Knockout.js の set 関数を比べると、自分は Angular のほう好きです。
最近、このライブラリも人気だそうです:
durandal
このライブラリは require.js を使っていますので、Angular でできない遅延ロードもできます。聞いたことですが、テンプレートのほうも Angular よりちょっとだけ優れています。
まぁ、これらは Angular 2.0 で改善される予定なので、少し待ってもいいような気がしますけど。
それでは。
さらに、Ajax call を実行するとき、$http と $httpBackend を利用することで、$browser も内部で持つことで、すべての操作をライブラリ内部で intercept できるようになります。
この中に、面白いのは $timeout というものです。実際の timeout 関数は既にあるのに、何で $timeout を実装するのか?
理由としては、二つあります。まずは Unit test を書くとき、自分で timeout をトリッガーできるようになります。実際のコードを見ると、promise を使っていますが、promise の中には、window.timeout 関数が使われています。これで timeout を同じ機能が実行できるようになります。さらに、もし外部で同じ promise を resolve したら、同じ関数が呼ばれますので、timeout 発生時と同じ動作になります。これはかなりいいことです。もう timeout を待つ必要がなくなります。
もう一つは Angular Scope の $digest に関係あります。$timeout の中に $rootScope.apply() が呼ばれています。これを理解するには JavaScript 実行のサイクルを理解する必要があります。以前では setTimeout(function() {}, 0) を呼び出すことで、今までの JavaScript コールスタックをすべてクリアした後に、コードを実行する方法があります。$timeout も同じです。AngularJS に自分のサイクル、いわゆる $digest がありますの、この関数ですべての watcher を oldValue, newValue を計算して、listener をその後呼び出します。一つのサイクルを終わらないと、Angular は対象の変更が察知できないことがあります。例えば、ng-include でテンプレートの path を変更したい場合、関数の中に
/* templateUrl の値は abc とします。*/
templateUrl = "";
/* その他の操作*/
templateUrl = "abc"; // テンプレートをリロードしたい
をやると、$digest はこの関数終わった後に実行されますので、templateUrl は変更してないように見えます。つまり、変数の変更をAngularJS に察知させる為に、一つの $digest サイクルの終わりを待つ必要があります。このとき、$timeout は利用すれば、ちゃんと意図通りできます。
templateUrl = "";
/* 他の操作 */
$timeout(function() {templateUrl = "abc"})
これで、最初の templateUrl がクリアされて、$digest の実行で、ng-include directive がクリアされます。その後、次の $digest サイクルで、templateUrl がまた元に戻したので、すべての expression がもう一度更新されます。
もちろん、自分で setTimeout を実行して、scope.apply() を呼び出しても、同じ機能ができますの、そのショートカットとして、$timeout を使えば、簡単になります。
まぁ、dirty check にはいろいろ不利なところもありますが、Knockout.js の set 関数を比べると、自分は Angular のほう好きです。
最近、このライブラリも人気だそうです:
durandal
このライブラリは require.js を使っていますので、Angular でできない遅延ロードもできます。聞いたことですが、テンプレートのほうも Angular よりちょっとだけ優れています。
まぁ、これらは Angular 2.0 で改善される予定なので、少し待ってもいいような気がしますけど。
それでは。
登録:
投稿 (Atom)