Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular Accordion not expanding the height

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.

like image 337
Oam Psy Avatar asked May 15 '15 12:05

Oam Psy


3 Answers

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)

  1. Set your section's height to auto when the animation is finished, so that if nested divs are expanded the section will expand correctly.
  2. Re-set a fixed height to the 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.

like image 100
Christina Avatar answered Nov 18 '22 15:11

Christina


If your intention is so that slider-content-directive slides up and down, hiding/showing the content as you click the button, it seems to be working... fine?

Ok, I see what's going on. Alright, so the problem seems to be that has a fixed height of 300px. You could override it to height: auto;, but that will break the container's sliding animation - because transitions need hard values for the beginning and the end of the transitions. Now, there are at least two ways to solve this:

  • You could re-work it to make it work with max-height. See Animating max-height with CSS transitions
  • You could make it so as the container's sliding animation when expanding is finished, set a class on the that overrides the height, and when clicking the "Show My Jobs >>" button, it calculates the new height of the container and assigns it to the container inline. This problem is a bit tricky to solve, by the way. :-)
like image 6
Giliar Perez Avatar answered Nov 18 '22 16:11

Giliar Perez


I think coding is meaningful only if you can write less and do more(ok, it's jQuery's motto, but it's a good one). Following that ideea I have a css only accordion that I really think that it may be useful. I have already posted it here. It is based on the material here. The main ideea is the utility of radio buttons(it can also be made with checkboxes). I think that making an AngularJS code for this is very heavy. The code is:

                    .accordion-example .accordion input[type="radio"] {
                        position: absolute;
                        left: -9999rem;
                    }
                    .accordion-example .accordion label {
                        display: block;
                        background: #4f6f8b;
                        color: #fff;
                        text-align: center;
                        padding: 1rem;
                        font-size: .8em;
                        letter-spacing: .13em;
                        padding-left: .13em;
                        padding-right: 0;
                        text-transform: uppercase;
                        cursor: pointer;
                        height: 3.3rem;
                        font-weight: 300;
                        transition: background 400ms, color 400ms, border-radius 400ms;
                    }
                    .accordion-example .accordion label:hover {
                        background: #385670
                    }
                    .accordion-example .accordion .baffle {
                        position: relative;
                        height: 3.3rem;
                        overflow: hidden;
                        transition: height 400ms;
                        -webkit-transform: translateZ(0);
                    }
                    .accordion-example .accordion .baffle-inner {
                        padding: 1.25rem;
                        background: #eee;
                        position: absolute;
                        top: 3.3rem;
                        left: 0;
                        height: 13.5rem;
                        -webkit-overflow-scrolling: touch;
                        overflow: scroll;
                        overflow-y: scroll;
                        overflow-x: hidden
                    }
                    .accordion-example .accordion .baffle-inner :first-child {
                        margin-top: 0
                    }
                    .accordion-example .accordion .baffle-inner :last-child {
                        margin-bottom: 0
                    }
                    .accordion-example .accordion input[type="radio"]:checked + .baffle {
                        height: 16.8rem
                    }
                    .accordion-example .accordion input[type="radio"]:checked + .baffle label {
                        background: #eee;
                        color: inherit;
                        box-shadow: inset 0 -1px rgba(0, 0, 0, .15);
                        font-weight: 300
                    }
                    .accordion-example .accordion input[type="radio"]:checked + .baffle:not(:first-of-type) label {
                        border-radius: 0
                    }
                    .accordion-example .accordion input[type="radio"]:not(:checked) + .baffle + input[type="radio"]:not(:checked) + .baffle label {
                        box-shadow: inset 0 1px rgba(0, 0, 0, .15);
                    }
                    .accordion-example ::-webkit-scrollbar {
                        width: .9375rem
                    }
                    .accordion-example ::-webkit-scrollbar-thumb {
                        background: #849cb1;
                        border: solid #eee;
                        border-width: .375rem .375rem .375rem 0;
                        border-radius: 0 .375rem .375rem 0
                    }
                    .accordion-example ::-webkit-scrollbar-thumb:window-inactive {
                        background: #aaa
                    }
<div class="accordion-example">
                    <div class="accordion">
                        <input type="radio" id="radio-option-1" name="accordion-radios" checked>
                        <div class="baffle">
                            <label for="radio-option-1">Accordion</label>
                            <div class="baffle-inner">
                                Accordions (from 19th century German Akkordion, from Akkord - “musical chord, concord of sounds”) are a family of box-shaped musical instruments of the bellows-driven free-reed aerophone type, colloquially referred to as a squeezebox. A person who plays the accordion is called an accordionist. The concertina and bandoneón are related; the harmonium and American reed organ are in the same family.
                            </div>
                        </div>
                        <input type="radio" id="radio-option-2" name="accordion-radios">
                        <div class="baffle">
                            <label for="radio-option-2">Lorem</label>
                            <div class="baffle-inner">
                                <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tempus bibendum arcu non sodales. Aenean consequat viverra velit sed elementum. Sed id neque eu quam ultricies blandit ut a lacus. Maecenas et feugiat turpis. Suspendisse nec sem massa. Nullam aliquam sem nibh, non pretium ante posuere vitae. Pellentesque rutrum lacinia enim, eget ornare felis egestas vel. Mauris ullamcorper, ex sit amet pellentesque facilisis, tellus nulla sodales mauris, eu euismod ligula leo in diam. Morbi at leo sit amet est suscipit imperdiet at ac nibh. Fusce ex nisi, rutrum sed sollicitudin eget, rutrum id sapien. </p>
                            </div>
                        </div>
                        <input type="radio" id="radio-option-3" name="accordion-radios">
                        <div class="baffle">
                            <label for="radio-option-3">ipsum</label>
                            <div class="baffle-inner">
                                Suspendisse sit amet lorem tempor, feugiat mauris quis, vehicula ante. Maecenas quis mauris quis tortor hendrerit bibendum et sed orci. Donec semper vel risus in scelerisque. Duis consectetur molestie dolor, eu aliquet ipsum maximus et. Ut massa sem, facilisis convallis nisi vel, posuere ullamcorper dolor. Donec accumsan ligula ornare ante convallis ornare. Donec lectus nibh, pharetra eget accumsan ac, vehicula quis elit. Nulla at mi ac metus ornare vehicula. Suspendisse ac metus maximus, semper est sed, gravida nisl. Nunc finibus lectus non magna congue malesuada. Cras auctor volutpat convallis. Pellentesque.
                            </div>
                        </div>
                    </div>
                </div>

I hope it helps.

EDIT: Because of the untold request of Dave Alperovich to have an appropriate code on the issue of height and dynamic content, I post the following code.

.accordion input[type="checkbox"], .accordion input[type="radio"] {
    position:absolute;
    left:-9999em
}

.accordion .baffle {
    position:relative;
    overflow:hidden;
    height:100%
}

.accordion label {
    display:block;
    text-align:center;
    padding:1em;
    cursor:pointer;
    height:100%;
    background:#00bfa5;
    color:#000;
    font-weight:300;
    letter-spacing:.13em;
    text-transform:uppercase
}

.accordion label:hover {
    background:#1de9b6
}

.accordion .baffle-inner {
    padding:0 1.25em;
    height:0;
    opacity:0;
    -webkit-transition:all 500ms ease-in-out;
    -o-transition:all 500ms ease-in-out;
    transition:all 500ms ease-in-out
}

.accordion > .baffle > input[type="checkbox"]:checked ~ .baffle-inner, .accordion > .baffle > input[type="radio"]:checked ~ .baffle-inner{
    padding:1.25em;
    height:100%;
    margin-bottom: 20px;
    opacity:1
}

.accordion > .baffle > input[type="checkbox"]:checked ~ label, .accordion > .baffle > input[type="radio"]:checked ~ label {
    background:#eee;
    box-shadow:inset 0 -1px rgba(0,0,0,.4);
    font-weight:300
}
.accordion > .baffle > input[type="checkbox"]:checked ~ label:hover, .accordion > .baffle > input[type="radio"]:checked ~ label:hover {
    background:#1de9b6
}
<div class="accordion">
    <div class="baffle">
        <input id="checkbox-option-1" type="checkbox" checked="checked" name="accordion-checkboxs">
        <label for="checkbox-option-1">Accordion</label>
        <div class="baffle-inner">
            Accordions (from 19th century German Akkordion, from Akkord - “musical chord, concord of sounds”) are a family of box-shaped musical instruments of the bellows-driven free-reed aerophone type, colloquially referred to as a squeezebox. A person who plays the accordion is called an accordionist. The concertina and bandoneón are related; the harmonium and American reed organ are in the same family. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis dignissim felis in lacinia vulputate. Vestibulum suscipit, lacus id tempus sagittis, ex orci molestie turpis, nec pharetra ligula arcu ac nisi. Donec in nisi arcu. Aenean non nisl purus. Fusce pretium lorem eget dui sollicitudin, eu gravida felis egestas. Vivamus orci tellus, efficitur at interdum vitae, ultricies quis urna. Sed ipsum eros, fermentum ornare ultrices vel, pharetra vitae metus. Sed ex metus, consectetur at tellus eget, porttitor molestie dolor. Duis nec metus non est tincidunt consectetur. Curabitur vel turpis sit amet leo fermentum cursus. Cras ligula erat, lobortis ut purus et, accumsan vestibulum lectus. Fusce sed nisl nisl. Aenean eget dolor lacinia, interdum ante id, fringilla orci. Cras eu porttitor est.

Fusce dignissim lectus quis ligula aliquet pretium. Vestibulum consequat risus orci, ac consectetur dolor finibus quis. Aliquam erat volutpat. Proin nunc quam, suscipit eget elementum eu, rhoncus in tortor. Aenean vitae lacinia lorem, maximus elementum urna. Vivamus vitae posuere libero, a fermentum lorem. Duis placerat laoreet ipsum, et malesuada lacus aliquam vitae.

Proin ut mauris ipsum. Suspendisse potenti. Mauris diam erat, ornare vitae tellus eget, elementum fringilla nunc. Maecenas eleifend enim non arcu blandit ullamcorper. Vivamus blandit dictum nulla, accumsan finibus leo blandit in. Curabitur nec tellus a nulla cursus semper. Sed fermentum velit quis pulvinar pellentesque. 
        </div>
    </div>
    <div class="baffle">
        <input id="checkbox-option-2" type="checkbox" name="accordion-checkboxs">
        <label for="checkbox-option-2">Construction</label>
        <div class="baffle-inner">
            Accordions have many configurations and types. What may be technically possible to do with one accordion could be impossible with another:
            <br>The accordion is a free reed instrument and is in the same family as other instruments such as the sheng and khaen. The sheng and khaen are both much older than the accordion and this type of reed did inspire the kind of free reeds in use in the accordion as we know it today.The accordion’s basic form is believed to have been invented in Berlin in 1822 by Christian Friedrich Ludwig Buschmann, although one instrument has been recently discovered that appears to have been built earlier.
        </div>
    </div>
    <div class="baffle">
        <input id="checkbox-option-3" type="checkbox" name="accordion-checkboxs">
        <label for="checkbox-option-3">History</label>
        <div class="baffle-inner">
            <p>The accordion is a free reed instrument and is in the same family as other instruments such as the sheng and khaen. The sheng and khaen are both much older than the accordion and this type of reed did inspire the kind of free reeds in use in the accordion as we know it today.</p>
            <p>The accordion’s basic form is believed to have been invented in Berlin in 1822 by Christian Friedrich Ludwig Buschmann, although one instrument has been recently discovered that appears to have been built earlier.</p>
        </div>
    </div>
</div>

It is an edit of the first one. I use checkboxes instead of radio buttons, and use a non-fixed height. This is the proof that it can be done.

If you find it useful, +1.

like image 1
vanntile Avatar answered Nov 18 '22 16:11

vanntile