かあちゃんエンジニアの気まぐれ開発メモ

フリーランス15年から足を洗ったかあちゃんエンジニアの技術メモ

テーブルの入力欄を矢印キーで移動したいって

テーブル形式になっている入力欄。
エクセルみたいに、矢印キーで入力欄を移動したいやつ。

これは1行の入力欄の数が異なるケースでも動作するように、
単に1行分の入力indexでフォーカスするんではなくて、tdを数えて移動。

$(document).on("keydown", ".arrowable", function(e){

	var index = null;
	var selector = ".arrowable";
	var $this = $(this);

	// 直近の親要素tr 内にあるselector 要素の数を取得
	var parent = this.closest('tr');
	var index = $(selector).index(this);
	
	// thisのjQueryオブジェクトをjavascrptのDOMエレメントに変換する
	var domElement = $this[0];
	
	var distance = '';
	// SELECTボックスの場合はEnter, Shit + Enterで移動
	if (domElement.tagName == 'SELECT') {
		if (e.shiftKey) {
			if (e.keyCode == 13) {
				// Shift + Enterキーの場合は上↑
				distance = 'up';
			}
		} else {
			if (e.keyCode == 13) {
				// Enterキーだけの場合は下↓
				distance = 'down';
				
			}
		}
	}
	
	//  ↓ 下キー
	if ( (e.keyCode == 40 && domElement.tagName != 'SELECT') || distance == 'down'){

		// 最後のtrでなければ、次のtrの同じtd内にあるarrowableへフォーカス
		if ($(parent).next().length) {

			var nextTr = $(parent).next();
			// 同じindexのtdがあれば移動、なかったら移動しない
			var number = $(parent).find('td').index(this.closest('td'));
			var target = $(nextTr).find("td:eq(" + (number) + ")");

			if (target.find(".arrowable").length) {
				target.find(".arrowable").focus();
			} else {
				nextTr = $(nextTr).next();
				target = $(nextTr).find("td:eq(" + (number) + ")");
				if (target.find(".arrowable").length) {
					target.find(".arrowable").focus();
				}
			}
		}
		return false;
	}

	//  ↑ 上キー
	if ( (e.keyCode == 38 && domElement.tagName != 'SELECT') || distance == 'up'){
		// 最後のtrでなければ、次のtrの同じtd内にあるarrowableへフォーカス
		if ($(parent).prev().length) {

			var prevTr = $(parent).prev();
			// 同じindexのtdがあれば移動、なかったらさらに下の行へ移動てしてみる ※空の<tr></tr>対策
			var number = $(parent).find('td').index(this.closest('td'));
			var target = $(prevTr).find("td:eq(" + (number) + ")");

			if (target.find(".arrowable").length) {
				target.find(".arrowable").focus();
			} else {
				prevTr = $(prevTr).prev();
				target = $(prevTr).find("td:eq(" + (number) + ")");
				if (target.find(".arrowable").length) {
					target.find(".arrowable").focus();
				}
			}
		}
		return false;
	}

	//  ← 左キー
	if ( e.keyCode == 37 ){
		if (index > 0){
			$(selector).eq(index-1).focus();
		}
		return false;
	}

	//  → 右キー
	if ( e.keyCode == 39 ){
		if (index < $(selector).length - 1 ){
			$(selector).eq(index+1).focus();
		}
		return false;
	}
});

セレクト欄で下矢印が効かないと困るので、除外した。
でも要素がSELECTかどうかを判定する処理がjQuery でうまく動かせなくて、ネイティブjava script のElementで取得し判定。
なんかかっこ悪い感じだけど、やり方わからずやっつけ。
他の作業者から引き継いだソースで、空の行が入っているという怪しい状態。綺麗につくってあるテーブルなら、小手先の回避策は不要。 このあと、テキスト入力欄で左右の移動ができない問題が発覚し、 結局Enterキーとtabでの移動に修正するハメに。そうだよなぁ。なんできづかなんだ。 フロントエンジニアとして不覚....orz 下は、EnterKeyバージョン。

    $(document).on("keydown", ".arrowable", function(e){

        var index = null;
        var selector = ".arrowable";
        var $this = $(this);

        // 直近の親要素tr 内にあるselector 要素の数を取得
        var parent = this.closest('tr');
        var index = $(selector).index(this);

        // thisのjQueryオブジェクトをjavascrptのDOMエレメントに変換する
        var domElement = $this[0];

        //  ↓ 下キー
        if (e.keyCode == 13 && !e.shiftKey){

            // 最後のtrでなければ、次のtrの同じtd内にあるarrowableへフォーカス
            if ($(parent).next().length) {

                var nextTr = $(parent).next();
                // 同じindexのtdがあれば移動、なかったら移動しない
                var number = $(parent).find('td').index(this.closest('td'));
                var target = $(nextTr).find("td:eq(" + (number) + ")");

                if (target.find(".arrowable").length) {
                    target.find(".arrowable").focus();
                } else {
                    nextTr = $(nextTr).next();
                    target = $(nextTr).find("td:eq(" + (number) + ")");
                    if (target.find(".arrowable").length) {
                        target.find(".arrowable").focus();
                    }
                }
            }
            return false;
        }

        //  ↑ shift + enterキー
        if ( e.shiftKey && e.keyCode == 13 ){
            // 最後のtrでなければ、次のtrの同じtd内にあるarrowableへフォーカス
            if ($(parent).prev().length) {

                var prevTr = $(parent).prev();
                // 同じindexのtdがあれば移動、なかったらさらに下の行へ移動てしてみる ※空の<tr></tr>対策
                var number = $(parent).find('td').index(this.closest('td'));
                var target = $(prevTr).find("td:eq(" + (number) + ")");

                if (target.find(".arrowable").length) {
                    target.find(".arrowable").focus();
                } else {
                    prevTr = $(prevTr).prev();
                    target = $(prevTr).find("td:eq(" + (number) + ")");
                    if (target.find(".arrowable").length) {
                        target.find(".arrowable").focus();
                    }
                }
            }
            return false;
        }

        //  ← shift + tabキー
        if ( e.shiftKey && e.tabKey ){
            if (index > 0){
                $(selector).eq(index-1).focus();
            }
            return false;
        }

        //  → tabキー
        if ( e.tabKey && !e.shiftKey ){
            if (index < $(selector).length - 1 ){
                $(selector).eq(index+1).focus();
            }
            return false;
        }
    });