记录:处理键盘 回车、上下左右方向键切换输入框

//#region 处理键盘 回车、上下左右方向键切换输入框
handleKeyDown(event) {
if (!isEmpty(this.$refs.formBuild)) {
// 仅处理当前组件的回车事件
const target = event.target;
const formBuildElement = this.$refs.formBuild.$el;
// 检查事件目标是否在 ng-form-build 组件内部
if (formBuildElement.contains(target) && event.key === "Enter") {
this.moveToNextInput();
event.preventDefault();
} else if (
event.key === "ArrowUp" ||
event.key === "ArrowDown" ||
event.key === "ArrowLeft" ||
event.key === "ArrowRight"
) {
this.moveToAdjacentInput(event.key);
event.preventDefault();
}
}
},
//逻辑查找下一个input
moveToNextInput() {
const inputs = this.getInputElements();
const activeElement = document.activeElement;
const currentIndex = inputs.indexOf(activeElement);
if (currentIndex > -1 && currentIndex < inputs.length - 1) {
inputs[currentIndex + 1].focus();
}
},
//获取所有input
getInputElements() {
return Array.from(this.$el.querySelectorAll("input,textarea"));
},
moveToAdjacentInput(key) {
// 获取所有输入框元素
const inputs = this.getInputElements();
// 获取当前获得焦点的输入框
const activeElement = document.activeElement;
// 找到当前焦点输入框的索引
const currentIndex = inputs.indexOf(activeElement);
// 如果当前焦点不在输入框列表中,返回
if (currentIndex === -1) return;
// 按行分组输入框
const rows = this.groupInputsByRow(inputs);
// 获取当前焦点输入框
const currentElement = inputs[currentIndex];
const currentRect = currentElement.getBoundingClientRect();
let nextElement = null;
// 根据按下的方向键决定如何移动焦点
switch (key) {
case "ArrowUp":
nextElement = this.findElementInDirection(rows, currentRect, "up");
break;
case "ArrowDown":
nextElement = this.findElementInDirection(rows, currentRect, "down");
break;
case "ArrowLeft":
nextElement = this.findElementInDirection(rows, currentRect, "left");
break;
case "ArrowRight":
nextElement = this.findElementInDirection(rows, currentRect, "right");
break;
}
// 如果找到下一个输入框,则将焦点设置到它上面
if (nextElement !== null) {
nextElement.focus();
}
},
findElementInDirection(rows, rect, direction) {
let targetElement = null;
// 根据方向找到目标输入框
switch (direction) {
case "up":
targetElement = this.findElementByPosition(rows, rect, "top");
break;
case "down":
targetElement = this.findElementByPosition(rows, rect, "bottom");
break;
case "left":
targetElement = this.findElementByPosition(rows, rect, "left");
break;
case "right":
targetElement = this.findElementByPosition(rows, rect, "right");
break;
}
return targetElement;
},
findElementByPosition(rows, rect, position) {
let bestElement = null;
let bestDistance = Infinity;
// 遍历所有输入框,找到最接近目标位置的输入框
rows.flat().forEach((input) => {
const inputRect = input.getBoundingClientRect();
let distance = Infinity;
switch (position) {
case "top":
// 查找在当前矩形上方且在同一列范围内的输入框
if (
inputRect.bottom <= rect.top &&
inputRect.left < rect.right &&
inputRect.right > rect.left
) {
distance = rect.top - inputRect.bottom;
}
break;
case "bottom":
// 查找在当前矩形下方且在同一列范围内的输入框
if (
inputRect.top >= rect.bottom &&
inputRect.left < rect.right &&
inputRect.right > rect.left
) {
distance = inputRect.top - rect.bottom;
}
break;
case "left":
// 查找在当前矩形左侧且在同一行范围内的输入框
if (
inputRect.right <= rect.left &&
inputRect.top < rect.bottom &&
inputRect.bottom > rect.top
) {
distance = rect.left - inputRect.right;
}
break;
case "right":
// 查找在当前矩形右侧且在同一行范围内的输入框
if (
inputRect.left >= rect.right &&
inputRect.top < rect.bottom &&
inputRect.bottom > rect.top
) {
distance = inputRect.left - rect.right;
}
break;
}
// 更新最佳输入框
if (distance < bestDistance) {
bestDistance = distance;
bestElement = input;
}
});
return bestElement;
},
groupInputsByRow(inputs) {
const rows = [];
let currentRow = [];
let lastTop = null;
// 遍历所有输入框,按行分组
inputs.forEach((input) => {
const rect = input.getBoundingClientRect();
// 如果当前输入框的顶部与上一个输入框相差不大,认为它在同一行
if (lastTop === null || Math.abs(rect.top - lastTop) < 5) {
currentRow.push(input);
} else {
rows.push(currentRow);
currentRow = [input];
}
lastTop = rect.top;
});
// 添加最后一行
if (currentRow.length > 0) {
rows.push(currentRow);
}
return rows;
},
//#endregion