如何用jQuery将含optgroup的select转换为对应结构的ul/li?
Hey there! Let's fix that duplicate option issue you're dealing with when converting <select> elements to custom dropdowns. I’ve run into this exact problem before, so I know exactly where you might be slipping up.
The Root Cause
Your current code is probably grabbing all <option> elements in the entire <select> every time you process an <optgroup>, which is why every group ends up with every option. The fix is simple: you need to only target the options that are direct children of the current <optgroup> you’re processing.
Solution Code
Here’s a clean, targeted jQuery implementation that handles both <optgroup> and standalone <option> cases correctly:
// Iterate over each target select element $('select.custom-select').each(function() { const $originalSelect = $(this); // Create a container for our custom dropdown const $customDropdown = $('<div class="custom-dropdown-container"></div>'); // Check if the select has optgroups if ($originalSelect.find('optgroup').length) { // Loop through each optgroup in the current select $originalSelect.find('optgroup').each(function() { const $currentOptgroup = $(this); // Create a UL to represent the optgroup const $groupList = $('<ul class="dropdown-group" data-group-label="' + $currentOptgroup.attr('label') + '"></ul>'); // ONLY grab options inside THIS optgroup (not all options in the select!) $currentOptgroup.find('option').each(function() { const $currentOption = $(this); const $listItem = $('<li class="dropdown-item" data-value="' + $currentOption.val() + '">' + $currentOption.text() + '</li>'); $groupList.append($listItem); }); // Add the group to our custom dropdown $customDropdown.append($groupList); }); } else { // No optgroups? Just convert all options directly to list items $originalSelect.find('option').each(function() { const $currentOption = $(this); const $listItem = $('<li class="dropdown-item" data-value="' + $currentOption.val() + '">' + $currentOption.text() + '</li>'); $customDropdown.append($listItem); }); } // Insert the custom dropdown after the original select, then hide the original $originalSelect.after($customDropdown); $originalSelect.hide(); });
Key Fix Explained
The critical line here is $currentOptgroup.find('option') — by using $currentOptgroup as the parent selector instead of $originalSelect, we only pull options that belong to the specific <optgroup> we’re currently converting. This eliminates the duplicate options across groups.
Bonus: Basic CSS for the Custom Dropdown
To make it look like a proper dropdown, add this simple styling:
.custom-dropdown-container { border: 1px solid #ddd; border-radius: 4px; padding: 6px; max-height: 250px; overflow-y: auto; width: 200px; } .dropdown-group { list-style: none; padding: 0; margin: 8px 0; } .dropdown-group::before { content: attr(data-group-label); font-weight: 600; color: #555; padding: 4px 0; display: block; border-bottom: 1px solid #eee; } .dropdown-item { padding: 4px 8px; cursor: pointer; border-radius: 2px; } .dropdown-item:hover { background-color: #f5f5f5; }
This will give you a clean, functional custom dropdown that respects your original <optgroup> structure without duplicate options.
内容的提问来源于stack exchange,提问作者user12682229

