Reputation: 757
<select id="myElement" multiple="multiple">
<option value="1">Category Type</option>
<option value="158">itemOne</option>
<option value="157">itemTwo</option>
<option value="7">My Type</option>
<option value="20">itemThree</option>
<option value="21">itemFour</option>
<option value="22">itemFive</option>
<option value="8">Category Yet Another</option>
<option value="31">itemCheese</option>
<option value="32">itemBrain</option>
</select>
I need to dynamically convert this so that "Category" options (anything that doesn't start with "item") is an optgroup, wrapping whaterver comes after it until the next Category option, so the above would end up looking like:
<select id="myElement" multiple="multiple">
<optGroup label="Category Type">
<option value="158">One</option>
<option value="157">Two</option>
</optGroup>
<optGroup label="My Type">
<option value="20">Three</option>
<option value="21">Four</option>
<option value="22">Five</option>
</optGroup>
<optGroup label="Category Yet Another">
<option value="31">Cheese</option>
<option value="32">Brain</option>
</optGroup>
</select>
How can I iterate over this and change values to acheive the desired effect using jQuery?
Upvotes: 11
Views: 30246
Reputation: 12701
You have to iterate over the option
elements and group them based on their contents. Using jQuery makes it way easier than just pure DOM API:
http://jsfiddle.net/kpykoahe/2/
$(document).ready(() => {
const $cont = $('select');
$('option').each((idx, el) => {
const $el = $(el);
if ($el.text().startsWith('item')) {
$cont.find('optgroup').last().append($el);
$el.text($el.text().substr(4));
} else {
$('<optgroup/>').attr('label', $el.text()).appendTo($cont);
$el.remove();
}
});
});
Upvotes: 20
Reputation: 74
$.each(data, function () {
if (prevGroupName.indexOf(this.Group.Name) == -1) {
$prevGroup = $('<optgroup />').prop('label', this.Group.Name).appendTo('#ConfigurationId');
} else {
$prevGroup = $("optgroup[label='" + this.Group.Name + "']");
}
$("<option />").val(this.Value).text(this.Text).appendTo($prevGroup);
prevGroupName.push(this.Group.Name);
});
Upvotes: 2
Reputation: 17288
My implementation:
$(function(){
var target = $('#target'),
optGroup;
function strStartsWith(str, prefix) {
return str.indexOf(prefix) === 0;
}
$('#myElement').find('option').each(function(){
var elm = $(this).clone();
if(strStartsWith(elm.text(), 'item'))
{
elm.text(elm.text().substr(4));
if(optGroup) optGroup.append(elm);
}
else
{
optGroup = $('<optgroup>').attr('label', elm.text());
target.append(optGroup);
}
});
});
Demo: http://jsfiddle.net/HUHRz/1/
Upvotes: 2
Reputation: 262919
You can use filter() to match the <option>
elements that will become groups. Then, you can iterate over them and call nextUntil() on the same element set to match the subsequent options (as nextUntil()
will stop when it encounters a member of the set, i.e. the next option that should become a group).
From there, you can use wrapAll() on these <option>
elements to create their <optgroup>
element, then call remove() on the <option>
element that just became a group. Something like:
var $groups = $("#myElement option").filter(function() {
return $(this).text().indexOf("item") != 0;
});
$groups.each(function() {
var $this = $(this);
$this.nextUntil($groups).wrapAll($("<optgroup>", {
label: $this.text()
})).end().remove();
});
You can see the results in this fiddle.
Upvotes: 6