Code

2016年8月28日日曜日

CSS Animation, Focus と Accessibility

  先日、あるアコーディオン的な Form を作って、CSS で slide up / down アニメーションをつけました。実際 CSS animation を使うとき、要素の display: none から display: block に, visibility: visible から visibility: hidden を変更すると、アニメーションが実行されません。現在 CSS animation はある数字から、次の数字までしか動きません。Height の場合、0 から 200 までアニメートできますが、0 から auto まではできません。
 では、なぜ jQuery animate 関数を使わないかと、CSS の方が効率がいいし、面白いからです。Chrome の FPS ツールでみると、CSS の場合、主に 50 FPS に一定していますが、jQuery animate は、JavaScript を実行しているので、FPS 一定ではないです。(https://greensock.com/js/speed.html)
 それで、display: none -> block の代わりに、maxHeight: 0 -> 200px を css transition でアニメートするようにしました。maxHeight 0 となっても、実際その要素は DOM に存在していますので、Screen Reader は認識しています。さらに、そのエリアに Form <input /> があるため、Tab Stop も発生しています。
 Focus が見えないところに行くのは大変良くないことです。ユーザーが Tab キーをおして、しばらく Focus はどこにあるかわからなくて、結構迷うようです。
 では、css animation を使うとき、accessibility について実装方法としては幾つかコツがあります。
 まず、<input /> みたいな Form 要素に対して、tabIndex を使って、 tab order を変更します。0 から -1 にします。0 の場合は、tab order をブラウザーを任していますが、-1 の場合、JavaScript の setFocus() 関数を使う必要があります。これで、ユーザーの Tab キーはその要素をスキップするようになります。
 次、aria-hidden を使って、見えない部分を Screen Reader から隠します。aria-hidden を true にすれば、すべての Screen Reader が読まないようになります。
 具体的に:
    <form aria-hidden={isOpen ? false : true}>
        <input tabIndex={isOpen ? 0 : -1} />
    </form>
 にするだけです。

 これで、ユーザーの Tab Stop が Form Hidden の場合、そのエリアの <input /> などに行かないように、矢印キーを使っても、その部分が読まないようになります。アニメーションは maxHeight を使って、開いたり、閉じたりできます。

 Accessibility について、いいコースがあります。英語ですけど。。。
    https://www.udacity.com/course/web-accessibility--ud891

 それでは。

2016年8月6日土曜日

React.js Component Wrapper

 先週、pre-loaded データを使って、Component を render する代わりに、ajax を使って、データロードして、そのあと、 Component を render する機能が出てきました。この機能を実現するために、Wrapper Component を作ったら、簡単にできました。
 具体的には autoLoadingComponent を作って、その中に、state を持たせて、componentDidMount の中に、ajax call を呼び出して、callback には setState() を読んで、state を更新します。この state をベースに元の Component に props として渡します。
 そうすると、state が更新するたびに、元の Component が re-render されますので、auto load 機能が実現できます。簡単な例として:

 autoLoader = React.createClass({
      mixin: [pureRenderMixin],
      getInitialState: function () {
        return {
           data: null,
           loading: true
        };
      },
      componentDidMount: function () {
         // ajax call to get data
         getJson(url, this.handleResponse);
      },
      handleResponse: function (response) {
          this.setState({
            data: response,
            loading: false
          });
      },
      render: function () {
         if (this.state.loading) {
           return <loading />;
         } else {
           return <component data={this.state.data} />;
         }
      }
   });

 これで、過去の Component も再利用できますし、どんなものでも auto load 機能がつけられます。
 さらに、Redux を使ってる場合、Redux.connect() という関数があります。この関数も同じ考え方で、state を持ってる Wrapper を作って、過去の Component を自動更新できるようにします。その中も subscribe などの biolaplate コードもいっぱい入ってます。
 
 それでは。