先日、あるアコーディオン的な 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
それでは。
Code
2016年8月28日日曜日
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 コードもいっぱい入ってます。
それでは。
具体的には 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 コードもいっぱい入ってます。
それでは。
登録:
投稿 (Atom)