Tuesday, July 2, 2013

jQuery UI: Disable Accordion Section

The disabled option of the accordion widget let's you disable the entire widget when set to true. But I don't want to disable the whole thing, only specific sections. Turns out, it isn't all that hard to make the disabled option understand this concept by extending the widget with some new behavior. The only requirement of the change is that we're able to pass a section index to the disabled option to disable that section only. So passing 0 would disable the first section, and so on. The trick to accomplishing this is that the default accordion implementation will ignore non-boolean values of disabled. This is where we can come in, and check for numbers.

This is what the end result looks like.


And here is the modified accordion code.

(function( $, undefined ) {

// Modify the accordion, making the disabled option
// accept section indexes.
$.widget( "ab.accordion", $.ui.accordion, {

    _create: function() {

        this._super();

        // Kick-start setting the disabled option,
        // using our custom code below.  jQuery widgets
        // don't call _setOption() when first created.
        this._setOption( 
            "disabled",
            this.options.disabled
        );

    },

    // Sets the disabled object, but only if the option key
    // is "disabled" and the option value is a number.
    _setOption: function( key, value ) {

        var isDisabled = ( key === "disabled" ),
            isNumber   = ( typeof value === "number" ),
            $panel;

        // Check if we're disabling a specific accordion
        // section by index.
        if ( isDisabled && isNumber ) {

            // Get the accordion header and panel, and 
            // disable them.  The base jQuery UI widget
            // knows not to handle events on elements that
            // have the ui-state-disabled class applied.
            // Adding the class to the panel header and 
            // content elements is enough to completely
            // disable the section.
            $panel = this._findActive( value );
            $panel.addClass( "ui-state-disabled" )
                  .next()
                  .filter( ".ui-accordion-content-active" )
                  .addClass( "ui-state-disabled" );

        }
        else {

            this._super( key, value );

        }

    },

});

})( jQuery );

$(function() {

    // Disable the first section, activate the second.
    $( "#accordion" ).accordion({
        disabled: 0,
        active: 1
    });

});

5 comments :

  1. What if you want to disable more than one section?

    ReplyDelete
    Replies
    1. Good question. For each section you want disabled, calling .accordion( "option", "disabled", section_index ) seems to work well enough.

      Delete
  2. How can you enable the section again?

    ReplyDelete
    Replies
    1. Good question. I would just destroy the widget and re-create it with the desired configuration.

      Delete
    2. Ok, but that solution does't works for my. So I modified your code a little bit:

      _setOption: function(key, value) {

      var isDisabled = (key === "disabled"),
      isNumber = (typeof value === "number"),
      $panel;
      var isEnabled = (key === "enabled");
      // Check if we're disabling a specific accordion
      // section by index.
      if (isDisabled && isNumber) {

      // Get the accordion header and panel, and
      // disable them. The base jQuery UI widget
      // knows not to handle events on elements that
      // have the ui-state-disabled class applied.
      // Adding the class to the panel header and
      // content elements is enough to completely
      // disable the section.
      $panel = this._findActive(value);

      $panel.addClass("ui-state-disabled")
      .next()
      .filter(".ui-accordion-content-active")
      .addClass("ui-state-disabled");
      }
      else if (isEnabled && isNumber) {
      $panel = this._findActive(value);

      $panel.removeClass("ui-state-disabled")
      .next()
      .filter(".ui-accordion-content")
      .removeClass("ui-state-disabled")
      .addClass("ui-accordion-content-active");
      }
      else {

      this._super(key, value);
      }
      }


      Thanks

      Delete