カテゴリー別アーカイブ: HTML

AngularJSでUI BootstrapのTooltipをValidationに連動させて表示させる

| コメントをどうぞ

AngularJSには強力なフォームのバリデーション機能があり、任意のタイミングでエラーメッセージを表示することができます。
このエラーメッセージをバリデーションの状態に連動してUI BootstrapのTooltipに表示したいとき、どうすればよいでしょうか。

まずは思い付く方法で

まずは最初に思い付いた方法で実装してみます。
ツールチップはtooltip-enableディレクティブで有効/無効を切り替えられるので、バリデーションに失敗したときだけ有効にして表示するようにしてみました。

index.html

...

<form name="form" style="width:500px;" ng-class="{'has-error': form.value1.$dirty && form.value1.$invalid}">
  <table class="table">
    <tr>
      <th>お名前(20文字まで)</th>
      <td>
        <input type="text" class="form-control input-sm" name="value1" ng-required="true" ng-model="value1" tooltip="20文字以下で入力して下さい" tooltip-enable="form.value1.$dirty && form.value1.$invalid" ng-maxlength="20">
      </td>
    </tr>
  </table>
</form>

...

app.js

angular.module('app', ['ui.bootstrap']);

結果

バリデーションエラー時にinputにマウスカーソルを乗せることでツールチップが表示されるようになりました。

tooltip1

しかし、これではinputにマウスカーソルを乗せるまでエラーの内容が分かりませんし、マウスカーソルを離すとツールチップが消えてしまうのでイマイチです。
これは、ツールチップの表示を切り替えるデフォルトのトリガーイベントがmouseenter/mouseleaveとなっているためです。

tooltip.js

...

  // Default hide triggers for each show trigger
  var triggerMap = {
    'mouseenter': 'mouseleave',
    'click': 'click',
    'focus': 'blur',
    'none': ''
  };

...

.directive('tooltip', [ '$tooltip', function($tooltip) {
  return $tooltip('tooltip', 'tooltip', 'mouseenter');
}])

...

tooltipのカスタムトリガーとカスタムディレクティブで実現する

Plunker
今回はバリデーションの状態変化に応じて自動的にツールチップを表示したいので、自由に使えるカスタムトリガーが必要になります。

まずは、適当なconfigセクションで$tooltipProvider.setTriggers()を使ってカスタムトリガーを定義しましょう。$tooltipProvider.setTriggers()の引数には、表示イベント名をプロパティ名、非表示イベントをそのプロパティの値として持つオブジェクトを指定します。

app.js


var app = angular.module('app', ['ui.bootstrap', 'app.directives']);

// 追加
app.config(['$tooltipProvider', function ($tooltipProvider) {
  $tooltipProvider.setTriggers({
    'show': 'hide'
  });
}]);

これで要素がshowイベントを受け取った時にツールチップが表示されるようになりました。

次に、このトリガーを発火するための仕組みを作ります。

AngularJSのバリデーション機能は、バリデーション対象として指定したモデルの値の変化に応じてリアルタイムに実行され、実行結果がスコープにセットされるようになっています。上記index.htmlの例だと、form.value1.$invalidform.value1.$validのboolean値を読むことでバリデーションの状態を取得することができます。

今回はtooltipディレクティブを拡張するカスタムディレクティブを用意して、その中でform.value1.$invalidを監視することで、バリデーションの状態が切り替わった時にshowイベントまたはhideイベントを発生させてツールチップの表示を切り替えるようにしました。

directives.js


angular.module('app.directives', [])

