Question

I am trying to append items to a nested accordion dynamically when the user clicks a button. I am using the following code to create a nested accordion:

  $(".accordion").accordion({
  collapsible: true, 
  autoHeight: false, 
  animated: 'swing',
  heightStyle: "content",
  changestart: function(event, ui) {
      child.accordion("activate", false);
  }
});

var child = $(".child-accordion").accordion({
  active:false,
  collapsible: true, 
  autoHeight: false,
  animated: 'swing'
});

In order to do this, I have found that I need to refresh the accordion using the following:

$('.accordion').accordion("refresh");

My problem is that when I try to refresh the inner accordion using:

$('.child-accordion').accordion("refresh");

I get the following: Error: cannot call methods on accordion prior to initialization; attempted to call method 'refresh'

When I inspect the div that should be refreshed it has the following ids/classes:

DIV#shelf0sections.child-accordion.ui-accordion-content.ui-helper-reset.ui-...

I tried using the selector:

$('#shelf0sections .child-accordion').accordion("refresh"); 

instead which doesn't give an error, but nothing happens visually.

jsFiddle: http://jsfiddle.net/Mw9SA/3/ (Note that the first element in the list is just an example to see the nested accordion working, If you try to add sections to it, it won't work. Use the '+Shelf' button, then open the new accordion and use the '+Section' button.)

Was it helpful?

Solution

How about a more modular approach?

Fiddle or it didnt happen: http://jsfiddle.net/Varinder/24hsd/1/

Explanation

The idea is ( the same ) to create a brand new accordion element on the fly with correct events attached and appended somewhere in the DOM.

It's generaly more managable to have repeater HTML markup abstracted away in a template somewhere in DOM and use JS to reference it rather that building it from a string.

Heres the accordion template in the markup:

<div class="template">
    <div class="accordion">
        <h3 class="accordion-title">accordion title</h3>
        <div class="accordion-content">
            accordion content
        </div>
    </div>
</div>

Heres the full HTML markup - just in case:

<div class="page">
</div>

<div id="addShelf" class="button">+ Shelf</div>
<div id="addSection" class="button">+ Section</div>

<div class="template">
    <div class="accordion">
        <h3 class="accordion-title">accordion title</h3>
        <div class="accordion-content">
            accordion content
        </div>
    </div>
</div>

JS

Starting off by storing different accordion configurations:

var shelfConfig = {
    collapsible: true, 
    autoHeight: false, 
    animated: "swing",
    heightStyle: "content"
}

var shelfSectionConfig = {
    active:false,
    collapsible: true, 
    autoHeight: false,
    animated: "swing"
}

Kepping a track of current accordion number and current accordion section number ( number of sections inside last accordion ) - might come in handy if you require a feature to remove an accordion shelf

var currentShelfNumber = 0;
var currentShelfSectionNumber = 0;

Chaching DOM elements, notice reference to the tempalte div

var $page = $(".page");
var $accordionTemplate = $(".template").children();

var $addSection = $("#addSection");
var $addShelf = $("#addShelf");

Creating a helper function that simply returns a cloned copy of the accordion template from the DOM

function getAccordionTemplate() {
     return $accordionTemplate.clone();   
}

Main function generateAccordion - it takes two arguments, accordionNumber to append current number in titles etc and accordionType to find out which accordion configuration to use.

With those parameters it will return a brand-spanking-new accordion with appropriate events attached which can then be append to the DOM

function generateAccordion( number, accordionType ) {
    var $accordion = getAccordionTemplate();
    var accordionTitle = "twerking bieber?";

    if ( accordionType == "shelf" ) {
        accordionTitle = "Shelf " + number;    
    } else {
         accordionTitle = "Shelf Section";
    }

    $accordion.find("h3").text( accordionTitle );

    var $accordionWithEvents = attachAccordionEvents( $accordion, accordionType );

    return $accordionWithEvents;
}

Notice the call to another function attachAccordionEvents as the name suggests - this fella will attach events to the accordion element.

function attachAccordionEvents( $accordionElement, accordionType ) {
    if ( accordionType == "shelf" ) {
        $accordionElement.accordion( shelfConfig );
    } else {
        $accordionElement.accordion( shelfSectionConfig );    
    }

    return $accordionElement;
}

Another helper function which makes sure "add section" button doesnt show up if there is no accordion shelf for it to work on

function manageSectionButton() {
    if ( $page.children().length > 0 ) {
        $addSection.show();
    } else {
        $addSection.hide();
    }
}

Finaly events and logic:

$addShelf.on("click", function(e) {
    e.preventDefault();

    var newShelfNumber = currentShelfNumber + 1;
    var $shelfElement = generateAccordion( newShelfNumber, "shelf" );

    currentShelfNumber = newShelfNumber;

    $page.append( $shelfElement );


    manageSectionButton();
});

$addSection.on("click", function(e) {
    e.preventDefault();

    var newShelfSectionNumber = currentShelfSectionNumber + 1;
    var $shelfSectionElement = generateAccordion( newShelfSectionNumber, "section" );
    var $activeShelfElement = $page.children().last().find(".accordion-content");

    $activeShelfElement.append( $shelfSectionElement );
});

... And thats about it.

Hope this helps,

Cheers

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top