DEV Community

Cover image for React time input⏳
Andriy Chemerynskiy
Andriy Chemerynskiy

Posted on

React time input⏳

At my work, I had to add input which is quite similar to YouTube's "Start at" input for sharing video:

Alt Text

I am a pro level programmer, so the first thing I did was googling. However, all inputs that I found were different from what I needed.

So I implemented my own clone of YouTube's "Start at" time input.


Before I jump into the implementation part, here is the demo of what we are going to archive:

Alt Text

Behind the scenes:

  • User types in
  • When he finishes typing and clicks somewhere else onBlur event is fired
  • Getting seconds from input value (getSecondsFromHHMMSS(value)
  • Converting those seconds back to hh:mm:ss format (toHHMMSS(seconds))

It may sound complicated now, but it will be clear a moment later 😉


So let's start coding.

Let's add a basic structure:

input {
  background-color: rgba(96, 108, 110, 0.15);
  height: 40px;
  padding: 10px 10px;
  color: #606c6e;
  font-size: 30px;
  outline: 0 solid transparent;
  border: 0 solid transparent;
  width: 100%;
  border-radius: 4px;
  letter-spacing: -0.4px;
  padding: 10px 18px;
  width: 200px;
}

body {
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-size: 30px;
  text-align: center;
}
Enter fullscreen mode Exit fullscreen mode
const TimeInput = () => {
  const [value, setValue] = React.useState("0:00");

  const onChange = (event) => {
    setValue(event.target.value);
  };

  return (
    <input 
     type="text"
     onChange={onChange}
     value={value}
     />
  );
};

ReactDOM.render(<TimeInput />,
  document.getElementById("root")
);
Enter fullscreen mode Exit fullscreen mode

We created TimeInput component that has an initial value set to O:00 and we update the state on every change.

Now we will add onBlur handler:

const TimeInput = () => {
  const [value, setValue] = React.useState("0:00");

+  const onBlur = (event) => {
+    const value = event.target.value;
+    const seconds = Math.max(0, getSecondsFromHHMMSS(value));
+
+    const time = toHHMMSS(seconds);
+    setValue(time);
+  };

  ...

  return (
     <input
      type="text"
      onChange={onChange}
+     onBlur={onBlur}
      value={value}
     />
  );
};

ReactDOM.render(<TimeInput />,
  document.getElementById("root")
);
Enter fullscreen mode Exit fullscreen mode

onBlur function makes the same steps that I described earlier:

  • Getting seconds from input value (getSecondsFromHHMMSS(value)
  • Converting those seconds back to hh:mm:ss format (toHHMMSS(seconds))

Math.max(0, getSecondsFromHHMMSS(value)) returns 0 if seconds are negative, so we don't have wrong values in our input.

Now let's take a closer look at getSecondsFromHHMMSS:

  const getSecondsFromHHMMSS = (value) => {
    const [str1, str2, str3] = value.split(":");

    const val1 = Number(str1);
    const val2 = Number(str2);
    const val3 = Number(str3);

    if (!isNaN(val1) && isNaN(val2) && isNaN(val3)) {
    // seconds
      return val1;
    }

    if (!isNaN(val1) && !isNaN(val2) && isNaN(val3)) {
    // minutes * 60 + seconds
      return val1 * 60 + val2;
    }

    if (!isNaN(val1) && !isNaN(val2) && !isNaN(val3)) {
    // hours * 60 * 60 + minutes * 60 + seconds
      return val1 * 60 * 60 + val2 * 60 + val3;
    }

    return 0;
  };
Enter fullscreen mode Exit fullscreen mode

We split the input's value by ":". Then we grab 3 values from this array and convert them to numbers.

Depending on the context val1, val2, val3 represent different values and handle those cases:

  • Only seconds (eg. 10, 40, 70 etc.)
  • Minutes and seconds (eg. 1:20, 0:10, 14:40 etc.)
  • Hours, minutes, and seconds (eg. 1:12:40, 123:49:12 etc.)

Finally, we format seconds from getSecondsFromHHMMSS back to hh:mm:ss format:

const toHHMMSS = (secs) => {
    const secNum = parseInt(secs.toString(), 10);
    const hours = Math.floor(secNum / 3600);
    const minutes = Math.floor(secNum / 60) % 60;
    const seconds = secNum % 60;

    return [hours, minutes, seconds]
      .map((val) => (val < 10 ? `0${val}` : val))
      .filter((val, index) => val !== "00" || index > 0)
      .join(":")
      .replace(/^0/, "");
};
Enter fullscreen mode Exit fullscreen mode
  • We get hours, minutes, seconds from total seconds using simple math
  • map those values and if the value is less than 10 we add 0 to it
  • We don't want to show values like 00 (exception is seconds), so we filter
  • join our strings with ":"
  • replace leading zero

And it's working 😎

Codepen: https://codepen.io/andrewchmr-the-vuer/pen/wvWLRVw


I hope this article was helpful and saved you the time of thinking about how to do this 😉

Thanks for reading!

Top comments (9)

Collapse
 
isarisariver profile image
Marian

I am a pro level programmer, so the first thing I did was googling

Nice 🙃

Collapse
 
andrewchmr profile image
Andriy Chemerynskiy

😁

Collapse
 
rubenvillarnet profile image
Rubén Villar Grela

Great code!

Thanks a lot!

Collapse
 
andrewchmr profile image
Andriy Chemerynskiy

Thank you 😊

Collapse
 
deepak22196 profile image
Deepak Malik

Somehow my onBlur is getting triggered only when I click outside the the entire page, and not when I click outside input box. When I click outside input box, it's cursor is still blinking, it stays in focus, and onBlur doesn't work. Can you help about this?

Collapse
 
lesmiks profile image
Oles

That's so cool!

Collapse
 
andrewchmr profile image
Andriy Chemerynskiy

Thanks!

Collapse
 
kplc_caxapok profile image
крістінич

Good work 👏

Collapse
 
andrewchmr profile image
Andriy Chemerynskiy

Thank you 😊