UPDATE: Customize JQueryUI Accordion Click

The widget event handling code has changed with the latest JQueryUI code…  Back in April I posted how I hacked the JQueryUI accordion control to restrict the click to the arrow icon.  To get this hack to work with JQueryUI 1.9.1+ the code changes to:


$(document).ready(function() {

$(“#accordion”).accordion({
active: false,
collapsible: true,
event: “”
});

$(“.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 )
});
});

Calling Prototype methods in Javascript

Sometimes when one is working with jquery plugins, one desires to call a juicy method in the control.  And you know the method is there because you can see it in action.  But you can’t call it…  Because it’s in the prototype…

This very thing happened to me today using a plugin called Chosen.  Chosen is an extension of the drop down list which allows the user to filter the list of items.  Pretty cool idea, and it doesn’t look bad either.  The problem occurred with switching between chosen controls with the tab key.   When tab is pressed, the value of the first control is set and the “changed” event is fired.  In the change event, an ajax call is made to populate the values in the second control (based on the value of the first control).  The problem was I could never set focus to the second control once the populate was finished.  What I finally had to do was call the close and activate prototype methods like so:

$(portfolioCtrl).data('chosen').close_field();
property.data('chosen').activate_action();

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!