处理键盘 回车、上下左右方向键切换表单输入框

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

 //#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

 

posted @ 2024-09-27 14:08  不知名路人!  阅读(134)  评论(0)    收藏  举报