如何将选项组添加到 multiselect-dropdown.js 插件?

How to add option groups to the multiselect-dropdown.js plugin?

我正在使用 multiselect-dropdown.js 插件来自定义我的 select 标签。它 运行 非常完美,但是当我添加一个选项组时它不会显示。是否有解决方案可以在没有复选框的情况下显示选项组标签?我只想添加选项组标签。因为现在,选项组没有显示。

var style = document.createElement('style');
style.setAttribute("id","multiselect_dropdown_styles");
style.innerHTML = `
.multiselect-dropdown{
  display: inline-block;
  padding: 2px 5px 0px 5px;
  border-radius: 4px;
  border: solid 1px #ced4da;
  background-color: white;
  position: relative;
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");
  background-repeat: no-repeat;
  background-position: right .75rem center;
  background-size: 16px 12px;
}
.multiselect-dropdown span.optext, .multiselect-dropdown span.placeholder{
  margin-right:0.5em; 
  margin-bottom:3px;
  padding:1px 0; 
  border-radius: 4px; 
  display:inline-block;
}
.multiselect-dropdown span.optext{
  background-color:lightgray;
  padding:1px 0.75em; 
}
.multiselect-dropdown span.placeholder{
  color:#ced4da;
}
.multiselect-dropdown-list-wrapper{
  box-shadow: gray 0 3px 8px;
  z-index: 100;
  padding:2px;
  border-radius: 4px;
  border: solid 1px #ced4da;
  display: none;
  margin: -1px;
  position: absolute;
  top:0;
  left: 0;
  right: 0;
  background: white;
}
.multiselect-dropdown-list-wrapper .multiselect-dropdown-search{
  margin-bottom:5px;
}
.multiselect-dropdown-list{
  padding:2px;
  height: 15rem;
  overflow-y:auto;
  overflow-x: hidden;
}
.multiselect-dropdown-list::-webkit-scrollbar {
  width: 6px;
}
.multiselect-dropdown-list::-webkit-scrollbar-thumb {
  background-color: #bec4ca;
  border-radius:3px;
}

.multiselect-dropdown-list div{
  padding: 5px;
}
.multiselect-dropdown-list input{
  height: 1.15em;
  width: 1.15em;
  margin-right: 0.35em;  
}
.multiselect-dropdown-list div.checked{
}
.multiselect-dropdown-list div:hover{
  background-color: #ced4da;
}
.multiselect-dropdown span.maxselected {width:100%;}
.multiselect-dropdown-all-selector {border-bottom:solid 1px #999;}
`;
document.head.appendChild(style);

