AngularJS Directive to add class on click, but remove and add it to another element if clicked
Solution 1
You can avoid DOM manipulation and allow for reuse if you utilize isolate scope and ng-class
:
Directive:
.directive('swapit', function(){
return {
restrict: 'E',
replace: true,
transclude: true,
scope: {
active: '='
},
template: '<a ng-click="active = $id" ng-class="{active: $id === active}" ng-transclude></a>'
}
})
HTML:
<swapit active="active">Still got game</swapit>
<swapit active="active">TnT</swapit>
...
By using isolate scope, each directive element will have its own scope with a unique ID, which can be accessed through scope.$id
. When a directive element is clicked on, you can assign this value to an active
scope variable, which is shared between your directives.
ng-click="active = $id"
Then, you can use ng-class
with an expression which determines whether each directive element's scope ID matches that of the currently active scope ID:
ng-class="{active: $id === active}"
View this demo, which more closely matches your implementation (though not 100%).
Solution 2
Here is a directive that does that:
BTW, you can specify a name for the togglable group, so you can have multiple "menus" on the same page (without one interfering with the other). E.g.:
<li ... toggle="site-menu">Page 1</li>
<li ... toggle="site-menu">Page 2</li>
...
<li ... toggle="whatever-submenu">Option 1</li>
<li ... toggle="whatever-submenu">Option 2</li>
app.directive('toggle', function () {
var TOGGLE_CLASS = 'selected';
var groups = {};
function addElement(groupName, elem) {
var list = groups[groupName] || (groups[groupName] = []);
if (list.indexOf(elem) === -1) {
list.push(elem);
}
}
function removeElement(groupName, elem) {
var list = groups[groupName] || [];
var idx = list.indexOf(elem);
if (idx !== -1) {
list.splice(idx, 1);
}
}
function setActive(groupName, elem) {
angular.forEach(groups[groupName], function (el) {
el.removeClass(TOGGLE_CLASS);
});
elem.addClass(TOGGLE_CLASS);
}
return {
restrict: 'A',
link: function postLink(scope, elem, attrs) {
var groupName = attrs.toggle || 'default';
addElement(groupName, elem);
elem.on('click', function () {
setActive(groupName, elem);
});
scope.$on('$destroy', function () {
removeElement(groupName, elem);
});
}
};
});
You can use it like this:
<a href="" toggle="test" ng-repeat="x in [1,2,3,4,5]">Link {{$index + 1}}</a>
See, also, this short demo.
![Mr. BigglesWorth](https://i.stack.imgur.com/0FmVX.jpg?s=256&g=1)
Mr. BigglesWorth
Trying to be a better javascript developer. Hungry to learn and grow my skill set.
Updated on July 09, 2022Comments
-
Mr. BigglesWorth almost 2 years
I've got a simple directive that I use to add a class on click and remove it from the element if clicked again. However I'd like to refactor it for a more common use in generic menus. Instead if a
<li>
element is clicked that is not the current active element, it should remove it from the current element and place it on the new one. Basically I want to add an "active" class to the<li>
element that is currently active.In my menu I have:
<ul> <li><a swapit ng-click="lol(stillgot)" class="select-show">Still Got Game</a></li> <li><a swapit ng-click="lol(thick)" class="select-show">TnT</a></li> <li><a swapit ng-click="lol(seldon)" class="select-show">Seldon</a></li> <li><a swapit ng-click="lol(hit)" class="select-show">HitMan</a></li> <li><a swapit ng-click="lol(community)" class="select-show">Community</a></li> </ul> .directive('swapit', function() { return { restrict : 'A', link : function(scope, elem) { var currentState = true; elem.on('click', function() { console.log('You clicked me!'); if(currentState === true) { console.log('It is on!'); angular.element(elem).addClass('active'); } else { console.log('It is off!'); angular.element(elem).removeClass('active'); } currentState = !currentState; }); } }; });