The problem here is the following; we want to avoid adding event listener to each and every child, but add it only to the parent. The parent will be responsible for taking the appropriate action. The general solution to this, is to use even propagation (delegation). We attach only one listener to the parent, when an event occurs on the child (focus on input element in this example), it will bubble up to the parent and the parent will execute the listener.
Here's the directive:
app.directive('ngFocusModel', function () {
return function (scope, element) {
var focusListener = function () {
scope.hasFocus = true;
scope.$digest();
};
var blurListener = function () {
scope.hasFocus = false;
scope.$digest();
};
element[0].addEventListener('focus', focusListener, true);
element[0].addEventListener('blur', blurListener, true);
};
});
The directive listens for events and accordingly sets the value on scope, so we can make conditional changes.
There are several things to notice here.
focus
and blur
events don't "bubble", we need to use "event capturing" to catch them. That's why element.on('focus/blur')
is not used (it doesn't allow for capture, afaik) but an addEventListener
method. This method allows us to specify if the listener will be executed on "event bubbling" or "event capturing" by setting the third argument to false
or true
accordingly.
We could have used focusin
and focusout
events which "bubble", unfortunatelly these aren't supported in Firefox (focusin and focusout).
Here's a plunker with the implementation.
Update:
It occurred to me that this can be done with pure CSS using the :focus
pseudo-class, the only downside is that the placeholder needs to be in proper position (sibling) relative to the input elements. See codepen.