function MultiselectDropdown(options){
  var config={
    search:true,
    height:'15rem',
    placeholder:'select',
    txtSelected:'selected',
    txtAll:'All',
    ...options
  };
  function newEl(tag,attrs){
    var e=document.createElement(tag);
    if(attrs!==undefined) Object.keys(attrs).forEach(k=>{
      if(k==='class') { Array.isArray(attrs[k]) ? attrs[k].forEach(o=>o!==''?e.classList.add(o):0) : (attrs[k]!==''?e.classList.add(attrs[k]):0)}
      else if(k==='style'){  
        Object.keys(attrs[k]).forEach(ks=>{
          e.style[ks]=attrs[k][ks];
        });
       }
      else if(k==='text'){attrs[k]===''?e.innerHTML=' ':e.innerText=attrs[k]}
      else e[k]=attrs[k];
    });
    return e;
  }

  
  document.querySelectorAll("select[multiple]").forEach((el,k)=>{
    
    var div=newEl('div',{class:'multiselect-dropdown',style:{width:config.style?.width??el.clientWidth+'px',padding:config.style?.padding??''}});
    el.style.display='none';
    el.parentNode.insertBefore(div,el.nextSibling);
    var listWrap=newEl('div',{class:'multiselect-dropdown-list-wrapper'});
    var list=newEl('div',{class:'multiselect-dropdown-list',style:{height:config.height}});
    var search=newEl('input',{class:['multiselect-dropdown-search'].concat([config.searchInput?.class??'form-control']),style:{width:'100%',display:el.attributes['multiselect-search']?.value==='true'?'block':'none'},placeholder:'search'});
    listWrap.appendChild(search);
    div.appendChild(listWrap);
    listWrap.appendChild(list);

    el.loadOptions=()=>{
      list.innerHTML='';
      
      if(el.attributes['multiselect-select-all']?.value=='true'){
        var op=newEl('div',{class:'multiselect-dropdown-all-selector'})
        var ic=newEl('input',{type:'checkbox'});
        op.appendChild(ic);
        op.appendChild(newEl('label',{text:config.txtAll}));
  
        op.addEventListener('click',()=>{
          op.classList.toggle('checked');
          op.querySelector("input").checked=!op.querySelector("input").checked;
          
          var ch=op.querySelector("input").checked;
          list.querySelectorAll("input").forEach(i=>i.checked=ch);
          Array.from(el.options).map(x=>x.selected=ch);
  
          el.dispatchEvent(new Event('change'));
        });
        ic.addEventListener('click',(ev)=>{
          ic.checked=!ic.checked;
        });
  
        list.appendChild(op);
      }

      Array.from(el.options).map(o=>{
        var op=newEl('div',{class:o.selected?'checked':'',optEl:o})
        var ic=newEl('input',{type:'checkbox',checked:o.selected});
        op.appendChild(ic);
        op.appendChild(newEl('label',{text:o.text}));

        op.addEventListener('click',()=>{
          op.classList.toggle('checked');
          op.querySelector("input").checked=!op.querySelector("input").checked;
          op.optEl.selected=!!!op.optEl.selected;
          el.dispatchEvent(new Event('change'));
        });
        ic.addEventListener('click',(ev)=>{
          ic.checked=!ic.checked;
        });

        list.appendChild(op);
      });
      div.listEl=listWrap;

      div.refresh=()=>{
        div.querySelectorAll('span.optext, span.placeholder').forEach(t=>div.removeChild(t));
        var sels=Array.from(el.selectedOptions);
        if(sels.length>(el.attributes['multiselect-max-items']?.value??5)){
          div.appendChild(newEl('span',{class:['optext','maxselected'],text:sels.length+' '+config.txtSelected}));          
        }
        else{
          sels.map(x=>{
            var c=newEl('span',{class:'optext',text:x.text});
            div.appendChild(c);
          });
        }
        if(0==el.selectedOptions.length) div.appendChild(newEl('span',{class:'placeholder',text:el.attributes['placeholder']?.value??config.placeholder}));
      };
      div.refresh();
    }
    el.loadOptions();
    
    search.addEventListener('input',()=>{
      list.querySelectorAll("div").forEach(d=>{
        var txt=d.querySelector("label").innerText.toUpperCase();
        d.style.display=txt.includes(search.value.toUpperCase())?'block':'none';
      });
    });

    div.addEventListener('click',()=>{
      div.listEl.style.display='block';
      search.focus();
      search.select();
    });
    
    document.addEventListener('click', function(event) {
      if (!div.contains(event.target)) {
        listWrap.style.display='none';
        div.refresh();
      }
    });    
  });
}

