Inputs equality check using jQuery without plugins I tried creating a function to check for a match between two email fields, let's say EMAIL and CONFIRM email. I absolutely try to avoid jQuery validate. It is not worth it for a single field. For the rest I use HTML5 properties like required, pattern, etc. and it works well. This is why I want to deliver seamless experience with setCustomValidity.

Here's my working code, which of course, may be improved by someone with more experience in JS and jQuery.

HTML

&lt;input type="email" name="addr1" id="addr1" required&gt;
&lt;input type="email" name="addr2" data-equal-to="addr1" data-msg-error="Emails do not match." required&gt;


jQuery

$('[data-equal-to]').on('blur', function() {
    var to_confirm = $(this),
        to_equal = $('#' + to_confirm.data('equal-to'));
    if (to_confirm.val() != to_equal.val()) {
        this.setCustomValidity(to_confirm.data('msg-error'));
    }
    else {
        this.setCustomValidity('');
    }
    to_equal.one('blur', function() {
        to_confirm.trigger('blur');
    });
});


Here are the problems I encountered:


I was using '.bind'. It turns out its deprecated. Then I started using
'.on'
I forgot the last 3 lines. There was no event listener for the
first email input, so I was only checking the CONFIRM input. This
got fixed.
I was using 'input' instead of 'blur'. 'input' provided
real-time validation (as you type) which is good, but consumed too
much in my opinion. Also it showed errors while you are still typing
(and haven't finished), so some users may find it confusing. The only
drawback of 'blur' I find is that feedback on validity comes after
you click another element (unfocus).
Binding on every input a new listener to 'to_equal' turned out to be a bad idea. I ended up with multiple listeners, causing the browser to crash. I found out that '.one' exists, which should do the same as .off before .on (or maybe after?)


My question is - can this little script be improved in terms of speed, or using less event listeners? Is there a case I haven't thought of? I searched a lot to find a good no plugin solution, but with no luck. Also I insist on using data- attributes to provide the matching id + the error message (as the website is multi-lingual and its easier to modify HTML than JS on the fly).

I think that having the 'perfect' version of this script will help a lot of people, especially in confirm email/password (for passwords case sensitivity must be in mind!) aspect.
Switch to the native DOM methods like getElementById, which is orders of magnitude faster and you can keep input:

$('[data-equal-to]').on('input', function() {
  var target = document.getElementById(this.dataset.equalTo);
  var isEqual = !target || target.value == this.value;  
  this.setCustomValidity(isEqual ? '' : this.dataset.msgError);
});


You can also debounce the verification by 200ms, for example:

;(function() {
  var timeout;

  function validateEquality(element) {
    var target = document.getElementById(element.dataset.equalTo);
    var isEqual = !target || target.value == element.value;  
    element.setCustomValidity(isEqual ? '' : element.dataset.msgError);
  }

  $('[data-equal-to]').on({
    input: function() {
      clearTimeout(timeout);
      timeout = setTimeout(validateEquality, 200, this);
    }),
    blur: function() {
      clearTimeout(timeout);
      validateEquality(this);
    }),
  });
})();


You can also make this a jQuery plugin.



As for mirroring the check for the target element, the preferable approach is to explicitly declare data-equal-to attribute on both input elements that points to the other one. But you can also do it implicitly by adding equalOf expando property pointing to the linked element so you can re-triggering input event on it:

$('[data-equal-to]')
  .on('input', function() {
    var target = document.getElementById(this.dataset.equalTo);
    var isEqual = !target || target.value == this.value;  
    this.setCustomValidity(isEqual ? '' : this.dataset.msgError);
  })
  .map(function() {
    var target = document.getElementById(this.dataset.equalTo);
    target.equalOf = this;
    return target;
  })
  .on('input', function() {
    $(target.equalOf).trigger('input');
  });