javascriptreactjsonbluronfocus

Calendar component loses focus when I click on it


What I have achieved is when I click on the input field to show the Calendar component, and when I click outside of it to hide it. However, the problem is when I click on the icon (Calendar component) itself, I lose the focus, how to fix that? I do not want to lose the focus of the icon when I click on it.

const [isFocused, setIsFocused] = useState(false);

<form onSubmit={addTask}>
  <div>
    <input
      id="task"
      placeholder=' Add task to "Inbox" on "Tomorrow"'
      className={styles.input}
      onChange={handleChange}
      value={taskName}
      onFocus={() => setIsFocused(true)}
      onBlur={() => setIsFocused(false)}
    ></input>
    {isFocused && <Calendar />}
  </div>

Calendar component

const Calendar = () => {
  const [isShown, setIsShown] = useState(false);

  const clickIcon = (e) => {
    e.preventDefault();
    setIsShown((current) => !current);
  };

  return (
    <div>
      <CalendarMonthIcon
        onClick={clickIcon}
        className={styles.calendar}
      ></CalendarMonthIcon>
      {isShown && <Datepicker />}
    </div>
  );
};

I currently can't stop the Calendar icon from losing the focus when click on it.


Solution

  • You loose the focus of your input, because the click on the icon triggers the onBlur function of the input. Therefor the input looses focus and the Calendar component is beeing closed.

    By adding the following function to your onBlur event, it should check if there item pressed is a child element. If that's the case the input should remain focused and the Calendar should stay open.
    IMPORTANT: you have to set that onBlur function on the parent div that includes the input and calendar, otherwise the function won't work, because it looks if the click contains a child. This is also a good way to keep track if the click was outside of your component.

    const closeDropdown = (event) => {
     if (!event.currentTarget.contains(event.relatedTarget)) {
      setIsFocused(false);  
     }
    }