Iamsamstimpson
Iamsamstimpson

Reputation: 1357

jQuery: Learning to make a Plugin: Basic Accordion Plugin

Again thank you to all who spend a little of their time helping me understand jQuery a little more, its very much appreciated.

I'm trying to make an effort and push my jQuery further, slowly. So I have been quite active within stackoverflow to ask questions to problems I'm facing..asking "why" a lot.. I'm very comfortable with jQuery and use it on a daily basis. However there is always more to learn and gaps in my knowledge to fill. So I decided to try and make a jQuery plugin - something very simple that I can expand as a personal project so I can learn doing something hands on.

So, I chose to make a very simple accordion plugin. jsFiddle of the current accordion

(function($){
//The current script for the plugin..
    $.fn.simpleAccordion = function() { 
        $("li a").live("click", function(e){
            e.preventDefault();
            $("li div").slideUp(200);
            $(this).next(':hidden').slideDown(200);
        });
    };
})(jQuery);

FIRST PROBLEM: I've been reading the jQuery docs Plugins/Authoring. The first problem I have come across, in the documents is states $(this) is commonly used incorrectly.. and should be "this.fadeIn('normal', function(){..." -

 $(this).next(':hidden').slideDown(200);

Am I using "this" in the right context within the line above? At present I understand that "this" is referencing the selector the plugin was invoked on.. so ul#accordion.. then why doesnt it work without $("")..

$(document).ready(function(){
    $("ul#accordion").simpleAccordion();
}); 

THE SECOND PROBLEM: I think I found a solution to this myself (see below) I was testing what I have already and realised if I put an ancor tag/link in the content body of my accordion, this link will also open and close the panels.. I tried to change the selector to the click function to

$("li a:first-child").live("click",.. 

to make sure only the first ancor tag proceeding the link that was clicked had an event attached.. but no luck...I do not want every single a tag within an li to fire off the slide functions I want them left alone.. so would it be best giving the a class name or using a much more specific selector? :P

I think I found the correct solution to the second question (below)

$("li > a:first-of-type").live("click", function(e){

Thanks again, I know this was a little wordy!

jsFiddle of the current accordion

Upvotes: 0

Views: 273

Answers (2)

Richard Neil Ilagan
Richard Neil Ilagan

Reputation: 14737

Let me try to walk you through this a bit.

First, when you do this:

$.fn.foo = function () {
    // some code
}

This let you do this:

$('div').foo();

The clincher here is that, inside foo, the this variable would be equivalent to the selection the function was acting on. In this case,

this === $('div')

Therefore, I don't see the point of selecting a completely different selection inside your plugin function (i.e. $('li a')).

You probably mean it like this:

$.fn.simpleAccordion = function () {
    var stuff = this.find('li a');
    // use your stuff
    return this; // for chaining
};

Secondly (and completely out of the question context), stop using .live(). Not only does it not make sense (you're making your plugin function act on something that looks like it should already be existing), it's arguably bad practice. Use .on() instead, or if you're using jQuery pre-1.7, .delegate(). But make sure first that it's not .bind() you really want (which I highly suspect).

To answer your second question, it's really a little vague. But from experience, there's almost always a way to select (and therefore modify / use) HTML elements in your DOM without having to flag them with decorator classes much like you'd use to identify / mark them.

If :first-child is firing off animations on more elements than you would've wanted, I suspect that you're either using the wrong selector/s, or you're not confining your jQuery method calls enough (like if you're misusing this, or selecting again inside a plugin function -- see point 1 above).

Without HTML markup, I can't point you to what you should improve though.

I don't know how much it can help you, but I wrote a really quick article on plugin authoring some time before. You might want to take a look.

Upvotes: 1

StilgarBF
StilgarBF

Reputation: 1060

for your first question: this in the context of an event-callback is always the element that has triggered the event. To use all the jQuery magic, this element hast to be extended by $(this). Only then it is a jQuery-Object. In your plugin you should not use $("li div") because that will give you all divs in an li on your complete page. You want only those within the current context. So use $(this) and then parents(), find() etc to go up or down in the DOM.

On your plugin: Read on in the Plugin-Tutorial. You have to change your code to make it a real plugin. At this state you are breaking the chain. something like $('.element).simpleAccordion().data('foo','bar') won't work because you don't return the object again.

And: don't use live() which is deprecated. have a look at .on() in the docs. http://api.jquery.com/on/

hope that helps

Upvotes: 1

Related Questions