Open zelenin opened 9 years ago
:+1: It's no good that the headers totally disappear when using the Semantic select dropdown.
+1 would be nice improvement
The coding concern is here is JS search dropdowns aren't built to hide headers for empty groups. I don't think we can add this feature without considering filtering empty groups.
I made a workaround using jquery:
$(function () {
// clear the generated semantic-ui menu
$('.menu').html("");
// add the head items based on the optgroup and the items based on the options
$('optgroup').each(function (index, element) {
$('.menu').append('<div class="header">' + element.label + '</div>')
$(element).children().each(function(i, e){
$('.menu').append('<div class="item" data-value="' + e.value + '">' + e.innerHTML + '</div>');
})
})
});
Keep in mind @brunotourinho if you are doing dom insertion you should work on a document fragment. DOM insertion is very costly.
$(function () {
// clear the generated semantic-ui menu
var $menu = $('<div/>').addClass('menu');
// add the head items based on the optgroup and the items based on the options
$('optgroup').each(function (index, element) {
$menu.append('<div class="header">' + element.label + '</div>')
$(element).children().each(function(i, e){
$menu.append('<div class="item" data-value="' + e.value + '">' + e.innerHTML + '</div>');
})
});
$('.menu').replaceWith($menu);
});
Thank you @jlukic !!! ^^
Hey @jlukic, I tested your code and got some animation error:
Transition: There is no css animation matching the one you specified. Please make sure your css is vendor prefixed, and you have included transition css. slide down in
<div class="menu" tabindex="-1">…</div>
e.fn.transition.a.each.C.error @ semantic.min.js:19
e.fn.transition.a.each.C.animate @ semantic.min.js:19
e.fn.transition.a.each.C.initialize @ semantic.min.js:19
(anonymous function) @ semantic.min.js:19
jQuery.extend.each @ jquery-1.10.2.js:671
jQuery.fn.jQuery.each @ jquery-1.10.2.js:280
e.fn.transition @ semantic.min.js:19
e.fn.dropdown.r.each.w.animate.show @ semantic.min.js:14
e.fn.dropdown.r.each.w.show @ semantic.min.js:13
e.fn.dropdown.r.each.w.toggle @ semantic.min.js:13
e.fn.dropdown.r.each.w.determine.eventOnElement @ semantic.min.js:14
e.fn.dropdown.r.each.w.event.test.toggle @ semantic.min.js:13
jQuery.event.dispatch @ jquery-1.10.2.js:5109
jQuery.event.add.elemData.handle @ jquery-1.10.2.js:4780
Sorry replacewith would remove events on $menu. I should have checked
Use
// clear the generated semantic-ui menu
var $menu = $('<div/>').addClass('menu');
// add the head items based on the optgroup and the items based on the options
$('optgroup').each(function (index, element) {
$menu.append('<div class="header">' + element.label + '</div>')
$(element).children().each(function(i, e){
$menu.append('<div class="item" data-value="' + e.value + '">' + e.innerHTML + '</div>');
})
});
$('.dropdown .menu').html($menu.html());
Hello! Works great! Now just a little issue, on your example the scrollbar is offset to 1 (or so), hiding the first header. I couldn't reset using scrollTop(0)
:+1: Currently a limitation for what I need..
Also consider how the label would appear for a multi-select select. Would the label include the optgroup
label? In the case below, would the label text be; "Letters - B", "Letters - C", and "Numbers - 1"?? Or perhaps some sort of divider between the group labels.?.
<select multiple class="ui dropdown">
<optgroup label="Letters">
<option>A</option>
<option selected>B</option>
<option selected>C</option>
</optgroup>
<optgroup label="Numbers">
<option selected>1</option>
<option>2</option>
<option>3</option>
</optgroup>
</select>
In order to be the closest from the original (browsers select) behavior, shouldn't we want to keep an "group container element" ? If that so, I would suggest as the result something like this :
<div class="menu">
<div class="ui basic segment">
<div class="header">Optgroup 1</div>
<div class="item">Option 1</div>
<div class="item">Option 2</div>
</div>
<div class="ui basic segment">
<div class="header">Optgroup 2</div>
<div class="item">Option 3</div>
<div class="item">Option 4</div>
</div>
</div>
Main benefit : being able to select, filter or discriminate the options from a group or an other (through extra ids or attributes).
Main drawback : it breaks the design since the css rule requires an item
to be a direct child of a menu
@jlukic Great work bro, I was looking at this issue and reached your code and it's worked well.
@jlukic when we have multiple dropdowns this optgroup is replacing other dropdowns. please check it and appreciate if you could fix this issue.
@jlukic your code is working fine but still have one problem in it. if you see the attached image "option1" is selected by default by dropdown but not marked as "active selected" for that option.
@merakeshvk here is solution for you :P
var $menu = $('<div/>').addClass('menu');
$('.opt-group').each(function(){
debugger
var parentthis=$(this);
$(this).find('optgroup').each(function (index, element) {
$menu.append('<div class="header">' + element.label + '</div>')
$(element).children().each(function(i, e){
$menu.append('<div class="item" data-value="' + e.value + '">' + e.innerHTML + '</div>');
})
})
$(this).find('.menu').html($menu.html());
$menu="";
$menu = $('<div/>').addClass('menu');
});
@harisahmed11 Thanks bro, Good work.
Fixed Issue by @brunotourinho It's 2017 now but sharing it for somebody like me.
const menu = jQuery('<div/>').addClass('menu');
menu.append('<div class="item" style="display: none"></div>');
jQuery(item).children('optgroup').each((index, element: any) => {
menu.append('<div class="header">' + element.label + '</div>');
jQuery(element).children().each((i, e: any) => {
menu.append('<div class="item" data-value="' + e.value + '">' + e.innerHTML + '</div>');
});
});
jQuery(item).siblings('.menu').html(menu.html());
}
Written in typescript, but I believe code is simple enough to convert for your project. It works by appending hidden item in front of menu.
Hi all! Based on @jlukic's answer, I wrote a generic code snippet that:
<optgroup>
CoffeeScript:
# Hack for Semantic UI multiple select with <optgroup> support
$('.ui.dropdown').has('optgroup').each ->
$menu = $('<div/>').addClass('menu')
# Recreate the dropdown menu with header items for optgroups and normal items for options
$(this).find('optgroup').each ->
$menu.append("<div class=\"header\">#{this.label}</div><div class=\"divider\"></div>")
$(this).children().each ->
$menu.append("<div class=\"item\" data-value=\"#{this.value}\">#{this.innerHTML}</div>")
$(this).find('.menu').html($menu.html())
JavaScript:
$('.ui.dropdown').has('optgroup').each(function() {
var $menu;
$menu = $('<div/>').addClass('menu');
$(this).find('optgroup').each(function() {
$menu.append("<div class=\"header\">" + this.label + "</div><div class=\"divider\"></div>");
return $(this).children().each(function() {
return $menu.append("<div class=\"item\" data-value=\"" + this.value + "\">" + this.innerHTML + "</div>");
});
});
return $(this).find('.menu').html($menu.html());
});
Demonstration: http://jsfiddle.net/hqqpvohL
😉
(However, there is still a tiny glitch: when you open a long dropdown, the focus goes on the first option, not on the first group label, as mentioned in #5099)
You can also make a disabled element as category header. This also looks nice with semantic-ui.
Like this:
<select id="category" class="ui selection dropdown">
<option disabled>Category 1</option>
<option value="1"> - Menu 1</option>
<option value="2"> - Menu 2</option>
<option disabled>Category 2</option>
<option value="3"> - Menu 3</option>
<option value="4"> - Menu 4</option>
</select>
Modified the code by @tristanjahier a bit so selected option would by highlighted.
$('.ui.dropdown').has('optgroup').each(function() {
const $menu = $('<div/>').addClass('menu');
$(this).find('optgroup').each(function() {
$menu.append("<div class=\"header\">" + this.label + "</div><div class=\"divider\"></div>");
return $(this).children().each(function() {
return $menu.append("<div class=\"item" + (this.selected ? ' active selected' : '') + "\" data-value=\"" + this.value + "\">" + this.innerHTML + "</div>");
});
});
return $(this).find('.menu').html($menu.html());
});
This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 30 days if no further activity occurs. Thank you for your contributions.
Bump, due to stale bot. Still a valid request.
Small caveat with @genabasov latest iteration of the code, it removes any separate 'option' that might have been in the select.
Fixing this issue in the actual dropdown.js
seems to be a lot more complicated than I initially expected, so I've updated the code to also handle any individual 'option' and also respect the disabled status for options and optgroups while also prioritizing any label over text as defined here.
$('.ui.dropdown').has('optgroup').each(function() {
const $menu = $('<div/>').addClass('menu');
$(this).find('select').children().each(function() {
if($(this).is('option')) {
return $menu.append('<div class="item' + (this.selected ? ' active selected' : '') + (this.disabled ? ' disabled' : '') + '" data-value="' + this.value + '">' + (this.label || this.innerHTML) + "</div>");
}
if($(this).is('optgroup')) {
var isDisabled = this.disabled || false;
$menu.append('<div class="header' + (isDisabled ? ' item disabled' : '') + '">' + this.label + '</div>');
//$menu.append('<div class="divider"></div>');
$(this).children().each(function() {
return $menu.append('<div class="item' + (this.selected ? ' active selected' : '') + (isDisabled || this.disabled ? ' disabled' : '') + '" data-value="' + this.value + '">' + (this.label || this.innerHTML) + "</div>");
});
return $menu;
}
});
return $(this).find('.menu').html($menu.html());
});
@zelenin: What if I want to use option as an object instead to achieve grouping ? I have dynamic options so I can't really use option tags. I work with semantic ui react.
I've added the group label to the option's data-text attribute (data-text="
$('.ui.dropdown').has('optgroup').each(function () {
var $menu = $('<div/>').addClass('menu');
$(this).find('select').children().each(function () {
if ($(this).is('option')) {
return $menu.append('<div class="item' + (this.selected ? ' active selected' : '') + (this.disabled ? ' disabled' : '') + '" data-value="' + this.value + '">' + (this.label || this.innerHTML) + "</div>");
}
if ($(this).is('optgroup')) {
var isDisabled = this.disabled || false;
var groupLabel = this.label;
$menu.append('<div class="header' + (isDisabled ? ' item disabled' : '') + '">' + groupLabel + '</div>');
$(this).children().each(function () {
return $menu.append('<div class="item' + (this.selected ? ' active selected' : '') + (isDisabled || this.disabled ? ' disabled' : '') + '" data-value="' + this.value + '" data-text="' + groupLabel + ': ' + this.label + '">' + (this.label || this.innerHTML) + "</div>");
});
return $menu;
}
});
return $(this).find('.menu').html($menu.html());
});
optgroups are now converted to headers in Fomantic-UI by https://github.com/fomantic/Fomantic-UI/pull/957
base on @tristanjahier :
$('.ui.dropdown').has('optgroup').each(function() {
const $menu = $('<div/>').addClass('menu');
$(this).find('optgroup').each(function() {
$menu.append("<div class=\"ui horizontal divider\"><div class=\"header\">" + this.label + "</div></div>");
return $(this).children().each(function() {
return $menu.append("<div class=\"item" + (this.selected ? ' active selected' : '') + "\" data-value=\"" + this.value + "\">" + this.innerHTML + "</div>");
});
});
return $(this).find('.menu').html($menu.html());
});
$('.ui.dropdown .menu>.divider').css('border-top','none');
Now Semantic UI not parses optgroups in select creating a dropdown like flat select.
I offer display optgroups like a dropdown header. See jsfiddle:
http://jsfiddle.net/zelenin/aLoanbwb/1/