www.webdeveloper.com
Results 1 to 2 of 2

Thread: How best to build my first jQuery pluggin

  1. #1
    Join Date
    Dec 2005
    Posts
    264

    How best to build my first jQuery pluggin

    I am creating my first jQuery plugin and have a somewhat working example (see http://jsfiddle.net/SN4aZ/1/ and below script). My primary purpose of doing so is to learn a consistent pattern on how they should be built. I am strongly basing my approach on http://docs.jquery.com/Plugins/Authoring (any reason I shouldn't?). I am hoping someone can review it, confirm whether I am correctly interpreting how to build a plugin, and provide any suggestions. I have a feeling that how I replace the original element might have a little to be desired. The CSS is weak, but hopefully I can improve it after the JavaScript is complete. I am also not sure whether I really get the whole namespace thing.

    The desired look and functionality starts with showing some text and an optional down arrow to the right of the text. Upon clicking, the text/arrow is replaced with a list of options with the currently selected option highlighted and at the same location of the original text. Clicking an option makes it the currently selected option, and initiates a user defined callback, and clicking any other space closes the dialog.

    Thanks!

    HTML Code:
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
    <html xmlns="http://www.w3.org/1999/xhtml"> 
        <head> 
            <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" /> 
            <title>Testing</title>  
            <script src="http://code.jquery.com/jquery-latest.js"></script> 
            <style type="text/css">
    
                /* CSS Not associated with plugin */
                #myList > li {
                    list-style-type: none;
                    display:inline;
                }
                div.selectIt {
                    display:inline;
                }
    
                /* CSS associated with plugin */
    
                li.selectIt-hover {
                    font-weight:bold;
                }
                ul.selectIt-list {
                    display:none;
                }
                ul.selectIt-list li.selectIt-selected {
                    background-color:yellow;
                }
                ul.selectIt-list li {
                    list-style-type: none;;
                }
                span.ddsArrow {
                    background-image: url("dropdown_arrow_blue.gif");
                    background-repeat: no-repeat;
                    width:10px;
                }
            </style> 
            <script> 
                /*
                * jQuery SelectIT
                * Copyright 2012 Michael Reed
                * Dual licensed under the MIT and GPL licenses.
                * 
                */
                (function( $ ){
    
                    var methods = {
                        init : function( options ) {
    
                            // Create some defaults, extending them with any options that were provided
                            var settings = $.extend({
                                'class' : null, //Applies custom class so user may override
                                'image' : true,  //If true, includes span for background graphic
                                'clickIt' : function(){}   //Some user defined code.
                                }, options  || {});
    
                            //Apply events that should only happen once (unlike what is done by http://docs.jquery.com/Plugins/Authoring#Events. Why did they do it that way?)
                            $(document).on('click.selectIt',methods.close);
    
                            return this.each(function(){
    
                                var $t = $(this),
                                data = $t.data('selectIt'),
                                list='';
    
                                //Replace element
                                $t.find('option').each(function(){
                                    var $t=$(this);
                                    list+='<li data-value="'+$t.val()+'"'+(($t.attr('selected'))?' class="selectIt-selected"':'')+'>'+$t.text()+'</li>';
                                });
                                var selectIt = $('<div class="selectIt"><div class="selectIt-text"><span class="selectIt-name ">'+$t.find(":selected").text()+'</span>'+(settings.image?'<span class="ddsArrow" />':'')+'</div><ul class="selectIt-list">'+list+'</ul></div>');
                                if(settings.class){selectIt.addClass(settings.class);}
    
                                //Apply events
                                selectIt.on('click.selectIt','div.selectIt-text',methods.open);
                                selectIt.on('mouseenter.selectIt','ul.selectIt-list li',methods.mouseenter);
                                selectIt.on('mouseleave.selectIt','ul.selectIt-list li',methods.mouseleave);
                                selectIt.on('click.selectIt','ul.selectIt-list li',methods.click);
    
                                // If the plugin hasn't been initialized yet.  Why?
                                if ( ! data ) {
                                    // Do more setup stuff here
                                    selectIt.data('selectIt', {
                                        target : $t,     //Not really sure where this will be used
                                        selectIt : selectIt,  //Will be used when removing in destroy method
                                        settings: settings  //Save here so other methods have access
                                    });
    
                                    $t.replaceWith(selectIt);
    
                                }
                            });
                        },
    
                        destroy : function(e) {
                            return this.each(function(){
                                var $t = $(this);
                                $t.off('.selectIt'); //Removes any events in selectIt namespace
                                $t.data('selectIt').selectIt.remove();   //Removes element from page
                                $t.removeData('selectIt');   //Removes data in selectIt namespace
                            })
                        },
    
                        open : function(e) {
                            methods.close();
                            $(this).hide().parent().find('ul.selectIt-list').show();
                            e.stopPropagation();    //Don't trigger documents click
                        },
    
                        //If this is all I am doing, can change to just using CSS psudo class.
                        mouseenter : function(e) {
                            $(this).addClass('selectIt-hover');
                        },
                        mouseleave : function(e) {
                            $(this).removeClass('selectIt-hover');
                        },
    
                        click : function(e) {
                            var $t=$(this),$p=$t.parent(),$pp=$p.parent();
                            if(!$t.hasClass('selectIt-selected'))
                            {
                                $p.find('li.selectIt-selected').removeClass('selectIt-selected');
                                $t.addClass('selectIt-selected');
                                $pp.find('span.selectIt-name').text($t.text());
                                $pp.data('selectIt').settings.clickIt.call(this);
                            }
                        },
    
                        close : function(e) {
                            $('div.selectIt').find('ul.selectIt-list').hide().parent().find('div.selectIt-text').show();
                        },
    
                        update : function(content) {alert('When will this type of method be used?');}
                    };
    
                    $.fn.selectIt = function(method) {
                        if ( methods[method] ) {
                            return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
                        } else if ( typeof method === 'object' || ! method ) {
                            return methods.init.apply( this, arguments );
                        } else {
                            $.error( 'Method ' +  method + ' does not exist on jQuery.selectIt' );
                        }    
                    };
    
                })( jQuery );
    
                $(function(){
                    $('.mySelect').selectIt({
                        'class' : 'whatever',
                        'image' : false,
                        'clickIt':function(){alert('Do something using '+$(this).attr('data-value'));}
                    });
                });
            </script>
        </head>
    
        <body>
            <ul id="myList">
                <li>
                    <label>Some Label</label>
                    <select class="mySelect">
                        <option value="first" selected="selected">First</option>
                        <option value="second">Second</option>
                        <option value="third">Third</option>
                    </select>
                </li>
                <li>
                    <span>Some Label</span>
                    <select class="mySelect">
                        <option value="first">First</option>
                        <option value="second" selected="selected">Second</option>
                        <option value="third">Third</option>
                    </select>
                </li>
            </ul>
        </body> 
    </html> 

  2. #2
    Join Date
    Dec 2005
    Posts
    264
    Instead of replacing the select element, I now am leaving it in place and hiding it, updating its value based on which list element was clicked, and not using the data-value attribute (see http://jsfiddle.net/SN4aZ/2/). This seems to make more sense, however, I am struggling on how the callback function will know how to act differently based on what is currently selected. Yes, the call back can traverse the DOM to find the select element, but seems would rather have it more easily obtainable. Maybe keep the data-value attribute? Thanks


    HTML Code:
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
    <html xmlns="http://www.w3.org/1999/xhtml"> 
        <head> 
            <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" /> 
            <title>Testing</title>  
            <script src="http://code.jquery.com/jquery-latest.js"></script> 
            <style type="text/css">
    
                /* CSS Not associated with plugin */
                #myList > li {
                    list-style-type: none;
                    display:inline;
                }
                div.selectIt {
                    display:inline;
                }
    
                /* CSS associated with plugin */
    
                li.selectIt-hover {
                    font-weight:bold;
                }
                ul.selectIt-list {
                    display:none;
                }
                ul.selectIt-list li.selectIt-selected {
                    background-color:yellow;
                }
                ul.selectIt-list li {
                    list-style-type: none;;
                }
                span.ddsArrow {
                    background-image: url("dropdown_arrow_blue.gif");
                    background-repeat: no-repeat;
                    width:10px;
                }
            </style> 
            <script> 
                /*
                * jQuery SelectIT
                * Copyright 2012 Michael Reed
                * Dual licensed under the MIT and GPL licenses.
                * 
                */
                (function( $ ){
    
                    var methods = {
                        init : function( options ) {
    
                            // Create some defaults, extending them with any options that were provided
                            var settings = $.extend({
                                'class' : null, //Applies custom class so user may override
                                'image' : true,  //If true, includes span for background graphic
                                'clickIt' : function(){}   //Some user defined code.
                                }, options  || {});
    
                            //Apply events that should only happen once (unlike what is done by http://docs.jquery.com/Plugins/Authoring#Events. Why did they do it that way?)
                            $(document).on('click.selectIt',methods.close);
    
                            return this.each(function(){
    
                                var $t = $(this),
                                data = $t.data('selectIt'),
                                list='';
    
                                //Replace element
                                $t.find('option').each(function(){
                                    var $t=$(this);
                                    list+='<li'+(($t.attr('selected'))?' class="selectIt-selected"':'')+'>'+$t.text()+'</li>';
                                });
                                var selectIt = $('<div class="selectIt"><div class="selectIt-text"><span class="selectIt-name ">'+$t.find(":selected").text()+'</span>'+(settings.image?'<span class="ddsArrow" />':'')+'</div><ul class="selectIt-list">'+list+'</ul></div>');
                                if(settings.class){selectIt.addClass(settings.class);}
    
                                //Apply events
                                selectIt.on('click.selectIt','div.selectIt-text',methods.open);
                                selectIt.on('mouseenter.selectIt','ul.selectIt-list li',methods.mouseenter);
                                selectIt.on('mouseleave.selectIt','ul.selectIt-list li',methods.mouseleave);
                                selectIt.on('click.selectIt','ul.selectIt-list li',methods.click);
    
                                // If the plugin hasn't been initialized yet.  Why?
                                if ( ! data ) {
                                    // Do more setup stuff here
                                    selectIt.data('selectIt', {
                                        target : $t,     //Not really sure where this will be used
                                        selectIt : selectIt,  //Will be used when removing in destroy method
                                        settings: settings  //Save here so other methods have access
                                    });
    
                                    $t.hide().after(selectIt); //Changed
    
                                }
                            });
                        },
    
                        destroy : function(e) {
                            return this.each(function(){
                                var $t = $(this);
                                $t.off('.selectIt'); //Removes any events in selectIt namespace
                                $t.data('selectIt').selectIt.remove();   //Removes element from page
                                $t.removeData('selectIt');   //Removes data in selectIt namespace
                            })
                        },
    
                        open : function(e) {
                            methods.close();
                            $(this).hide().parent().find('ul.selectIt-list').show();
                            e.stopPropagation();    //Don't trigger documents click
                        },
    
                        //If this is all I am doing, can change to just using CSS psudo class.
                        mouseenter : function(e) {
                            $(this).addClass('selectIt-hover');
                        },
                        mouseleave : function(e) {
                            $(this).removeClass('selectIt-hover');
                        },
    
                        click : function(e) {
                            var $t=$(this),$p=$t.parent(),$pp=$p.parent();
                            if(!$t.hasClass('selectIt-selected'))
                            {
                                var select=$pp.prevAll("select:first");
                                var option=select.find('option').eq($t.index());
                                var value=(value=option.val())?value:option.text();
                                select.val(value);
                                $p.find('li.selectIt-selected').removeClass('selectIt-selected');
                                $t.addClass('selectIt-selected');
                                $pp.find('span.selectIt-name').text($t.text());
                                $pp.data('selectIt').settings.clickIt.call(this);
                            }
                        },
    
                        close : function(e) {
                            $('div.selectIt').find('ul.selectIt-list').hide().parent().find('div.selectIt-text').show();
                        },
    
                        update : function(content) {alert('When will this type of method be used?');}
                    };
    
                    $.fn.selectIt = function(method) {
                        if ( methods[method] ) {
                            return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
                        } else if ( typeof method === 'object' || ! method ) {
                            return methods.init.apply( this, arguments );
                        } else {
                            $.error( 'Method ' +  method + ' does not exist on jQuery.selectIt' );
                        }    
                    };
    
                })( jQuery );
    
                $(function(){
                    $('.mySelect').selectIt({
                        'class' : 'whatever',
                        'image' : false,
                        'clickIt':function(){alert('Do something');}
                    });
                });
            </script>
        </head>
    
        <body>
            <ul id="myList">
                <li>
                    <label>Some Label</label>
                    <select class="mySelect">
                        <option value="first" selected="selected">First</option>
                        <option value="second">Second</option>
                        <option>Third</option>
                    </select>
                </li>
                <li>
                    <span>Some Label</span>
                    <select class="mySelect">
                        <option value="first">First</option>
                        <option value="second" selected="selected">Second</option>
                        <option value="third">Third</option>
                    </select>
                </li>
            </ul>
        </body> 
    </html> 
    Last edited by NotionCommotion; 10-29-2012 at 10:24 AM.

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
HTML5 Development Center



Recent Articles