首页 > 解决方案 > 如何使用keyevents在输入字段中防止负数和超过一定限制的数字,并让用户在可用数字之间循环?

问题描述

我有一个名为TimePickerComponent的可重用组件,我用它来显示一个输入字段,用户可以在其中输入小时和分钟。我有一些箭头出现在悬停上,让用户在几小时内向上或向下,以及另一个箭头在分钟内(我在附件中放了截图)

对于分钟,如果用户使用向上箭头到达 59 并再次单击,则下一个数字将是 00。而如果用户使用向下箭头并到达数字 00,则下一个值将是 60。所以基本上它循环。小时数也是如此,但从 0 到 24。使用箭头和 onClick 事件,一切正常。

问题是,当我不使用箭头并且用户使用keyDown事件时,数字变为负无穷大,而当用户使用keyUp事件时,数字变为无穷大。所以我想知道是否应该创建一个可以覆盖此输入字段行为的函数,如果是,如何?我尝试了这些事件,但没有成功。这是我使用箭头但不使用 keyPress 事件的初始代码,我还在附件中添加了页面部分的屏幕截图:

function TimePickerComponent({ hours, setHours, minutes, setMinutes }) {


  // INTERVAL STATES ARROWS
  const [showIntervalFirstHoursArrow, setShowIntervalFirstHoursArrow] =
    useState(false);
  const [showIntervalFirstMinutesArrow, setShowIntervalFirstMinutesArrow] =
    useState(false);

  // INCREMENTS/DECREMENTS HOURS INTERVALS
  const incrementFirstHour = () => {
    setHours((prevHours) => {
      // if there is nothing
      if (!prevHours || +prevHours >= 24) {
        return '0';
      } else {
        return String(+prevHours + 1);
      }
    });
  };

  const decrementFirstHours = () => {
    setHours((prevHours) => {
      // if there is nothing
      if (!prevHours) {
        return '0';
      } else if (+prevHours <= 0) {
        return '24';
      } else {
        const currentHours = String(+prevHours - 1);
        if (currentHours.length === 1) {
          return '0' + currentHours;
        }
        return currentHours;
      }
    });
  };

  // INCREMENTS/DECREMENTS MINUTES INTERVALS
  const incrementFirstMinutes = () => {
    setMinutes((prevMinutes) => {
      // if there is nothing
      if (!prevMinutes || +prevMinutes >= 59) {
        return '00';
      } else {
        const currentMinute = String(+prevMinutes + 1);
        if (currentMinute.length === 1) {
          return '0' + currentMinute;
        }
        return currentMinute;
      }
    });
  };
  const decrementFirstMinutes = () => {
    setMinutes((prevMinutes) => {
      // if there is nothing
      if (!prevMinutes) {
        return '00';
      } else if (+prevMinutes <= 0) {
        return '59';
      } else {
        const currentMinute = String(+prevMinutes - 1);
        if (currentMinute.length === 1) {
          return '0' + currentMinute;
        }
        return currentMinute;
      }
    });
  };

  return (
    <Inputs>
      <Container>
        <Selection
          onMouseEnter={() => setShowIntervalFirstHoursArrow(true)}
          onMouseLeave={() => setShowIntervalFirstHoursArrow(false)}
        >
          {showIntervalFirstHoursArrow && (
            <p className="icon" onClick={incrementFirstHour}>
              <IoIosArrowUp />
            </p>
          )}
          <SquareInput
            type="number"
            onChange={(e) => setHours(e.target.value)}
            value={hours}
          />
          {showIntervalFirstHoursArrow && (
            <p className="icon" onClick={decrementFirstHours}>
              <IoIosArrowDown />
            </p>
          )}
        </Selection>
        <p>:</p>
        <Selection
          onMouseEnter={() => setShowIntervalFirstMinutesArrow(true)}
          onMouseLeave={() => setShowIntervalFirstMinutesArrow(false)}
        >
          {showIntervalFirstMinutesArrow && (
            <p className="icon" onClick={incrementFirstMinutes}>
              <IoIosArrowUp />
            </p>
          )}
          <SquareInput
            min="0"
            type="number"
            onChange={(e) => {
              if (e.target.value.split('').length > 2) return;
              // if(e.target.value == 0) return;
              if (+e.target.value > 60) {
                return;
                // setMinutes('0');
              } else if (+e.target.value < 0) {
                setMinutes('60');
              } else {
                setMinutes(e.target.value);
              }
            }}
            value={minutes}
          />
          {showIntervalFirstMinutesArrow && (
            <p className="icon" onClick={decrementFirstMinutes}>
              <IoIosArrowDown />
            </p>
          )}
        </Selection>
      </Container>
    </Inputs>
  );
}

