Mojtaba Reyhani
Mojtaba Reyhani

Reputation: 457

How can I use jQuery for predefined events?

Are there any standard rules for implementing predefined events in a jQuery plugin?

For example, the special case of Zebra Accordion plugin (a tiny accordion plugin for jQuery) or any other plugin defines some events like below:

Zebra Accordion Events:

onClose: Event fired after a tab is collapsed.

onOpen: Event fired after a tab is collapsed.

In this case if I wanted to add a div with special Font Awesome characters (such as fa-chevron-down) after the box title when collapsed (Closed, collapsed state) and remove that character and replace it with a new character (like fa-chevron-up) near the box title when expended(opened state). I would like to finally add some functionality like jQuery Accordion to it. I've tried with the below code but it appears something is wrong:

$('.Zebra_Accordion').on('onOpen', function(e) {
      $(this).append( "<span class='fa fa-chevron-down'></span>" );
  });
  
$('.Zebra_Accordion').off('onClose', function(e) {
      $(this).append( "<span class='fa fa-chevron-up'></span>" );
  });

Upvotes: 12

Views: 397

Answers (3)

K Scandrett
K Scandrett

Reputation: 16540

Most jQuery plugins have an options object that you pass in. In here you can define the properties you want to set including event handlers.

The documentation for the zebra accordion events says for each of the events the plugin provides:

The callback function takes 3 arguments:

  • the tab's position in the accordion (0 based)
  • the associated title element, as a jQuery object
  • the tab, as a jQuery object

I've just given them 3 suitable names, and used the second argument (which I arbitrarily named hdr).

As noted in the documentation the hdr argument returned is a jQuery object wrapping the <dt> element (at least in my example). On this object I called the jQuery function .find() to find the element(s) inside that <dt> having the fa-chevron-* class, and then I switch the classes on that span by chaining further jQuery functions.

As noted in the comments you can do it perfectly well in-line like:

var accordian = new $.Zebra_Accordion($('.Zebra_Accordion'), {
    collapsible: true,
    onBeforeOpen: function(index, hdr, body) {
       hdr.find(".fa-chevron-down").removeClass('fa-chevron-down').addClass('fa-chevron-up');
    },
    onBeforeClose: function(index, hdr, body) {
       hdr.find(".fa-chevron-up").removeClass('fa-chevron-down').addClass('fa-chevron-down');
    }
});

And in this particular case I would, but I wanted to illustrate what to do when the handlers have more code, in which case making them separate functions makes for better readability.

Don't forget to use console.log() a lot - it is a JavaScript developer's best friend.

To check what this Zebra Accordion was providing me I did the following first:

onBeforeOpen: function(index, hdr, body) {
       console.log("onBeforeOpen", index, hdr, body);
},

This output shows up in the browser's developer's console.

Here's my demo putting it all together:

$(function() {
  
  // add default chevrons here so they only get appended once
  $(".Zebra_Accordion dt").append("<span class='chevron fa fa-chevron-down'></span>");

  // set up the according options
  var accordian = new $.Zebra_Accordion($('.Zebra_Accordion'), {
    collapsible: true,
    onBeforeOpen: showCollapseChevron,
    onBeforeClose: showExpandChevron
  });
  
  function showExpandChevron(index, hdr, body) {
      hdr.find(".fa-chevron-up").removeClass('fa-chevron-up').addClass('fa-chevron-down');
  }
  
  function showCollapseChevron(index, hdr, body) {
      hdr.find(".fa-chevron-down").removeClass('fa-chevron-down').addClass('fa-chevron-up');
  }
  
});
dl.Zebra_Accordion { width: 100% }
dl.Zebra_Accordion dt { background: #000; color: #FFF; font-weight: bold; padding: 5px }
dl.Zebra_Accordion dd { background: #EFEFEF; padding: 15px; margin: 1px 0 } 
dl.Zebra_Accordion dt.Zebra_Accordion_Expanded { background: #C40000 }

.chevron {
  margin-left: 5px;
}
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/stefangabos/Zebra_Accordion/dist/zebra_accordion.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/gh/stefangabos/Zebra_Accordion/dist/zebra_accordion.min.css">
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">

<dl class="Zebra_Accordion">
  <dt>Lorem ipsum dolor sit amet consectetuer</dt>
  <dd>
    Lorem ipsum dolor sit amet consectetuer facilisis lacinia sapien ac et. Quis hendrerit neque congue pretium iaculis justo laoreet orci elit condimentum. Eros natoque Curabitur accumsan eget quis porttitor Sed Vestibulum amet sed.
  </dd>
  <dt>Lorem ipsum dolor sit amet consectetuer</dt>
  <dd>
    Lorem ipsum dolor sit amet consectetuer facilisis lacinia sapien ac et. Quis hendrerit neque congue pretium iaculis justo laoreet orci elit condimentum. Eros natoque Curabitur accumsan eget quis porttitor Sed Vestibulum amet sed.
  </dd>
  <dt>Lorem ipsum dolor sit amet consectetuer</dt>
  <dd>
    Lorem ipsum dolor sit amet consectetuer facilisis lacinia sapien ac et. Quis hendrerit neque congue pretium iaculis justo laoreet orci elit condimentum. Eros natoque Curabitur accumsan eget quis porttitor Sed Vestibulum amet sed.
  </dd>
  <dt>Lorem ipsum dolor sit amet consectetuer</dt>
  <dd>
    Lorem ipsum dolor sit amet consectetuer facilisis lacinia sapien ac et. Quis hendrerit neque congue pretium iaculis justo laoreet orci elit condimentum. Eros natoque Curabitur accumsan eget quis porttitor Sed Vestibulum amet sed.
  </dd>
</dl>

External Demo https://jsfiddle.net/8wzvucgb/

Upvotes: 9

Arash
Arash

Reputation: 143

The "Plugin Events" aren't really events, and won't work with on().

You can supply callbacks in the invocation that will be called when the accordion opens and closes.

Their "events" are coded in the initial invocation of the plugin:

var myAccordion = new $.Zebra_Accordion($('.Zebra_Accordion'),{
      onOpen:function(index,$title,$block){…},
      onClose:function(index,$title,$block){…}
});

It's an old-fashioned way of doing things. It's much more flexible of plugins use real events.

Upvotes: 3

Emmanuel Delay
Emmanuel Delay

Reputation: 3679

What you might need: watch out for dynamically created html. For example

<script>
...
$('#add').click(function() {
  $('#container').append('<div class="item"> new item </div>')
})
$('#container .item').on('click', function() { ... })
...
</script>  
<input id="add" value="Add 1 item" type="button"/>  
<div id="container">
  <div class="item"> click item 1 </div>
  <div class="item"> click item 2 </div>
</div>

The problem is that the dynamically added items will not respond to the click event, because the html didn't exist yet.


What you do, is this:

$('#container').on('click', '.item', function() { ... })

Then, jQuery will listen to a click on #container, and then at click time it will dynamically search the .item children.

Upvotes: 0

Related Questions