Subscribed unsubscribe Subscribe Subscribe

Hatena Developer Blog

はてな開発者ブログ

Chrome+HTML5 Conference「Webアプリの道も一歩から」講演資料を公開します

2011年8月21日(日)に開催されたChrome+HTML5 Conferenceにて、id:nagayamaid:nanto_viが講演を行ったので、その資料を公開します。「Webアプリの道も一歩から 〜はてなブックマークの場合〜」と題し、公式Chromeウェブアプリ はてなブックマークの開発フローや利用技術について解説しました。発表は2部構成で、ディレクターのid:nagayamaがはてなでのChromeウェブアプリの開発フローに関して、エンジニアのid:nanto_viがウェブアプリで活用しているHTML5関連技術に関して述べています。

Web アプリの道も一歩から

  • 実装について
    • by nanto_vi
  • スクリプトの設計
  • 表示内容と URL の切り替え
  • Flexbox レイアウト
  • 新着エントリーの通知
  • プレビュー
  • タグ自動補完
  • キーボードアクセス
  • フォーカス管理
  • 設定の記憶
  • アプリケーションキャッシュ
  • CSS3 による装飾
  • CSS Transitions
  • スクロールバー

スクリプトの設計

  • MVC アーキテクチャを意識
    • Model: サーバー側 API、データの取得・保存
    • View: ユーザーへの表示、HTML の編集
    • Controller (Browsing): ユーザーアクションに基づく内容の切り替え

  • jQuery を採用
    • Events: Observer パターンの実装、独自イベント
    • Deferred: リソースの非同期読み込み
// Model
$([Model.Entry]).trigger('change');
$(this).trigger('load');

// View
$([Model.Entry]).bind('change', onChange);
$(model).bind('load', onLoad);

表示内容と URL の切り替え

  • URL から各表示領域の内容を再現できるように
    • /viewer
    • /viewer/hot/social
      • 「社会」カテゴリのエントリー一覧
    • /viewer?entry=http://example.org/
      • エントリーのブックマーク一覧
  • URL の切り替えは History API で
    • 「戻る」「進む」にも対応
    • pushState()replaceState()onpopstate
    • HTML5 で定義。WebKit、Mozilla、Opera が対応
<a href="/viewer/hot/social" data-link-target="viewer">社会</a>
$(document).delegate('a', 'click', function (e) {
  e.preventDefault();
  browse(e.currentTarget.href);
});

$(window).bind('popstate', function () {
  setup(document.URL);
});

function browse(url) {
  history.pushState({}, document.title, url);
  setup(url)
}

function setup(url) { ... }

Flexbox レイアウト

  • Mozilla の XUL レイアウト由来
  • ボックスを縦または横に並べていく
    • Flexbox: コンテナ
    • Flexbox items: Flexbox の子要素。縦一列または横一列になる
  • 固定長と可変長の組み合わせ
    • box-flex に正整数を指定した flexbox item は可変長になる

CSS3 Flexible Box Layout Module
  • Flexbox の標準化
  • display: flexbox
  • flex-flow, flex-align, flex-pack...

新着エントリーの通知

  • Notifications API
  • 通知は即表示されるとは限らない
  • 通知の消去も自分で制御
    • ondisplay から 10 秒後に cancel()
var n = webkitNotifications.createHTMLNotification(url);
n.ondisplay = ...;
n.onclick = ...;
n.show();

プレビュー

  • iframe 要素で埋め込み
  • sandbox 属性でフォーム送信、スクリプト実行等を禁止
    • sandbox="allow-forms allow-scripts" で個別許可も可能
    • HTML5 で定義。WebKit、IE 10 PP が対応
<iframe sandbox></iframe>

タグ自動補完

  • ユーザーが入力するたびにタグ候補を表示
    • ユーザーの入力は input イベントで監視
    • HTML5 で定義。Mozilla、WebKit、Opera が対応
    • DOM3 Events では textinput イベントが定義される

キーボードアクセス

  • tabindex 属性でキーボードアクセス可能に
    • 「Enter で実行」は自前で実装
    • IE 由来。HTML5 で定義。各ブラウザが対応
<span class="trigger" tabindex="0">実行</span>
.trigger { cursor: pointer; }
$(document).delegate('.trigger', {
    keypress: function (event) {
        if (event.which !== 13) return;
        $(event.currentTarget).click();
        event.preventDefault();
        event.stopPropagation();
    },
    mouseup: function (event) {
        event.currentTarget.blur();
    },
});

フォーカス管理

  • モーダルダイアログ表示は、モーダルダイアログのみにフォーカス
  • focusin, focusout イベント
    • focus, blur イベントと同じだが、バブルする
    • IE 由来。DOM3 Eventsで定義。
    • jQuery は独自に模倣

設定の記憶

  • 設定読み書き用オブジェクトを作成
    • バックエンドに Web Storage を使用
  • JSON にシリアライズして保存
  • 名前空間を指定してキー名の競合を防ぐ
    • Mozilla の preferences より
var storage = new Storage('my.namespace');
storage.set('foo', { bar: 42 });
// 実際の localStorage には、キー 'my.namespace.foo' に
// 文字列値 '{"foo":42}' が保存される

アプリケーションキャッシュ

  • HTML、CSS、JS、画像などをキャッシュ、オフラインで利用可能に
    • HTML5で定義。Mozilla、WebKit、Opera が対応
  • 今回は単に読み込み高速化のためだけに使用
    • エントリー一覧、ブックマーク一覧をオンラインで取得するため
  • サーバー側での表示出し分けはできなくなる
    • ユーザー名表示などもクライアント側で生成
<html manifest="/viewer.appcache">
CACHE MANIFEST
# <commit hash>

CACHE:
base.css
viewer.js

# マニフェストに記載のないリソースをオンラインで取得
NETWORK:
*

CSS3 による装飾

  • Photoshop で作った画像を CSS で再現可能
    • グラデーション
    • 複数背景
    • box-shadow
    • text-shadow

CSS Transitions

  • ツールチップを浮かび上がるように表示
    • スクリプトで class 名を切り替え
    • visibility も変化させないと、フォーカス移動の対象になったりしてしまう
.tooltip {
  visibility: hidden;
  opacity: 0;
  -webkit-transition: 0.2s ease-out;
  -webkit-transition-property: visibility, opacity;
}
.tooltip.shown {
  visibility: visible;
  opacity: 1;
}
切り替えアニメーションでの注意

  • 文書木にない要素は表示されない
    • 新旧の要素を重ねる場合、旧要素 (A) の削除と新要素 (B) の追加を同時には行えない
  • webkitTransitionEnd イベントが発生したら A を削除

スクロールバー

  • -webkit-scrollbar 擬似要素
    • WebKit の独自拡張
  • 内容に重なるスクロールバー
    • スクロールバー専用の要素
    • スクリプトでスクロール位置を同期