skip to Main Content

I have created an application form with a validation function that requires the user to input the correct information within the boxes before clicking away from that focused box or an error message will show. Problem is when that happens, it affects all of the boxes instead of one that’s being focused on e.g. user does not put in correct email, error message for email, first name and last name etc will show.

Here is my code for my AppForm.jsx:

import React, { useState } from 'react'
import DialingCodes from './DialingCodes'
import FileUpload from './FileUpload'

const AppForm = () => {
  const [ focused, setFocused ] = React.useState(false)

  const handleFocus = (e) => {
    setFocused(true)
  }

  return (
    <>
      <div className='app-card'>
        <img 
          src='../../../../logo/Logo ROASTAR-white.PNG'
          width={150}
        />
        <form className='app-form'>
          <h3>Personal Details</h3>
          <div className='form-content'>
            <div className='first-name'>
              <input
                type='text'
                name='first_name'
                placeholder='First Name'
                autoComplete='given-name'
                pattern='^[A-Za-z]{2,16}$'
                onBlur={handleFocus}
                {/* This attribute is what activates the function */}
                data-focused={focused.toString()}
                required
              >
              </input>
              <span>First Name must be at least 2 characters or more</span>
            </div>
            <div className='last-name'>
              <input
                type='text'
                name='last_name'
                placeholder='Last Name'
                autoComplete='family-name'
                pattern='^[A-Za-z]{2,16}$'
                onBlur={handleFocus}
                data-focused={focused.toString()}
                required
              >
              </input>
              <span>Last Name must be at least 2 characters or more</span>
            </div>
            ...
          </div>
          ...
        </form>
      </div>
  </>

Here is my css code:

.form-content span {
  display: none;
}

.form-content input:invalid[data-focused="true"] ~ span {
  padding-left: 4.3vw;
  text-align: left;
  position: relative;
  top: 2.7em;
  font-size: 12px;
  display: block;
  color: red;
  z-index: 1;
}

Any help will be very much appreciated, thank you.

2

Answers


  1. You currently have a single bool, focused, shared by all inputs. That is only enough to represent whether any single field was focused, not which specific ones.

    You need to track in state which fields were "touched" and then use that information when deciding what value to render in data-focus:

    import React, { useState } from 'react'
    import DialingCodes from './DialingCodes'
    import FileUpload from './FileUpload'
    
    const AppForm = () => {
      const [ touched, setTouched ] = React.useState({})
    
      const handleBlur = (e) => {
        setTouched(prev => ({...prev, [e.target.name]: true}))
      }
      
      const isTouched = (name) => {
        return touched[name] === true
      }
    
      return (
        <>
          <div className='app-card'>
            <img 
              src='../../../../logo/Logo ROASTAR-white.PNG'
              width={150}
            />
            <form className='app-form'>
              <h3>Personal Details</h3>
              <div className='form-content'>
                <div className='first-name'>
                  <input
                    type='text'
                    name='first_name'
                    placeholder='First Name'
                    autoComplete='given-name'
                    pattern='^[A-Za-z]{2,16}$'
                    onBlur={handleBlur}
                    {/* This attribute is what activates the function */}
                    data-focused={isTouched('first_name').toString()}
                    required
                  >
                  </input>
                  <span>First Name must be at least 2 characters or more</span>
                </div>
                <div className='last-name'>
                  <input
                    type='text'
                    name='last_name'
                    placeholder='Last Name'
                    autoComplete='family-name'
                    pattern='^[A-Za-z]{2,16}$'
                    onBlur={handleBlur}
                    data-focused={isTouched('last_name').toString()}
                    required
                  >
                  </input>
                  <span>Last Name must be at least 2 characters or more</span>
                </div>
                ...
              </div>
              ...
            </form>
          </div>
      </>
    

    Just a note on this question, but implementing this kind of form logic yourself is usually "reinventing the wheel a little". Many React form libraries exist to make managing this type of thing easier, like react-hook-form. This particular "onBlur" behaviour with that library is simple, as shown in this example.

    Login or Signup to reply.
  2. If I understand correctly what you want to do, you need to use this selector .form-content input:invalid:focus-visible + span.

    https://developer.mozilla.org/fr/docs/Web/CSS/:focus-visible

    The :focus-visible pseudo-class applies while an element matches the :focus pseudo-class and the UA (User Agent) determines via heuristics that the focus should be made evident on the element. (Many browsers show a "focus ring" by default in this case.)

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search