I'm using an angular accordion directive to hide/show content.
The problem i am facing is that the height of the content's container is not changing.
Here's a plunker: http://plnkr.co/edit/oWXrqoJpaYPDbe4TwT3c?p=preview
If you click on Show More, you can see how the contents is hidden rather than the height of show-jobs changing.
JS:
app.directive('sliderContentDirective', function () {
return {
restrict:'A',
compile: function (element, attr) {
// wrap tag
var contents = element.html();
element.html('<div class="slideable_content" style="margin:0 !important; padding:0 !important" >' + contents + '</div>');
return function postLink(scope, element, attrs) {
// default properties
attrs.duration = (!attrs.duration) ? '0.7s' : attrs.duration;
attrs.easing = (!attrs.easing) ? 'ease-in-out' : attrs.easing;
element.css({
'overflow': 'hidden',
'height': '0px',
'transitionProperty': 'height',
'transitionDuration': attrs.duration,
'transitionTimingFunction': attrs.easing
});
};
}
};
});
app.directive('sliderToggleDirective', function($document, $timeout) {
return {
restrict: 'AE',
scope: {
target: "@"
},
link: function(scope, element, attrs) {
var timeoutFunc = function () {
var target = angular.element($document[0].querySelector("#" + scope.target))[0];
attrs.expanded = false;
element.bind('click', function() {
var content = target.querySelector('.slideable_content');
if(!attrs.expanded) {
content.style.border = '1px solid rgba(0,0,0,0)';
var y = content.clientHeight;
content.style.border = 0;
target.style.height = y + 'px';
}
else {
target.style.height = '0px';
}
attrs.expanded = !attrs.expanded;
});
}
$timeout(timeoutFunc, 0);
}
}
});
If i inspect the show-jobs element, i can see it has an initial height of 312px. If i remove this, then it works as expected.
As Giliar already said, the problem is that you have a fixed height which does not allow your section
element to automatically get its height to fit all of its content (including the newly expanded accordion). The solution I can propose to this (which is also pretty much what Bootstrap does as far as I can tell)
section
's height to auto
when the animation is finished, so that if nested divs are expanded the section
will expand correctly.section
when the user attempts to close it, before setting its height back to 0, so that the closing animation will work correctly.To do the first part you can just define a function which adjusts the section
's height to auto
and call it after the expand animation is finished.
var adjustHeightFunc = function() {
var target = angular.element($document[0].querySelector("#" + scope.target))[0];
if (attrs.expanded) {
var content = target.querySelector('.slideable_content');
target.style.height = 'auto';
}
}
Since the expand animation takes 0.7 seconds you can just call the adjustHeightFunc function with a timeout of 0.8 seconds (I get this is not really optimal, since if you change the duration of your animation you will also need to change this timeout, but it's the best solution I have found so far, any other suggestions are welcome). So in the end of your onClick function you can have:
$timeout(adjustHeightFunc, 800);
To do the second part is to not set the section
's height to 0 when the section should be collapsed, but to always set it to the height of its contents. After you do this, and if the section should be collapsed, you call a separate function using $timeout with a value of 0 (so that it will be executed on a separate digest cycle) which sets the section's height to 0 thus triggering the collapse animation. Your onClick function therefore becomes something like this:
element.bind('click', function() {
var content = target.querySelector('.slideable_content');
var y = content.clientHeight;
target.style.height = y + 'px';
if(!attrs.expanded) {
content.style.border = '1px solid rgba(0,0,0,0)';
content.style.border = 0;
}
else {
$timeout(closeAccordionFunc, 0);
}
attrs.expanded = !attrs.expanded;
$timeout(adjustHeightFunc, 800);
});
See the updated Plunker.
EDIT: As it turns out from the comments setting the closeAccordionFunc
to be executed with timeout 0 does not work well with all the browsers. A workaround for this is to declare a CSS class which will set the element's height to auto
(using !important
to override the height set directly on the element) and use Angular's $animate
service to add / remove this class to the element and execute the closeAccordionFunc
after the class has been removed. The updated onClick
function therefore is:
element.bind('click', function() {
var content = target.querySelector('.slideable_content');
var y = content.clientHeight;
target.style.height = y + 'px';
if(!attrs.expanded) {
content.style.border = '1px solid rgba(0,0,0,0)';
content.style.border = 0;
}
else {
$animate.removeClass(angular.element(target), 'auto', function(){$timeout(closeAccordionFunc);});
}
attrs.expanded = !attrs.expanded;
if (attrs.expanded) {
$timeout(adjustHeightFunc, 800);
}
});
See it also in Plunker.