I'm building a JavaScript plugin which will bolt onto other websites.
The whole thing is written with pure JS but there's one bit I haven't been able to get away from jQuery with:
var key = "some_key";
var selector = "#my_input, input[name=my_input], .someInputs";
jQuery(document).on('change', selector, function() {
doSomething(key, this.value);
});
The reason I want to avoid jQuery is that I expect this JS to be included in a wide range of sites, many of which won't have jQuery. Some will have other frameworks already installed such as Mootools, some will have old versions of jQuery where .on()
isn't supported, etc.
That, and I am ideally trying to keep it very lightweight, so adding in jQuery just for this tiny task seems excessive.
Here’s some futuristic JavaScript that does exactly the same thing:
var key = "some_key";
var selector = "#my_input, input[name=my_input], .someInputs";
document.addEventListener('change', function (e) {
if (e.target.matches(selector)) {
doSomething(key, e.target.value);
}
});
However, several browsers only support it with a prefix, so it’ll be closer to this:
var matches = (function () {
var names = ['matches', 'matchesSelector', 'mozMatchesSelector', 'webkitMatchesSelector', 'msMatchesSelector'];
for (var i = 0; i < names.length; i++) {
var name = names[i];
if (name in HTMLElement.prototype) {
return HTMLElement.prototype[name];
}
}
return null;
})();
var key = "some_key";
var selector = "#my_input, input[name=my_input], .someInputs";
document.addEventListener('change', function (e) {
if (matches.call(e.target, selector)) {
doSomething(key, e.target.value);
}
});
Assuming the selector isn’t dynamic and you need delegation, you can still do the verbose, manual check:
var key = "some_key";
document.addEventListener('change', function (e) {
var target = e.target;
if (target.id === 'my_input' ||
target.nodeName === 'INPUT' && target.name === 'my_input' ||
(' ' + target.className + ' ').indexOf(' someInputs ') !== -1) {
doSomething(key, target.value);
}
}, false);
As @T.J. Crowder points out, although this works for input elements, you’ll need to check an element’s parents in many cases. Here’s some even more futuristic JavaScript to accomplish the task:
function* ascend(element) {
do {
yield element;
} while ((element = element.parentNode));
}
var key = "some_key";
var selector = "#my_input, input[name=my_input], .someInputs";
document.addEventListener('change', function (e) {
var match = Array.from(ascend(e.target)).find(x => x.matches(selector));
if (match) {
doSomething(key, match.value);
}
});
If you smashed Firefox Nightly and Chrome together, this would work in that browser. We don’t have that, but feel free to shim Array.prototype.find
!