window.addEventListener('load',()=>{
  MultiselectDropdown(window.MultiselectDropdownOptions);
});
select {
  width: 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<select multiple multiselect-search="true" multiselect-select-all="true" multiselect-max-items="3" class="w-full mt-8 no-border">
  <optgroup label="Tenants">
    <option>Create Proposal</option>
    <option>Retrieve Customer Data</option>
    <option>Edit Dependant Data</option>
  </optgroup>

  <optgroup label="User">
    <option>User 1</option>
    <option>User 2</option>
    <option>User 3</option>
  </optgroup>
</select>

var style = document.createElement('style');
    style.setAttribute("id", "multiselect_dropdown_styles");
    style.innerHTML = `
    .multiselect-dropdown{
    display: inline-block;
    padding: 2px 5px 0px 5px;
    border-radius: 4px;
    border: solid 1px #ced4da;
    background-color: white;
    position: relative;
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath
    fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6
    6-6'/%3e%3c/svg%3e");
    background-repeat: no-repeat;
    background-position: right .75rem center;
    background-size: 16px 12px;
    }
    .multiselect-dropdown span.optext, .multiselect-dropdown span.placeholder{
    margin-right:0.5em;
    margin-bottom:3px;
    padding:1px 0;
    border-radius: 4px;
    display:inline-block;
    }
    .multiselect-dropdown span.optext{
    background-color:lightgray;
    padding:1px 0.75em;
    }
    .multiselect-dropdown span.placeholder{
    color:#ced4da;
    }
    .multiselect-dropdown-list-wrapper{
    box-shadow: gray 0 3px 8px;
    z-index: 100;
    padding:2px;
    border-radius: 4px;
    border: solid 1px #ced4da;
    display: none;
    margin: -1px;
    position: absolute;
    top:0;
    left: 0;
    right: 0;
    background: white;
    }
    .multiselect-dropdown-list-wrapper .multiselect-dropdown-search{
    margin-bottom:5px;
    }
    .multiselect-dropdown-list{
    padding:2px;
    height: 15rem;
    overflow-y:auto;
    overflow-x: hidden;
    }
    .multiselect-dropdown-list::-webkit-scrollbar {
    width: 6px;
    }
    .multiselect-dropdown-list::-webkit-scrollbar-thumb {
    background-color: #bec4ca;
    border-radius:3px;
    }

    .multiselect-dropdown-list div{
    padding: 5px;
    }
    .multiselect-dropdown-list input{
    height: 1.15em;
    width: 1.15em;
    margin-right: 0.35em;
    }
    .multiselect-dropdown-list div.checked{
    }
    .multiselect-dropdown-list div:hover{
    background-color: #ced4da;
    }
    .multiselect-dropdown span.maxselected {width:100%;}
    .multiselect-dropdown-all-selector {border-bottom:solid 1px #999;}
    `;
    document.head.appendChild(style);

    function MultiselectDropdown(options) {
        var config = {
            search: true,
            height: '15rem',
            placeholder: 'select',
            txtSelected: 'selected',
            txtAll: 'All',
            ...options
        };

        function newEl(tag, attrs) {
            var e = document.createElement(tag);
            if (attrs !== undefined) Object.keys(attrs).forEach(k => {
                if (k === 'class') {
                    Array.isArray(attrs[k]) ? attrs[k].forEach(o => o !== '' ? e.classList.add(o) : 0) :
                        (attrs[k] !== '' ? e.classList.add(attrs[k]) : 0)
                } else if (k === 'style') {
                    Object.keys(attrs[k]).forEach(ks => {
                        e.style[ks] = attrs[k][ks];
                    });
                } else if (k === 'text') {
                    attrs[k] === '' ? e.innerHTML = '&nbsp;' : e.innerText = attrs[k]
                } else e[k] = attrs[k];
            });
            return e;
        }


        document.querySelectorAll("select[multiple]").forEach((el, k) => {
            var dataChecked = [];
            var
                div = newEl('div', {
                    class: 'multiselect-dropdown',
                    style: {width: config.style?.width ?? el.clientWidth + 'px', padding: config.style?.padding ?? ''}
                });
            el.style.display = 'none';
            el.parentNode.insertBefore(div, el.nextSibling);
            var listWrap = newEl('div', {class: 'multiselect-dropdown-list-wrapper'});
            var list = newEl('div', {class: 'multiselect-dropdown-list', style: {height: config.height}});
            var
                search = newEl('input', {
                    class: ['multiselect-dropdown-search'].concat([config.searchInput?.class ?? 'form-control']),
                    style: {
                        width: '100%',
                        display: el.attributes['multiselect-search']?.value === 'true' ? 'block' : 'none'
                    },
                    placeholder: 'search'
                });
            listWrap.appendChild(search);
            div.appendChild(listWrap);
            listWrap.appendChild(list);

            el.loadOptions = () => {
                list.innerHTML = '';

                if (el.attributes['multiselect-select-all']?.value == 'true') {
                    var op = newEl('div', {class: 'multiselect-dropdown-all-selector'})
                    var ic = newEl('input', {type: 'checkbox'});
                    op.appendChild(ic);
                    op.appendChild(newEl('label', {text: config.txtAll}));

                    op.addEventListener('click', () => {
                        op.classList.toggle('checked');
                        op.querySelector("input").checked = !op.querySelector("input").checked;

                        var ch = op.querySelector("input").checked;
                        list.querySelectorAll("input").forEach(i => i.checked = ch);
                        Array.from(el.options).map(x => x.selected = ch);
                        dataChecked = $('#mySelect').val();
                        el.dispatchEvent(new Event('change'));
                    });
                    ic.addEventListener('click', (ev) => {
                        ic.checked = !ic.checked;
                    });
                    list.appendChild(op);
                }

                // my idea is here start
                $.each($(el).children(), function (index, value) {
                    if ($(value).is('optgroup')) {
                        let label = $(`<div class="label-group"><span>${$(value).attr('label')}</span></div>`)

                        $.each(value.children, function (idx, option) {
                            $(label).append(createOption(option))
                        })
                        $(list).append(label)
                    } else {
                        let child = createOption(value)
                        $(list).append(child)
                    }
                })

                if (options) {

                }

                function createOption(element) {
                    let option = $(`<div><label><input class="item" data-option="${$(element).val()}" type="checkbox"> ${$(element).text()}</label></div>`)
                    option.on('click', 'label', function () {
                        let input = $(this).find('input')
                        let optionValue = input.data('option')
                        let isChecked = input.prop('checked')

                        if (isChecked && dataChecked.indexOf(optionValue) === -1) {
                            dataChecked.push(optionValue)
                        } else if (!isChecked && dataChecked.indexOf(optionValue) !== -1) {
                            dataChecked.splice(dataChecked.indexOf(optionValue), 1);
                        }
                        // Check all checkboxes are checked or not?
                        $('.multiselect-dropdown-all-selector').find('input').prop('checked', !$('input.item:checkbox:not(:checked)').length > 0)

                        $('#mySelect').val(dataChecked)
                    });
                    return option
                }

                div.listEl = listWrap;
                div.refresh = () => {
                    div.querySelectorAll('span.optext, span.placeholder').forEach(t => div.removeChild(t));
                    var sels = Array.from(el.selectedOptions);
                    if (sels.length > (el.attributes['multiselect-max-items']?.value ?? 5)) {
                        div.appendChild(newEl('span', {
                            class: ['optext', 'maxselected'],
                            text: sels.length + ' ' + config.txtSelected
                        }));
                    } else {
                        sels.map(x => {
                            var c = newEl('span', {class: 'optext', text: x.text});
                            div.appendChild(c);
                        });
                    }
                    if (0 == el.selectedOptions.length)
                        div.appendChild(newEl('span', {
                            class: 'placeholder',
                            text: el.attributes['placeholder']?.value ?? config.placeholder
                        }));
                };
                div.refresh();
            }
            el.loadOptions();

            search.addEventListener('input', () => {
                list.querySelectorAll("div").forEach(d => {
                    var txt = d.querySelector("label").innerText.toUpperCase();
                    d.style.display = txt.includes(search.value.toUpperCase()) ? 'block' : 'none';
                });
            });

            div.addEventListener('click', () => {
                div.listEl.style.display = 'block';
                search.focus();
                search.select();
            });

            document.addEventListener('click', function (event) {
                if (!div.contains(event.target)) {
                    listWrap.style.display = 'none';
                    div.refresh();
                }
            });
        });
    }

    window.addEventListener('load', () => {
        MultiselectDropdown(window.MultiselectDropdownOptions);
    });
    select {
        width: 100%;
    }
    .label-group span {
        font-weight: bold;
    }

    .label-group label {
        padding-left: 15px;
    }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<select id="mySelect" multiple multiselect-search="true" multiselect-select-all="true" multiselect-max-items="3" class="w-full mt-8 no-border">
<optgroup label="Tenants">
    <option value="Create Proposal">Create Proposal</option>
    <option value="Retrieve Customer Data">Retrieve Customer Data</option>
    <option value="Edit Dependant Data">Edit Dependant Data</option>
</optgroup>
<optgroup label="User">
    <option value="User 1">User 1</option>
    <option value="User 2">User 2</option>
    <option value="User 3">User 3</option>
</optgroup>

<option value="No 1">No 1</option>
<option value="No 2">No 2</option>
</select>