3
\$\begingroup\$

Basically, the code converts a large select list with optgroups into a more user-friendly tabbed Twitter-bootstrap component.

I'm not sure how much into detail I need to get but on GitHub I uploaded a working example. This is the first time I write a jquery plugin and any advise is welcome.

(function($) {
    $.fn.bootstrapSelect = function(options) {

        //Extend default options with those provided.
        var opts = $.extend({}, $.fn.bootstrapSelect.defaults, options);

        var topLevel='';
        var subLevel='';
        var selectedValue ='';
        var sectionActive = false;
        var selected = '';

        // Rewrite the info in the select box 
        $('optgroup',this).each(function(x) { //for each optgroup

            //get section label
            var sectionLabel = $(this).attr('label');

            //get section content
            var sectionContent = '';
            $('option',this).each(function(x) {

                /*Check if an option is selected, and set section active*/
                if ($(this).attr('selected')) {
                    selectedValue = $(this).attr('value');
                    sectionActive = true;
                }
                sectionContent += '<a ';
                $(this).attr('selected') ? sectionContent += 'class="bsactive" ' : '';
                sectionContent += 'value="' + $(this).attr('value') + '">' + $(this).text() + '</a>\n';
            });

            sectionActive ? selected = true : false;

            topLevel += '<li';
            sectionActive ? topLevel += ' class="active"' : '';
            topLevel += '><a href="#optgroup' + x + '" data-toggle="tab">' + sectionLabel + '</a></li>\n';

            subLevel += '<div id="optgroup' + x + '" class="tab-pane';
            sectionActive ? subLevel += ' active' : '';
            subLevel += '">\n' + sectionContent + '</div>\n';

            sectionActive = false;

        });
        //Replacement tabbed select
        var tabbedselect = '<div class="tabbable '+opts.position+'">\n';
        if ( opts.position != "tabs-below") tabbedselect += '<ul class="nav nav-tabs">\n'+topLevel+'</ul>\n';
        tabbedselect += '<div class="tab-content">\n'+subLevel+'</div>\n'
        if ( opts.position == "tabs-below") tabbedselect += '<ul class="nav nav-tabs">\n'+topLevel+'</ul>\n';
        tabbedselect += '</div>\n';

        // Replace the select with the tabbed menu
        this.replaceWith(tabbedselect);

        // Add hidden input field with the selected value
        $("form").append('<input type="hidden" name="'+opts.inputId+'" id="'+opts.inputId+'" value="'+selectedValue+'" />');

        //set first tab and content area active if nothing is selected
        if (selected == false) {
            $("div.tab-content div:first-child").addClass("active");
            $("ul.nav-tabs li:first-child").addClass("active");
        }

        // set input field value on click
        $(".tab-content a").click(function () {
            $("*").removeClass("bsactive");
            $(this).addClass("bsactive");       
            $('input#'+opts.inputId).attr('value', $(this).attr('value'));
        })
    };

    //default settings
    $.fn.bootstrapSelect.defaults = {
        'position' : 'tabs-left',
        'inputId'  : 'bootstrap-select'
    };

})(jQuery);
\$\endgroup\$
  • \$\begingroup\$ I set up a working example at tinker.io/fce06, would you please take a look an make sure that it looks and works as you would expect? It looks good to me. \$\endgroup\$ – Dan Ross Jun 12 '13 at 21:13
1
\$\begingroup\$

In order to be able to use this plugin on a page with multiple forms, I would recommend identifying the specific form that you are operating on. I can see that you are aware of the issue because you have been using the context parameter in most of the selectors, but since the <form> is an ancestor of this, it would need to be selected differently:

$("form").append(...

should be

$(this).ancestor('form').append(...

Also, I just wanted to mention a personal preference when working with HTML snippets generated by jQuery. Instead of having HTML in JS strings, like

$(this).ancestor("form").append(
    '<input type="hidden" name="'+opts.inputId+'" id="'+opts.inputId+'" value="'+selectedValue+'" />'
);

I like to make hidden template elements, and then clone them, remove the .template class, set the attributes and inner html, and insert the clone into the document. For example:

// stylesheet:
.template { display: none; }

// html, near the end of the <body>:
<input type="hidden" id="hiddenInputTemplate" class="template"/>

// js:
$(this).ancestor('form').append(
    $('#hiddenInputTemplate')
    .clone()
    .removeClass('template')
    .attr('name', opts.inputId)
    .attr('id', opts.inputId)
    .val(selectedValue)
);

For a short example like this, it might not be worth the extra verbosity, but I do have reasons for doing it this way:

  • My editor can't highlight HTML code, and especially syntax errors, inside of JS strings.
  • I can read and understand this style a little faster, and I will read it more often than I will write it. Like I said, it's just personal preference.
\$\endgroup\$
  • \$\begingroup\$ Thanks for the review. I tried using "ancestor" but it didn't work, so I tried to see the documentation for it but didn't find it. I found "parents" which I believe does what was intended, the problem is that doesn't work either. I believe it is because I do a "replaceWith" right before, so the object doesn't exist anymore. As a workaround I added the input field to the tabbedselect variable. I also made other changes to support multiple instances. \$\endgroup\$ – plinto Jun 13 '13 at 20:34
  • \$\begingroup\$ Would it work to select the form and keep the selection in a variable, before doing the replaceWith? You would still be doing form.append(... after replaceWith. \$\endgroup\$ – Dan Ross Jun 13 '13 at 22:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.