// ツールチップ本体に付加されるディレクティブ
// デフォルトのものをそのまま流用
.directive( 'myTooltipPopup', function () {
  return {
    restrict: 'EA',
    replace: true,
    scope: { content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
    templateUrl: 'template/tooltip/tooltip-popup.html'
  };
})

// tooltipディレクティブを拡張するカスタムディレクティブ
.directive('myTooltip', ['$tooltip', '$timeout', function ($tooltip, $timeout) {
  // tooltipのインスタンスを作成する
  var tooltip = $tooltip( 'myTooltip', 'myTooltip', 'show' );
  var originalCompile = angular.copy(tooltip.compile);
  tooltip.scope = {myTooltipShow : "&"}

  // compileを拡張
  tooltip.compile = function(element, attrs, scope){
    // オリジナルのLink function
    var originalLink = originalCompile(element, attrs);

    // linkを拡張
    var link = function(scope, element, attrs) {
      // my-tooltip-show属性の値を監視する
      scope.$watch(scope.myTooltipShow, function (val) {
        if (val) { // 評価値がtrueならばshowイベントを発生させる
          $timeout(function() {
            element.triggerHandler('show');
          });
        }else{ // 評価値がfalseならばhideイベントを発生させる
          $timeout(function() {
            element.triggerHandler('hide');
          });
        }
      });
      // オリジナルのlinkを実行する
      originalLink(scope, element, attrs);
    }
    return link;
  }
  return tooltip;
}])

index.html

...

<input type="text" class="form-control input-sm" name="value2" ng-required="true" ng-model="value2" my-tooltip-show="form.value2.$dirty && form.value2.$invalid" my-tooltip="20文字以下で入力して下さい" ng-maxlength="20">

...

これで、バリデーションの状態がエラーになった時点でツールチップが表示され、正常になった時点で非表示になるようになりました。
my-tooltip-showform.value2.$dirtyを加えているのは初期状態でツールチップが表示されるのを抑制するためです。

今回作成したものはPlunkerにデモを用意していますので、ご利用ください。
http://plnkr.co/edit/ynywEEpNvlx0iSA2bUpI?p=preview

ウィンドウのサイズを変えていないのに window.resize イベントが発生する

| コメントをどうぞ

IE8で、ウィンドウの大きさを変えていないのに window.resize イベントが発生することがあります。

再現方法

ドキュメント内で要素の高さを変更するだけで再現します。

以下、サンプルHTMLです。

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
    <script>
    function resized(e){
      alert("window resized!");
    }
    function doResize(){
      document.getElementById("field").style.height = "100px"
    }
    if (window.addEventListener) {
      window.addEventListener("resize", resized, false);
    }
    else {
      window.attachEvent("onresize", resized);
    }
    </script>
  </head>
  <body style="background-color:green;">
    <input type="button" id="doResize" value="ResizeTest" onclick="doResize()">
  <div id="field" style="background-color:#000000;color:#FFF;">リサイズテスト</div>
  </body>
</html>

続きを読む

IE10以降で iframe 内ドキュメントの表示が崩れることがある問題について

| 1件のフィードバック

IE10 以降で、iframeドキュメント内の表示が崩れることがあったので調査を行いました。

結果から書きますと、 iframe親のドキュメントモードが 9 以上、iframeドキュメントが IE5 Quirksモード である場合 に問題が発生する可能性があることがわかりました。

現象

  • 親(= iframe の親ドキュメント)はmetaタグで 最新のレンダリングモード(edge)が指定されている
  • <meta http-equiv="X-UA-Compatible" content="IE=edge">
  • 子(= iframe ドキュメント)は古いドキュメントなので、Quirksモードでレンダリングされている。DOCTYPEスイッチで指定
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
  • 親ドキュメント内iframeから子を参照すると一部スタイルが崩れている
  • 子を iframe を介さずに直接表示した場合は問題なし
  • iframeを介して参照した時、子のドキュメントモードは「 10 」だった(IE10で参照)
  • iframeを介さずに参照した時、子のドキュメントモードは「 5 」だった(IE10で参照)

続きを読む

WebRTCでビデオチャットアプリ作成

| 1件のフィードバック

WebRTCを使ったコミュニケーションアプリを作る機会があったので、今回はWebRTC関連のライブラリの使い方とサンプルアプリを紹介したいと思います。

WebRTCとは?

WebRTCはリアルタイムコミュニケーションアプリを作成するためのAPIです。具体的には以下のような機能がJavaScriptの知識さえあれば簡単に実装できます。

  • テキストチャット
  • ビデオチャット
  • ボイスチャット
  • ファイル共有
  • スクリーン共有(Chromeのみ)

続きを読む

モバイル向けWebページのデバッグについて

| コメントをどうぞ

スマートフォンやタブレットにもSafari、 Android Browser(Android 標準ブラウザ)、Chorme、IE、FireFoxなどたくさんのブラウザがあります。作成するWebページをそれらのブラウザに対応させるとなると、ブラウザ間でのCSSの動作や表示の違いを修正するために何かと実機でのデバッグ作業が必要な場合がでてくるかと思います。しかし、実際にiOSのSafariでデバッグするのにMacを利用できる環境でなかったり、デバッグツールがないAndroid Browserでデバッグしたい場合などがあると思います。そのような場合には「weinre」というリモートデバッグツールが有用かと思います。
weinreを利用することにより、 Chromeを使ってデバイス上のWebページのリモードデバッグが可能になります。

weinreのセットアップ手順

1.node.jsのインストール

以下のサイトからnode.jsのインストーラをダウンロードしてインストールします。
http://nodejs.jp/nodejs.org_ja/

2.weinreのインストール

npmを使用してweinreをインストールします。

npm install weinre

nodo_modules以下にweinreディレクトリが作成されればインストール成功です。

デバッグの準備

1.weinreの起動

以下のコマンドを実行しweinreを起動します。

./node_modules/weinre/weinre

起動オプションについての詳細は以下のページをご参照ください。

http://people.apache.org/~pmuellr/weinre/docs/latest/Running.html

2.スクリプトの埋め込み

デバッグしたいページに以下のスクリプトを埋め込みます。

<script src="[ホスト名]:8080/target/target-script-min.js#dev1"></script>
  • ホスト名にはweinreが起動しているPCのホスト名、IPアドレスを指定します。
  • #以降は任意の文字列を指定します。

3.デバッグコンソールの起動

Chromeで以下のURLを開きます。

http://localhost:8080/client/#dev1
  • #以降は2で指定した文字列と同じものを指定します。

weinre-console-1

デバッグ

実機で先ほどのスクリプトを埋め込んだWebページを開き、コンソール画面を見ると以下のようにターゲットに追加されます。URLが緑色になっていればデバッグ可能な状態です。
target-list

では、実際にコンソールを操作してみます。
Consoleボタンを選択し、コンソールを開き以下のスクリプトを実行してみると実機の情報が出力されます。

console.log(window.navigator.userAgent);

debug-js

Elementsタブを開けばCSSもデバッグできます。サポートしていないCSSのクラスやプロパティを指定しようすると警告表示となります。
debug-css

おわりに

以上のように、weinreを利用することで、PCから各デバイスのブラウザ上のWebページをデバッグすることができます。
専用のデバッグツールほど高機能ではありませんが、手っ取り早くCSSのサポート状態を確認したり、作成したCSSの動作確認をする時などに利用できるのではないかと思います。
また、クラウド上の検証サービスで実機をレンタルしてリモートデバッグしたり、ハイブリッドアプリケーションのデバッグも可能かと思います。

モバイル向けWebページでiFrameを使用する際のスクロール問題

| 5件の返信

infoScoopをタブレット対応させる際に、iFrame絡みのクロスブラウザ対策でハマったので、そのあたりの対策について紹介したいと思います。

 

サポートブラウザ

今回サポート対象となったブラウザ(OS)は以下になります。

  • Safari(iOS)
  • Android Browser(Android)
  • IE(Windowsタブレット)

 

DOCTYPE宣言

HTMLの記述の前に、まずはDOCTYPE宣言です。 以下はHTML5のDOCTYPE宣言になります。

<!DOCTYPE HTML>

現状では何も宣言しないとブラウザのレンダリングモードは互換モードが選択されます。
また、互換モードに比べて標準モードの場合は記述が厳格になることもあり表示速度の向上も期待できます。
なので、モバイル向けにWebページを作成する際にはHTML5のDOCTYPE宣言をするようにしましょう。

 

iOSのiFrameにおけるスクロール問題

iFrameにコンテンツを表示するために、以下のようにHTMLを記述してブラウザで表示してみたところ、

<!DOCTYPE HTML>
<html>
<head>
  <style type="text/css">  
    .ifrm {
      width:300px;
      height: 250px;
      border:none;
    }
  </style>
</head>
<body>
  <iframe class="ifrm" scrolling="auto" src="http://www.infoscoop.org"></iframe>
</body>
</html>

Android BrowserとIEでは問題ありませんでしたが、

IE(Windowsタブレット)の場合

IE(Windowsタブレット)の場合

iOS Safariで表示してみると、コンテンツのサイズに合わせてiFrameがのびてしまいました。PC向けのサイトだと以下のように画面一杯に広がってしまいます。

iframe-safari-err


iOS Safariの場合

 

どうやらiOS Safariではiframeのscrolling属性が”auto”、もしく”yes”の場合、指定したサイズが効かないらしく、他の方のブログを参考にさせて頂いてiframeの直上にスクロール要素を用意する方法が有効である、ということが分かりました。
エージェントで判別してiOS Safariの場合だけ直上にスクロール要素を用意する方法も考えましたが、
煩雑になると思いCSSで対応することにしました。

以下のように記述し、実機で確認してみたところ、

<!DOCTYPE HTML>
<html>
<head>
  <style type="text/css">  
    .ifrm-container {
      width:300px;
      height:250px;
      overflow:auto;
      -webkit-overflow-scrolling:touch;
    }
    .ifrm {
      width:100%;
      height:100%;
      border:none;
    }
  </style>
</head>
<body>
  <div class="ifrm-container">
    <iframe class="ifrm" scrolling="auto" src="http://www.infoscoop.org"></iframe>
  </div>
</body>
</html>

SafariではiFrameの高さも固定され問題なくスクロールできるようになりましたが、
今度はAndroid BrowserとIEで2重にスクロールバーが表示されるようになってしまいました。

2重スクロール(IEの場合)

2重スクロール(IEの場合)

 

2重スクロールへの対処

どうやらiframeの高さがうまく調整できていないのが原因のようです。
iframeの下に余白が発生してしまっています。

試行錯誤の後、iframe要素に明示的に「display:block;」を指定することで回避できました。
理由はよく分かりません、デフォルトの表示形式が「inline-block」だとしても高さ調整は可能なはず…
対処するのに半日ほど掛かってしまいました…。
最終的に作成できたHTMLは以下になります。

<!DOCTYPE HTML>
<html>
<head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">
  <style type="text/css">
    .ifrm-container {
      width:300px;
      height:250px;
      overflow:auto;
      -webkit-overflow-scrolling:touch;
      display: inline-block;
      margin: 10px;
    }

    .ifrm {
      width:100%;
      height:100%;
      border:none;
      display:block;
    }
  </style>
</head>
<body>
  <div class="ifrm-container">
    <iframe class="ifrm" scrolling="auto" src="http://www.infoscoop.org"></iframe>
  </div>
  <div class="ifrm-container">
    < iframe class="ifrm" scrolling="auto" src="http://jquery.com/"></iframe>
  </div>
</body>
</html>