Wednesday, December 19, 2007

Accordion select list

Kevin Miller has created an accordion widget using prototype and script.aculo.us. In this article I'll demonstrate how to build a selection list that has sub-sections utilizing the accordion control. Here is a demonstation comparing a basic select list control and the accordion select list: To make the control work, you first add a read-only text box with the drop down button image floating to the right of it, for example:

<span style="float: right">
 <img id="accSelectToggle" src="images/btn_dropdown.png" width="21" height="16" alt="Expand list" title="Expand list" />
</span>
<input type="text" id="accSelect" title="List" readonly="readonly" value="Option 1.1" />
You then define a div which contains the accordion control, such as:
<div id="accSelectOptions" class="accordion_container" style="display: none">
<h1 class="accordion_toggle">Category 1</h1>
<div class="accordion_content">
<ul>
 <li value="option11">Option 1.1</li>
 <li value="option12">Option 1.2</li>
 <li value="option13">Option 1.3</li>
</ul>
</div>
...
</div>
Add your preferred CSS styling. In my example the CSS looks like:
#accSelect {
 width: 169px;
 height: 14px;
 border-style: none;
 padding-left: 4px;
}

.accordion_container {
 z-index: 10;
 position: absolute;
 width: 198px;
 border: solid 1px black;
}

.accordion_toggle {
 display: block;
 padding: 0.3em 0.5em;
 font-weight: normal;
 font-size: 1em;
 text-decoration: none;
 outline: none;
 border-bottom: 1px solid #def;
 color: #000000;
 cursor: pointer;
 margin: 0px;
 background-image: url('images/expand.gif');
 background-position: 99% 50%;
 background-repeat: no-repeat;
 padding-right: 5px;
 background-color: #9BE;
}

.accordion_toggle_active {
 color: #ffffff;
 font-weight: bolder;
 background-image: url('images/ns-expand.gif');
 background-color: #47B;
}

.accordion_content {
 background-color: #ffffff;
 overflow: auto;
 display: block;
}

.accordion_content ul {
 padding-left: 1em;
 margin: 0;
 padding-right: 0;
 padding-bottom: 0px;
 list-style-type: none
}

.accordion_content li {
 margin: auto;
 padding: 0px;
}

.highlight {
 background-color: #DDD;
}
And finally add a little JavaScript to make the magic happen:
    function  toggleAccordionSelect(event) {
        var image = event.target;
        var collapseIcon = "images/btn_dropdown-selected.png";
        var expandIcon = "images/btn_dropdown.png";

        if ($('accSelectOptions').style.display === 'none') {
            image.src = collapseIcon;
            image.alt = "Collapse List";
            image.title = "Collapse List";
            $('accSelectOptions').show();
        }
        else {
            image.src = expandIcon;
            image.alt = "Expand List";
            image.title = "Expand List";
            $('accSelectOptions').hide();
        }
    }

    function toggleHighlight(event) {
        var item = event.target;
        item.toggleClassName('highlight');
    }
    
    function selectAccordionOption(event) {
        var selectedOption = Event.element(event);

        // update the selection input field value to the selected display value
        $('accSelect').value = selectedOption.innerHTML;

        // internal value is stored as an attribute, do whatever you need to with it
        var realValue = selectedOption.getAttribute('value');
        
        // hide the accordion list
        toggleAccordionSelect({target: $('accSelectToggle')});       
    }
    

    var accordionSelect = new accordion('accSelectOptions', {
        classNames : {
            toggle : 'accordion_toggle',
            toggleActive : 'accordion_toggle_active',
            content : 'accordion_content'
            },
        onEvent : 'mousedown'
        });

    // By default, open first accordion in the drop down
    var accordionToggles = $$('#accSelectOptions .accordion_toggle');
    accordionSelect.activate(accordionToggles[0]);

    // register event observers        
    Event.observe('accSelectToggle', 'mousedown', toggleAccordionSelect);
    $$('#accSelectOptions .accordion_content li').each(function(accSelectOption) {
        Event.observe(accSelectOption, 'mousedown', selectAccordionOption);
        Event.observe(accSelectOption, 'mouseover', toggleHighlight);
        Event.observe(accSelectOption, 'mouseout', toggleHighlight);
    });

No comments: