I wouldn't go for a single listener, unless that listener itself is designed to be extensible, because it would violate the Open-Closed Principle, would lead to uncohesive features being packed together and would potentially break encapsulation by suggesting to leak internal details of sub-components into the "parent".
If we take very simple example where we have a feature A which is composed of features B & C.
With a single non-extensible listener approach we may be forced toward such a design:
function initFeatureA(domEl) {
initFeatureB(domEl);
initFeatureC(domEl);
domEl.addEventListener('someevent''change', function (e) {
if (...) doSomethingRelatedToFeatureA();
else if (...) doSomethingRelatedToFeatureB();
else if (...) doSomethingRelatedToFeatureC();
//#1. OCP violation because as we add features we need to change the handler
//#2. Not cohesive because we might have a very large spectrum of unrelated behaviors
//#3. The design invites us to leak logic of sub-features into feature A
});
}
function initFeatureB(domEl) {
//...
}
function initFeatureC(domEl) {
//...
}
Compare with the following, where every feature/component is better encapsulated:
function initFeatureA(domEl) {
initFeatureB(domEl);
initFeatureC(domEl);
domEl.querySelector('.feature-a-input').addEventListener('someevent''change', function (e) {
doSomethingRelatedToFeatureA();
});
}
function initFeatureB(domEl) {
domEl.querySelector('.feature-b-input').addEventListener('someevent''change', function (e) {
doSomethingRelatedToFeatureB();
});
}
function initFeatureC(domEl) {
domEl.querySelector('.feature-c-input').addEventListener('someevent''change', function (e) {
doSomethingRelatedToFeatureC();
});
}
Now, there may be cases where a single handler makes more sense than multiple ones, but that single handler is generally designed to be extensible in order to avoid some of the concerns mentionned above. For instance, imagine a game where individual keys may bind to actions.
You could potentially design an InputController component which is responsible for abstracting away the low-level details of capturing the keys that were pressed. Then, other components may register themselves through the InputController as they need to perform actions.