javascripttom-select

Tom Select - Preventing Selection of Child Options When Parent is Selected


I'm using Tom Select with a multi-select dropdown for a folder structure where options are grouped by parent folders. For example, if I select a top-level folder like app, I want any child options like app/Helpers or app/Http to automatically be disabled, preventing them from being selected.

Here's the current setup:

HTML Structure:

<select id="myFolders" name="folders[]" multiple autocomplete="off">
    <optgroup label="app">
        <option value="app" selected>app</option>
        <option value="app/Helpers">app/Helpers</option>
        <option value="app/Http">app/Http</option>
    </optgroup>
</select>

Javascript I tried.

const tomSelect = new TomSelect('#myFolders', {
    plugins: {
        'checkbox_options': {
            'checkedClassNames':   ['ts-checked'],
            'uncheckedClassNames': ['ts-unchecked'],
            'className': 'form-check-input'
        },
        'remove_button': {
            title:'Remove this item',
        }
    },
    onChange: function () {
        updateDisabledOptions(this);
    }
});

function updateDisabledOptions(tomSelectInstance) {
    const selectedValues = tomSelectInstance.getValue();
    const selectedGroups = new Set(selectedValues);

    Object.values(tomSelectInstance.options).forEach(option => {
        const optionElement = document.querySelector(`.ts-dropdown [data-value="${option.value}"]`);
        
        if (optionElement) {
            const checkbox = optionElement.querySelector('.form-check-input');

            if (checkbox) {
                const isChildOfSelectedGroup = Array.from(selectedGroups).some(
                    group => option.value.startsWith(group + '/')
                );

                if (isChildOfSelectedGroup) {
                    optionElement.classList.add('disabled');
                    checkbox.setAttribute('disabled', true);
                } else {
                    optionElement.classList.remove('disabled');
                    checkbox.removeAttribute('disabled');
                }
            }
        }
    });
}

Problem: The function is adding the disabled attribute to child checkboxes, but it doesn’t prevent them from being selected. Even with aria-disabled and disabled attributes set, the checkboxes are still selectable.

Expected Outcome: If a parent folder like app is selected, any child folders (e.g., app/Helpers) should be automatically disabled and not selectable.

Questions:

Any suggestions or code modifications are welcome! Thank you in advance.

The jsFiddle link Here

(No Jquery)


Solution

  • thanks, @La Ngoc, I changed the function updateDisbaledOptions when the child selected and later user select the parent the selected child will be auto removed. here is the final function.. maybe helpful for others.

     const updateDisabledOptions = (tomSelectInstance) => {
                const selectedValues = tomSelectInstance.getValue();
                const parents = [...Object.values(tomSelectInstance.optgroups)].map(o => o.label);
    
                parents.forEach(parent => {
                    const options = [...Object.values(tomSelectInstance.options)];
                    const children = options.filter(option => option.value.startsWith(`${parent}/`));
    
                    children.forEach(child => {
                        if (selectedValues.includes(parent)) {
                            tomSelectInstance.removeItem(child.value);
                            tomSelectInstance.updateOption(child.value, {
                                value: child.value,
                                text: child.text,
                                disabled: true,
                            });
                        } else {
                            tomSelectInstance.updateOption(child.value, {
                                value: child.value,
                                text: child.text,
                                disabled: false,
                            });
                        }
                    });
                });
    };