Customize JQueryUI Accordion Click

So I put together a page with several sections and sub-items.  The sections are report groupings and the sub items are reports themselves.  Users can click on the report name to run the report (which they will do 96.5% of the time) or they can expand out the details for the report and view a description, or perform actions like staring the report as a favorite.  So I decided to use the JqueryUI accordion control.  And to allow the user to navigate by clicking on the report name, I set the accordion event to be the mouseover (that way clicking on the report name would take them to the run report page).  Turns out this was a bad idea – the page was jumpier than a cat in a room full of rockers…  Just moving your mouse around would cause the page to resize and redraw.

After the usability review, it was decided to leave the accordions in place and to change the expand event to a click.  But in order to allow the user to navigate, the click had to be confined to the accordion’s icon…

Accordion_1

Turns out that wasn’t so easy…  And none of the answers on stackoverflow seemed like what I was looking for.  So I started digging into the accordion source and came up with this solution:

1. $(document).ready(function () { 2. $(".categoryBox #accordion").accordion({ 3. active: false, 4. collapsible: true, 5. event: "" 6. });

7.

8. $(".ui-accordion-header .ui-icon").bind("click.accordion", function (evt) { 9. var header = $(this.parentNode); 10. var current = $(this.parentNode.parentNode).data("accordion"); 11. evt.currentTarget = null; 12. current._clickHandler.call(current, evt, header); 13. evt.preventDefault();

14. });

15. });

So the accordion is a jquery widget control and is built as such (see http://wiki.jqueryui.com/w/page/12138135/Widget-factory).  The method that’s called when the event (whichever the user specified) occurs is “_clickHandler”.  But widget functions that begin with “_” are hidden functions – hard to get at.  The handy “.data” function comes to the rescue – it will return the original object!

Nothing is out of the ordinary in the control constructor except for the event option (line 5), which is suspiciously blank.  This is noticed in the accordion’s “_create” function, and prevents events from being attached to the header.  Then I find all the accordion icons that were added during the create, and I bind a click event to them (line 8).    When the click is fired, I get the header control (line 9) and the current accordion object (line 10).  Because I used the “data” method to get the object, I can now call those tasty hidden functions it contains = in my case it’s “_clickHandler” (line 12).  Setting the event’s currentTarget to null (line 11) is very important though.  The “_clickHandler” function in the accordion source uses javascript’s coalesce operator thusly:

var clicked = $( event.currentTarget || target )

(where “target” is a parameter on the _clickHandler method).  So to get the click to show the proper control, I set the currentTarget to null and pass in the header control as the target.  The header control is then set as the “clicked” object, and everything works as planned!

Advertisements

5 comments on “Customize JQueryUI Accordion Click

  1. Frank says:

    I tried this but get error current._clickHandler is undefined.

    • Jack says:

      You might need to modify your selector — I was able to use $(this.parentNode.parentNode), but you might have to poke around in the debugger to see which element contains the accordion data

      • frankhinchey says:

        it’s definitely selecting the accordion (v 1.9.1) as i can inspect it in firebug. the closes hidden function i can see is _eventHandler. but replacing that for _clickHandler doesn’t work.

    • Jack says:

      I sure am glad you posted about this – I was still using jquery-ui 1.6rc6, and they’ve changed the accordion control sometime between then and the latest version (I tested with v1.9.2). I guess that’s the problem with these hacks. I need to go and update all my references now…

      I’ve figured out how to fire the event correctly though – here’s the JSFiddle link to my modified code:
      http://jsfiddle.net/2u5cq/

      And here’s the code:

      $(“.ui-accordion-header .ui-icon”).bind(“click.accordion”, function(evt) {
      var current = $(this.parentNode.parentNode).data(“accordion”);
      console.log(current);
      evt.currentTarget = this.parentNode;
      current._eventHandler( evt )
      });

      I’ll post an update with the latest jquery-ui support. Let me know how it works out.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s