javascriptevent-handlingdom-eventsaddeventlistenerevent-delegation

How to use event-delegation properly instead of adding a 2nd, concurring, event-listener for the same event-type?


I am using a document.addEventListener("DOMContentLoaded", function () to force the input pattern (first must be a letter, then prevent consecutive spaces, etc.)

Now I want to make a 2nd document.addEventListener("DOMContentLoaded", function () for an email. (I want to prevent multiple .'s and prevent more than 1 @)

Here is my code for both of the above but it isn't working, it only capitalizes the email (i kept that option on to see if that part works, which it does but nothing else does work.)

Here is the first part (I also tried something else, removing the first }); at the document.addEventListener("DOMContentLoaded", function () at the start of the 2nd as I heard that should work but that didn't work either.

document.addEventListener("DOMContentLoaded", function() {
  // Function to handle all keypress events
  function handleKeyPress(event) {
    const input = event.target;
    const char = String.fromCharCode(event.which);

    // Prevent first character from being a space
    if (input.selectionStart === 0 && event.code === "Space") {
      event.preventDefault();
      return;
    }

    // Prevent first character from being a non-letter
    if (input.selectionStart === 0 && !/^[a-zA-Z]$/.test(char)) {
      event.preventDefault();
      return;
    }

    // Prevent consecutive spaces
    const lastChar = input.value.charAt(input.selectionStart - 1);
    if (char === " " && lastChar === " ") {
      event.preventDefault();
      return;
    }
  }

  // Attach event listeners to input fields
  const inputs = document.querySelectorAll("input[name='real_name'], input[name='display_name']");
  inputs.forEach(input => {
    input.addEventListener("keypress", handleKeyPress);

    // Set text-transform to capitalize to force capitalization of each word
    input.style.textTransform = "capitalize";
  });
});
document.addEventListener("DOMContentLoaded", function() {
  // Function to handle all keypress events
  function handleKeyPress2(event) {
    const input = event.target;
    const char = String.fromCharCode(event.which);

    // Prevent first character from being a space
    if (input.selectionStart === 0 && event.code === "Space") {
      event.preventDefault();
      return;
    }

    // Prevent consecutive spaces
    const lastChar = input.value.charAt(input.selectionStart - 1);
    if (char === "@" && lastChar === "@") {
      event.preventDefault();
      return;
    }

    var key = event.keyCode || event.charCode || event.which;
    if (key == 32) {
      return false;
    } else {
      return key;
    }
  }

  // Attach event listeners to input fields
  const inputs = document.querySelectorAll("input[name='email']");
  inputs.forEach(input => {
    input.addEventListener("keydown", handleKeyPress2);

    // Set text-transform to capitalize to force capitalization of each word
    input.style.textTransform = "capitalize";
  });
});


Solution

  • First, one does not even necessarily need a 'DOMContentLoaded' handler for dealing with input-element related 'keypress' events, as long as the element-query for the latter's registration starts after the targeted element/s have been made accessible within the DOM.

    Second, and regardless of whether one registers the event-handling at 'DOMContentLoaded', one always should make use of event-delegation in order to handle input-element related 'keypress' events.

    One would register the event-listener at the closest available parent of all involved input-elements. The main-handler then just needs to validate and distinguish the relevant input-elements in order to forward the element-type/element-name specific handling to the targeted element's related, more specialized, handler function.

    Edit

    The above described, and beneath implemented approach (event-delegation) achieves exactly what the OP just recently did ask for in one of the comments ...

    "How can I make 2 separate systems? One to watch and modify changes to the names, and another to work separately with the email?" Antarctica-UFO // Commented 1 hour ago

    function handleNameFieldKeypress(target, evt) {
      console.log('handleNameFieldKeypress ... target ...', target);
    
      const char = String.fromCharCode(evt.which);
    
      const { selectionStart } = target;
    
      const isNotValid = (selectionStart === 0 && (
    
        evt.code === 'Space' ||
        !/^[a-zA-Z]$/.test(char)
    
      )) || (
    
        char === ' ' &&
        target.value.charAt(selectionStart - 1) === ' '
      );
      if (isNotValid) {
    
        evt.preventDefault();
      }
    }
    function handleEmailFieldKeypress(target, evt) {
      console.log('handleEmailFieldKeypress ... target ...', target);
    
      const { selectionStart } = target;
    
      const isNotValid =
        (selectionStart === 0 && evt.code === 'Space') || (
    
          String.fromCharCode(evt.which) === '@' &&
          target.value.charAt(selectionStart - 1) === '@'
        );
    
      if (isNotValid) {
        evt.preventDefault();
    
        return;
      }
      const key = evt.keyCode || evt.charCode || evt.which;
    
      return (key !== 32) && key;
    }
    
    function handleInputFieldKeypress(evt) {
      const { target } = evt;
    
      let result;
    
      if (target.matches('input[name="real_name"], input[name="display_name"]')) {
    
        result = handleNameFieldKeypress(target, evt);
    
      } else if (target.matches('input[name="email"]')) {
    
        result = handleEmailFieldKeypress(target, evt);
      }
      return result;
    }
    
    document
      .addEventListener('DOMContentLoaded', () => {
    
        document
          .querySelector('form')
          .addEventListener('keypress', handleInputFieldKeypress);
      });
    body { margin: 0; }
    form { width: 32%; }
    fieldset { margin: 0 0 8px 0; }
    label, label > span { display: block; }
    label > span { margin: 4px 0 2px 0; }
    /*
    input[name="email"],
    input[name="real_name"],
    input[name="display_name"] { text-transform: capitalize; }
    */
    .as-console-wrapper { left: auto!important; width: 66%; min-height: 100%; }
    <form>
      <fieldset>
        <legend>User Data</legend>
      
        <label>
          <span>Real Name</span>
          <input type="text" name="real_name" />
        </label>
      
        <label>
          <span>Display Name</span>
          <input type="text" name="display_name" />
        </label>
    
      </fieldset>
      <fieldset>
      
        <label>
          <span>E-mail Address</span>
          <input type="email" name="email" />
        </label>
    
      </fieldset>
    </form>
    
    <!--
    <script>
    
    // - one does not even need a 'DOMContentLoaded' event listener
    //   for dealing with input-element related 'keypress' events,
    //   as long as the element-query starts after the targeted
    //   element/s have been made accessible within the DOM.
    
    function handleNameFieldKeypress(target, evt) {
      console.log('handleNameFieldKeypress ... target ...', target);
    
      const char = String.fromCharCode(evt.which);
    
      const { selectionStart } = target;
    
      const isNotValid = (selectionStart === 0 && (
    
        evt.code === 'Space' ||
        !/^[a-zA-Z]$/.test(char)
    
      )) || (
    
        char === ' ' &&
        target.value.charAt(selectionStart - 1) === ' '
      );
      if (isNotValid) {
    
        evt.preventDefault();
      }
    }
    function handleEmailFieldKeypress(target, evt) {
      console.log('handleEmailFieldKeypress ... target ...', target);
    
      const { selectionStart } = target;
    
      const isNotValid =
        (selectionStart === 0 && evt.code === 'Space') || (
    
          String.fromCharCode(evt.which) === '@' &&
          target.value.charAt(selectionStart - 1) === '@'
        );
    
      if (isNotValid) {
        evt.preventDefault();
    
        return;
      }
      const key = evt.keyCode || evt.charCode || evt.which;
    
      return (key !== 32) && key;
    }
    
    function handleInputFieldKeypress(evt) {
      const { target } = evt;
    
      let result;
    
      if (target.matches('input[name="real_name"], input[name="display_name"]')) {
    
        result = handleNameFieldKeypress(target, evt);
    
      } else if (target.matches('input[name="email"]')) {
    
        result = handleEmailFieldKeypress(target, evt);
      }
      return result;
    }
    document
      .querySelector('form')
      .addEventListener('keypress', handleInputFieldKeypress);
    
    </script>
    //-->