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);  • 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. – Dan Ross Jun 12 '13 at 21:13 ## 1 Answer 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.
• 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. – plinto Jun 13 '13 at 20:34
• 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. – Dan Ross Jun 13 '13 at 22:59