// STYLES
const Inputs = styled.div`
  display: flex;
  align-items: center;

  p {
    font-size: 0.8em;
    text-align: center;
  }
`;
const Container = styled.div`
  border: 1px solid #c5c5c5;
  border-radius: 10px;
  height: 40px;
  margin: 0 5px;
  display: flex;
  align-items: center;
`;
const Selection = styled.div`
  .icon {
    font-size: 1.2em;
    padding: 10px;
    cursor: pointer;
  }
`;

const SquareInput = styled.input`
  width: 50px;
  border: none;
  outline: none;
  font-size: 1.1em;
  text-align: center;
  ::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
  }
  ::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
  }
  /* /* Firefox */
  /* input[type=number] {
  -moz-appearance: textfield;
} */
`;

export default TimePickerComponent;

在此处输入图像描述

标签: javascriptreactjs

解决方案


您可以尝试更改方法并统一处理值的增加/减少的函数和负责处理格式的函数。
这将在值更改后触发,无论是在箭头单击还是键盘事件上:

function TimePickerComponent({ hours, setHours, minutes, setMinutes }) {
  // INTERVAL STATES ARROWS
  const [showIntervalFirstHoursArrow, setShowIntervalFirstHoursArrow] =
    useState(false);
  const [showIntervalFirstMinutesArrow, setShowIntervalFirstMinutesArrow] =
    useState(false);

  // INCREMENTS/DECREMENTS HOURS INTERVALS
  const incrementDecrementHours = (isIncrement) => {
    setHours((prevHours) => (isIncrement ? prevHours + 1 : prevHours - 1));
  };

  const handleFirstHoursChange = () => {
    setHours((prevHours) => {
      if (!prevHours || prevHours >= 24) {
        return '0';
      } else if (prevHours <= 0) {
        return '24';
      } else if (prevHours.length === 1) {
        return '0' + prevHours;
      } else {
        return prevHours;
      }
    });
  };

  // INCREMENTS/DECREMENTS MINUTES INTERVALS
  const incrementDecrementMinutes = (isIncrement) => {
    setMinutes((prevMinutes) =>
      isIncrement ? prevMinutes + 1 : prevMinutes - 1
    );
  };

  const handleFirstMinutesChange = () => {
    setMinutes((prevMinutes) => {
      if (!prevMinutes || prevMinutes >= 59) {
        return '00';
      } else if (prevMinutes <= 0) {
        return '59';
      } else if (prevMinutes.length === 1) {
        return '0' + prevMinutes;
      } else {
        return prevMinutes;
      }
    });
  };

  return (
    <Inputs>
      <Container>
        <Selection
          onMouseEnter={() => setShowIntervalFirstHoursArrow(true)}
          onMouseLeave={() => setShowIntervalFirstHoursArrow(false)}
        >
          {showIntervalFirstHoursArrow && (
            <p className='icon' onClick={incrementDecrementHours(true)}>
              <IoIosArrowUp />
            </p>
          )}
          <SquareInput
            type='number'
            onChange={handleFirstHoursChange}
            value={hours}
          />
          {showIntervalFirstHoursArrow && (
            <p className='icon' onClick={incrementDecrementHours(false)}>
              <IoIosArrowDown />
            </p>
          )}
        </Selection>
        <p>:</p>
        <Selection
          onMouseEnter={() => setShowIntervalFirstMinutesArrow(true)}
          onMouseLeave={() => setShowIntervalFirstMinutesArrow(false)}
        >
          {showIntervalFirstMinutesArrow && (
            <p className='icon' onClick={incrementDecrementMinutes(true)}>
              <IoIosArrowUp />
            </p>
          )}
          <SquareInput
            min='0'
            type='number'
            onChange={handleFirstMinutesChange}
            value={minutes}
          />
          {showIntervalFirstMinutesArrow && (
            <p className='icon' onClick={incrementDecrementMinutes(false)}>
              <IoIosArrowDown />
            </p>
          )}
        </Selection>
      </Container>
    </Inputs>
  );
}

推荐阅读