Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Extending jQuery UI Widgets for Enhanced User Interfaces

Tech May 15 1

Creating compelling user experiences involves leveraging tools that streamline development while maintaining flexibility. jQuery UI fills gaps left by inconsistent browser implementations and provides a foundation for building robust, themeable interfaces. This guide explores practical techniques for extending core jQuery UI widgets—Accordion, Autocomplete, and Button—to support advanced interactions like drag-and-drop, dynamic resizing, multi-source data handling, and animated transitions.

Enhancing the Accordion Widget

The Accordion widget organizes content into collapsible sections. While functional out of the box, it can be extended to support richer interactions.

Tab Navigation Between Sections

By default, Accordions use arrow keys for navigation. To enable Tab and Shift+Tab navigation between headers:

(function($, undefined) {
  $.widget("custom.accordion", $.ui.accordion, {
    _create: function() {
      this._super("_create");
      this._on(this.headers, { keydown: "_handleTabKey" });
    },
    _handleTabKey: function(event) {
      if (event.altKey || event.ctrlKey || event.keyCode !== $.ui.keyCode.TAB) return;
      
      const headers = this.headers,
            currentIndex = headers.index(event.target),
            isBackward = event.shiftKey,
            newIndex = isBackward ? currentIndex - 1 : currentIndex + 1;

      if (newIndex >= 0 && newIndex < headers.length) {
        $(event.target).attr("tabindex", -1);
        const newHeader = headers.eq(newIndex);
        newHeader.attr("tabindex", 0).focus();
        event.preventDefault();
      }
    }
  });
})(jQuery);

Dynamic Height Adjustment

When content changes dynamically, override the refresh() method to switch to heightStyle: "content" and clear fixed heights:

$.widget("custom.accordion", $.ui.accordion, {
  refresh: function() {
    this._super("refresh");
    if (this.options.heightStyle === "content") {
      this.headers.next().css("height", "");
    }
  }
});

Resizable Content Panels

Integrate jQuery UI's Resizable interaction to let users adjust panel heights:

$.widget("custom.accordion", $.ui.accordion, {
  options: { resizable: true },
  _create: function() {
    this._super("_create");
    if (this.options.resizable) {
      this.headers.next()
        .resizable({ handles: "s" })
        .css("overflow", "hidden");
    }
  },
  _destroy: function() {
    this._super("_destroy");
    if (this.options.resizable) {
      this.headers.next()
        .resizable("destroy")
        .css("overflow", "");
    }
  }
});

Drag-and-Drop Between Accordions

Enible transferring sections between two Accordions using Draggable and Sortable:

$.widget("custom.accordion", $.ui.accordion, {
  options: { target: null, accept: null },
  _create: function() {
    this._super("_create");
    if (this.options.target) this._makeDraggable();
    if (this.options.accept) this._makeDroppable();
  },
  _makeDraggable: function() {
    this.headers.each(function() {
      $(this).next().addBack().wrapAll("<div>");
    });
    this.element.find("> div").draggable({
      handle: "h3",
      helper: "clone",
      connectToSortable: this.options.target
    });
  },
  _makeDroppable: function() {
    this.headers.each(function() {
      $(this).next().addBack().wrapAll("<div>");
    });
    this.element.sortable({
      stop: (event, ui) => {
        if (!ui.item.hasClass("ui-draggable")) return;
        // Clone cleanup and event rebinding logic here
      }
    });
  }
});

Advanced Autocomplete Implementations

The Autocomplete widget enhances input fields with suggestion lists. Key extensions include:

Styling Input Fields

Apply jQuery UI theme classes to the input element:

$.widget("custom.autocomplete", $.ui.autocomplete, {
  _create: function() {
    this._super("_create");
    this.element.addClass("ui-widget ui-widget-content ui-corner-all");
  }
});

Multiple Data Sources

Combine arrays from different sources into a single suggestion list:

$.widget("custom.autocomplete", $.ui.autocomplete, {
  options: { sources: [] },
  _create: function() {
    if (this.options.sources.length) {
      this.options.source = (request, response) => {
        const allItems = [].concat(...this.options.sources);
        response($.ui.autocomplete.filter(allItems, request.term));
      };
    }
    this._super("_create");
  }
});

Categorized Results

Display suggestions grouped by category with custom rendering:

$.widget("custom.autocomplete", $.ui.autocomplete, {
  _renderMenu: function(ul, items) {
    let currentCategory = "";
    items.sort((a, b) => a.cat.localeCompare(b.cat));
    items.forEach(item => {
      if (item.cat !== currentCategory) {
        ul.append($("<li class='ui-autocomplete-category'>").text(item.cat));
        currentCategory = item.cat;
      }
      this._renderItem(ul, item);
    });
  },
  _renderItem: function(ul, item) {
    return $("<li>").append($("<a>")
      .append($("<div>").text(item.label))
      .append($("<small>").text(item.desc))
    ).appendTo(ul);
  }
});

Customizing Button Widgets

Buttons can be enhanced for better usability and visual consistency.

Consistent Button Widths

Make all buttons in a group match the widest button's width:

$.widget("custom.button", $.ui.button, {
  options: { matchWidth: false },
  refresh: function() {
    this._super("refresh");
    if (!this.options.matchWidth) return;
    
    const siblings = this.element.siblings(":custom-button").addBack(),
          texts = siblings.children(".ui-button-text"),
          maxWidth = Math.max(...texts.map((i, el) => $(el).width()));
    
    texts.width(maxWidth);
  }
});

Animated Hover States

Add smooth transitions to hover effects:

$.widget("custom.button", $.ui.button, {
  options: { animateHover: false },
  _create: function() {
    this._super("_create");
    if (this.options.animateHover) {
      this._off(this.element, "mouseenter mouseleave");
      this._on({
        mouseenter: () => this.element.addClass("ui-state-hover", 200),
        mouseleave: () => this.element.removeClass("ui-state-hover", 100)
      });
    }
  }
});

Toggle Icons Visibility

Prseerve icon configurations when toggling visibility:

$.widget("custom.button", $.ui.button, {
  options: { icon: true },
  _hiddenIcons: {},
  _setOption: function(key, value) {
    if (key !== "icon") return this._superApply(arguments);
    
    if (!value && !$.isEmptyObject(this.options.icons)) {
      this._hiddenIcons = this.options.icons;
      this._super("icons", {});
    } else if (value && $.isEmptyObject(this.options.icons)) {
      this._super("icons", this._hiddenIcons);
    }
  }
});

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.