はじめに
実務で簡単な画面を作成しているときにTab押下時に以下の要素に順番にフォーカスする。という仕様があった。
初めての開発案件でまともにjsを触るのが初めてだった私はググれば一瞬で出来るやろ。とたかを括っていました。(多分基礎的な仕様なんだと思いますが。。)
ググり方が悪いのか意外とやりたい事がビシッと書いてある記事がない、、、
やりたいこと
画面上にある要素に対してTabキー押下時にフォーカスをループしたい。
手順
1.フォーカスループしたい要素を配列の形式で取得する
2.Tabボタン押下のイベントをキャッチ
3.1で取得した配列をもとにジェネレーターを作る(ジェネレーターを使ってみたかったから使用しました。多分簡単なやり方が他にもある)
準備
マテリアルUIを使ってますが簡単に言えば二つのボタンが画面上にあるイメージ
今回使用するコードの全体像
const Tabs = () => {
useEffect(() => {
//フォーカスループしたい要素を取得
const button = document.querySelectorAll('button');
//フォーカスループした要素数に応じてインデックスを延々と返す関数
function* next(a: any, num=0):Generator {
do{
if (num === a.length ) {
num = 0
}
yield num++;
} while(num <= a.length )
}
var index = next(button);
//Tabキー押下時のイベントキャッチ
const tabmove = (e: any) => {
if (e.key === "Tab") {
e.preventDefault();
button[index.next().value].focus();
}
}
document.addEventListener('keydown', tabmove);
},[])
return (
<Container maxWidth="sm">
<Box>
<Stack spacing={2} direction="row" >
<Button variant="contained" color='success' tabIndex={1} >
成功
</Button>
<Button variant="contained" color="error" tabIndex={ 2}>
失敗
</Button>
</Stack>
</Box>
</Container>
)
}
1.フォーカスループしたい要素を配列の形式で取得する
今回はボタン要素にのみフォーカスを当てたかったので以下の記述でいいですが、フォーカスを当てたい要素が他にもあれば取得方法は検討する必要があると思います。
const button = document.querySelectorAll('button');
2.Tabボタン押下のイベントをキャッチ
初めはkeydownではなくkeyupにしていたのですが、それではTabキーのデフォルト動作をpreventDefaultで削除することが出来ず沼っていました。keydownにすれば問題なくTabの動作を制御できました。(制御しなかったら画面の検索バーとかにもフォーカスが当たってしまう)
(typescriptの型は今回anyでやってます。。時間ができた時に型を指定したい)
const tabmove = (e: any) => {
if (e.key === "Tab") {
e.preventDefault();
button[index.next().value].focus();
}
}
document.addEventListener('keydown', tabmove);
上記コードでやってるのは
e.preventDefaultでTabのデフォルト動作を消してから、項番1で取得したボタン配列の要素に応じてインデックスを指定し、指定された要素にのみfoucsを用いてフォーカスしている。と分かればかなりシンプルな作り。
3.1で取得した配列をもとにジェネレーターを作る
Tabボタンを押下したタイミングで取得した配列の要素数に応じて0から始まり要素数までインクリメントで上がってくれる関数を作ろうと思いました。
やりたいことのイメージに一番あっていたのがジェネレーターを用いた関数でした。
function* next(a: any, num=0):Generator {
do{
if (num === a.length ) {
num = 0
}
yield num++;
} while(num <= a.length )
}
let index = next(button);
index.next() //=> 0
index.next() //=> 1
index.next() //=> 2
index.next() //=> 3
index.next() //=> 0
index.next() //=> 1
index.next() //=> 2
index.next() //=> 3
引数aには配列を格納し、その要素数を上限に0スタートで数値を返却するという関数です。
終わり
将来、開発経験をさらに積んで今回記述した汚いコードを赤面して見るために頑張ります。。