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);
-
\$\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– Dan Ross2013年06月12日 21:13:13 +00:00Commented Jun 12, 2013 at 21:13
1 Answer 1
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.
-
\$\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\$abc– abc2013年06月13日 20:34:52 +00:00Commented Jun 13, 2013 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 doingform.append(...
afterreplaceWith
. \$\endgroup\$Dan Ross– Dan Ross2013年06月13日 22:59:26 +00:00Commented Jun 13, 2013 at 22:59
Explore related questions
See similar questions with these tags.