需求
项目需要表格内的编辑框可以根据上下左右方向键去自动聚焦
实现思路
查阅网上的资料,是给表格内的每一个编辑框增加一个标识,要么类,要么是类似递增的一个id,然后监听键盘事件,但是我觉得这样不好,需要手动给每个编辑框加标识,太麻烦了。
我的思路是写一个指令"keyboard-navigation",仅需给需要的表格绑定该指令即可(多个表格也可以),极大的减少手部运动,果然懒是第一生产力。
<el-table :data="tableData" v-keyboard-navigation>
</el-table>
指令大概实现思路是,获取当前表格中的聚焦元素,如果是左右键则向上递归找到td,然后再定位该td,获取相邻td后向下递归找到编辑框进行focus聚焦,上下键思路也差不多,需要找到该td的相邻行,我这里的代码如果碰到禁用的编辑框会跳过。
在directive.js文件中添加下方代码
Vue.directive("keyboard-navigation", {
bind(el) {
const handleKeydown = (event) => {
const activeElement = document.activeElement;
// 检查当前聚焦的元素是否为 input
if (activeElement && activeElement.tagName.toLowerCase() === "input") {
// 检查 input 是否在表格内
let parent = activeElement.parentElement;
let isInTable = false;
while (parent) {
if (parent.tagName.toLowerCase() === "table") {
isInTable = true;
break;
}
parent = parent.parentElement;
}
if (isInTable) {
// 处理方向键
switch (event.key) {
case "ArrowUp":
case "Up":
event.preventDefault();
navigateInput(activeElement, "up");
break;
case "ArrowDown":
case "Down":
event.preventDefault();
navigateInput(activeElement, "down");
break;
case "ArrowLeft":
case "Left":
event.preventDefault();
navigateInput(activeElement, "left");
break;
case "ArrowRight":
case "Right":
event.preventDefault();
navigateInput(activeElement, "right");
break;
default:
break;
}
}
}
};
// 绑定事件
el.addEventListener("keydown", handleKeydown);
// 清理事件监听
el._removeKeyboardListener = () => {
el.removeEventListener("keydown", handleKeydown);
};
},
unbind(el) {
// 解绑事件
if (el._removeKeyboardListener) {
el._removeKeyboardListener();
}
},
});
// 导航函数,根据方向查找相邻的输入字段并聚焦
function navigateInput(currentInput, direction) {
const td = currentInput.closest("td");
if (!td) return;
const tr = td.parentElement;
if (!tr) return;
const table = tr.closest("table");
if (!table) return;
const rows = Array.from(table.rows);
const rowIndex = rows.indexOf(tr);
const cellIndex = Array.from(tr.cells).indexOf(td);
let targetRow, targetCell;
switch (direction) {
case "up":
if (rowIndex > 0) targetRow = rows[rowIndex - 1];
break;
case "down":
if (rowIndex < rows.length - 1) targetRow = rows[rowIndex + 1];
break;
case "left":
targetCell = tr.cells[cellIndex - 1];
break;
case "right":
targetCell = tr.cells[cellIndex + 1];
break;
default:
break;
}
if (targetRow) {
// 递归查找可聚焦的 input
const targetInput = findFocusableInput(targetRow, cellIndex, direction);
if (targetInput) {
targetInput.focus();
}
} else if (targetCell) {
// 在当前行查找可聚焦的 input
const targetInput = findFocusableInput(targetCell, null, direction);
if (targetInput) {
targetInput.focus();
}
}
}
// 递归查找可聚焦的输入框
function findFocusableInput(target, cellIndex, direction) {
let input = null;
if (target instanceof HTMLTableCellElement) {
// 在当前单元格查找 input
input = target.querySelector("input:not([disabled])");
if (!input) {
// 若当前单元格没有可聚焦的 input,继续向左或向右查找
const siblingCells = Array.from(target.parentElement.cells);
const nextCellIndex =
siblingCells.indexOf(target) +
(direction === "left" ? -1 : direction === "right" ? 1 : 0);
if (nextCellIndex >= 0 && nextCellIndex < siblingCells.length) {
input = findFocusableInput(
siblingCells[nextCellIndex],
null,
direction
);
}
}
} else if (target instanceof HTMLTableRowElement) {
// 在行中查找指定列的单元格
if (cellIndex >= 0 && cellIndex < target.cells.length) {
const targetCell = target.cells[cellIndex];
if (targetCell) {
input = targetCell.querySelector("input:not([disabled])");
if (!input) {
// 若当前单元格没有可聚焦的 input,继续查找上一行或下一行
if (direction === "up" && target.previousElementSibling) {
input = findFocusableInput(
target.previousElementSibling,
cellIndex,
direction
);
} else if (direction === "down" && target.nextElementSibling) {
input = findFocusableInput(
target.nextElementSibling,
cellIndex,
direction
);
}
}
}
}
}
return input;
}
注意
如果指令绑定是在el-table上的话,需要注意如果有用到固定列fixed功能,固定区域与非固定区域是分开的不同表格,这个跟el-table组件本身的实现逻辑有关,这会导致固定区域跳不到非固定区域!
标签:case,direction,方向键,const,target,break,VUE2,input,编辑框 From: https://blog.csdn.net/weixin_47416493/article/details/145152908