skip to Main Content

I have JavaScript code, in which I change the input field css class on invalid event to add an error class to a parent div having the class form-row.

I furthermore attach then an event listener on the form fields fields on input and click to remove the error class once the user interacts with the form fields (text, textarea, dropdown select boxes, …)

Now, those form fields already have event handlers attached, and some stop event propagation.

Certain elements won’t call formElementRow.classList.remove('error'); as some other event handler acts first.

Is there a quick way to force my event handler that I define here to take precdence while still allowing the other event handlers to act on the page?

I control the html of the form, I do not want to change anything with the code that registers the other event handlers.

This is my code that works for all my form elements except those who stop the event propagation:

const formErrorStatesOnBrowserSideValidation = () => {
    const targetClass = '.form-row';
    document.addEventListener('invalid', (invalidEvent) => {
        const {target} = invalidEvent;
        const formElementRow = target.closest(targetClass);

        if (formElementRow) {
            formElementRow.classList.add('error');
        }

        ['input', 'click'].forEach(event => {
            // some elements have other js / jQuery applied to them that stops event propagation hence the class removal is never called to those
            target.addEventListener(event, () => {
                formElementRow.classList.remove('error');
            });
        });
    }, true);
};
formErrorStatesOnBrowserSideValidation();

This is a follow-up question from: What event to listen for when a user clicks submit button in html5 invalid form?

2

Answers


  1. Since the OP seems to have to handle an environment where event-listeners are attached with a true value for the addEventListener‘s third usecapture‘s parameter, the OP has to handle each form-control’s invalid event/state at the most convenient parent level of all the effected form-controls, preferably the form-element itself and not necessarily at document-level.

    At such a parent level it does not matter whether any of the event-handler’s, the OP is not in control of, prevents the propagation of an invalid event-type; rather the other way around, the OP could prevent the invalid-event from reaching its targeted handlers for every subscribed form-control.

    function handleInvalidControl (evt) {
    
      // - none of the below three lines does interfer with the event
      //   handling of any other invalid form-control because each
      //   invalid control dispatches its invalid event separately.
      //
      evt.cancleBubble = true;
      evt.stopPropagation();
      evt.stopImmediatePropagation();
      
      console.log('control ...', {
        target: evt.target,
        currentTarget: evt.currentTarget,
      });
    }
    document
      .querySelectorAll('[type="text"]')
      .forEach(control =>
        control.addEventListener('invalid', handleInvalidControl, true)
      );
    
    document
      .forms[0]
      .addEventListener('invalid', evt => {
    
        // - if invoked, it will stop the propagation
        //   towards any effected form-control
        //
        // evt.stopPropagation();
    
        console.log('form ...', {
          target: evt.target,
          currentTarget: evt.currentTarget,
        });
    
      }, true);
    
    document
      .forms[0]
      .addEventListener('submit', evt => {
    
        evt.preventDefault();
        evt.currentTarget.reset();
    
        console.clear();
      });
    body { margin: 0; }
    form { width: 44%; }
    label { display: block; margin: 8px 0; }
    .as-console-wrapper { left: auto!important; top: 8px; width: 56%; min-height: 100%; }
    <form>
      <fieldset>
        <legend>validation test</legend>
        <label>
          <span>No digits</span>
          <input type="text" pattern="D+" required />
        </label>
        <label>
          <span>No word characters</span>
          <input type="text" pattern="[W+]+" required />
        </label>
      </fieldset>
      <button type="submit">Submit</button>
    </form>
    Login or Signup to reply.
    1. Using setTimeout to delay the execution of removing the class:
      In this method, with setTimeout, you postpone the deletion of the class until after the completion of other events. This prevents interference with other recorded events.

      ”javaScript

    const formErrorStatesOnBrowserSideValidation = () => {
        const targetClass = '.form-row';
        document.addEventListener('invalid', (invalidEvent) => {
            const { target } = invalidEvent;
            const formElementRow = target.closest(targetClass);
    
            if (formElementRow) {
                formElementRow.classList.add('error');
            }
    
            ['input', 'click'].forEach(event => {
                target.addEventListener(event, () => {
                    setTimeout(() => {
                        formElementRow.classList.remove('error');
                    }, 0);
                });
            });
        }, true);
    };
    formErrorStatesOnBrowserSideValidation();
     const formErrorStatesOnBrowserSideValidation = () => {
        const targetClass = '.form-row';
        document.addEventListener('invalid', (invalidEvent) => {
            const { target } = invalidEvent;
            const formElementRow = target.closest(targetClass);
    
            if (formElementRow) {
                formElementRow.classList.add('error');
            }
    
            ['input', 'click'].forEach(event => {
                target.addEventListener(event, () => {
                    setTimeout(() => {
                        formElementRow.classList.remove('error');
                    }, 0);
                });
            });
        }, true);
    };
    formErrorStatesOnBrowserSideValidation();
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search