I'm having some unexpected behavior with DOM manipulation in a directive. I have two icons and would like icons, a phone and email and would like to show their contents when hovered over. Which should be simple enough but I'm having trouble traversing the DOM to reach the child elements.
Here's my Directive:
app.directive('contact', function () {
return {
restrict: 'E',
scope: {
email:'@',
phone: '@',
extension: '@'
},
link: function (scope, element, attrs) {
var el = element;
var kids = $(el).children();
var emailChild = $(kids[0]);
var email = emailChild.context.children[0];
element.children().bind('mouseenter', function() {
console.log(this);
console.log(emailChild.context.children[0]);
console.log(email);
});
},
template: '<div class="contact_methods">' +
'<div ng-if="email" class="email" href="#">' +
'<div class="contact_info_email more_info" style="display:none;"><a ng-href="mailto:{{email}}">{{email}}</a></div>' +
'</div> ' +
'<div ng-if="phone" class="phone" href="#"> ' +
'<div class="contact_info_phone more_info">{{phone}} ext.{{extension}}</div>' +
'</div>' +
'</div>'
}
});
Here is the unexpected behavior:
console.log(this);
console.log(emailChild.context.children[0]);
console.log(email);
These evaluate to:
<div class="contact_methods">…</div>
<div ng-if="email" class="email ng-scope" href="#">…</div>
undefined
email outputs undefined; however, it's contents are the line above it? Also I am unable to drill down into it like element.children().children()? The hover stops working.
You do not need to do DOM access necessarily here instead you could just use ng-events. In your case ng-mouseenter ng-mouseleave combined with ng-show.
<div ng-if="email" class="email" href="#"
ng-mouseenter="toggleEmail(true)" ng-mouseleave="toggleEmail(false)">Email
<div class="contact_info_email more_info" ng-show="displayEmail">
<a ng-href="mailto:{{email}}">{{email}}</a></div>
and in the linking function:
link: function (scope, element, attrs) {
scope.toggleEmail = function(shouldShow){
scope.displayEmail = shouldShow;
}
},
A Sample Demo
angular.module('app', []).controller('ctrl', function($scope) {
$scope.email = "[email protected]";
$scope.phone = "111-222-3333";
}).directive('contact', function() {
return {
restrict: 'E',
scope: {
email: '@',
phone: '@',
extension: '@'
},
link: function(scope, element, attrs) {
scope.toggleEmail = function(shouldShow) {
scope.displayEmail = shouldShow;
}
},
template: '<div class="contact_methods"> \
<div ng-if="email" class="email" href="#" ng-mouseenter="toggleEmail(true)" ng-mouseleave="toggleEmail(false)">Email \
<div class="contact_info_email more_info" ng-show="displayEmail"><a ng-href="mailto:{{email}}">{{email}}</a></div> \
</div> \
<div ng-if="phone" class="phone" href="#">Phone \
<div class="contact_info_phone more_info">{{phone}} ext.{{extension}}</div> \
</div> \
</div>'
}
});;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<contact email="{{email}}" phone="{{phone}}"></contact>
</div>
I would even use a controller for this directive and set up the scope methods and variables there. Also you could place your template externally instead of inline template since it is larger and difficult to maintain in a javascript file.
The issue with your approach is that you have the selector wrong here. because actual root will be the element <contact so the children is not what you expect. Also remember you do not need to wrap element with $(element) again since it is already a jquery wrapper element (provided you have jquery included before angular). So you could do this way as well (even though i would rethink doing it this way):
//Let the directive render DOM first
$timeout(function() {
element.find('.email, .phone').bind('mouseenter mouseleave', function() {
$(this).children().toggle()
});
});
angular.module('app', []).controller('ctrl', function($scope) {
$scope.email = "[email protected]";
$scope.phone = "111-222-3333";
}).directive('contact', function($timeout) {
return {
restrict: 'E',
scope: {
email: '@',
phone: '@',
extension: '@'
},
link: function(scope, element, attrs) {
$timeout(function() {
element.find('.email, .phone').bind('mouseenter mouseleave', function() {
$(this).children().toggle()
});
});
},
template: '<div class="contact_methods">' +
'<div ng-if="email" class="email" href="#"> Email' +
'<div class="contact_info_email more_info" style="display:none;"><a ng-href="mailto:{{email}}">{{email}}</a></div>' +
'</div> ' +
'<div ng-if="phone" class="phone" href="#"> Phone' +
'<div class="contact_info_phone more_info" style="display:none;">{{phone}} ext.{{extension}}</div>' +
'</div>' +
'</div>'
}
});;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<contact email="{{email}}" phone="{{phone}}"></contact>
</div>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With