Checkbox (Tri-State) (2024)

Required: The complete JavaScript file (for all patterns in the library): deque-patterns.min.js

Key parts of the JavaScript file

Note: The code below is functional only in the context of the complete JavaScript file.

In the @checkbox section:

 /* TO DO: - Throw an error if the label is missing */ function toggle(element) { if (isToggledOn(element)) { toggleOff(element); } else { toggleOn(element); } } function isToggledOn(element) { return getCheckboxData(element) === 'true'; } function replaceSpace(str) { return str.replace(/ /g, '_').toLowerCase(); } function buildCheckboxTristate() { var _customCheckboxTristateWidgets = document.querySelectorAll('.custom-checkbox-widget'); var _instanceChkTristateCount = 0; if (_customCheckboxTristateWidgets.length > 0) { [].slice.call(_customCheckboxTristateWidgets).forEach(function (_eachCustomWidget) { var _dataConfig = { groupTitle: _eachCustomWidget.getAttribute('data-group-title'), groupOptionTitle: _eachCustomWidget.getAttribute('data-group-option-title'), options: _eachCustomWidget.getAttribute('data-options'), delimiter: _eachCustomWidget.getAttribute('data-delimiter') || ',' }; if (_dataConfig.options) { _dataConfig.options = _dataConfig.options.split(_dataConfig.delimiter); } var _id = 'instance_' + _instanceChkTristateCount + '_' + replaceSpace(_dataConfig.groupTitle); var _elementControl = '<div class="custom-checkbox" id="' + _id + '" role="group" aria-labelledby="group-header' + _id + '">'; _elementControl += '<div class="checkbox-group-heading" id="group-header' + _id + '">' + _dataConfig.groupTitle + '</div>'; _elementControl += '<div role="checkbox" name="parent-checkbox[]" data-childs="' + _id + '_childs" class="parent-checkbox checkbox-holder" \ aria-labelledby="' + _id + replaceSpace(_dataConfig.groupOptionTitle) + '" tabindex="0"> \ <span class="checkbox-indicator"></span> \ <span class="checkbox-label" id="' + _id + replaceSpace(_dataConfig.groupOptionTitle) + '">' + _dataConfig.groupOptionTitle + '</span> \ </div> \ <div class="child-checkbox-list" data-group="' + _id + '">'; for (var _elementOptionIndex in _dataConfig.options) { _elementControl += '<div role="checkbox" name="child-checkbox[]" class="child-checkbox checkbox-holder ' + _id + '_childs" aria-labelledby="' + _id + replaceSpace(_dataConfig.options[_elementOptionIndex]) + '" tabindex="0"> \ <span class="checkbox-indicator"></span> \ <span class="checkbox-label" id="' + _id + replaceSpace(_dataConfig.options[_elementOptionIndex]) + '">' + _dataConfig.options[_elementOptionIndex] + '</span> \ </div>'; } _elementControl += '</div></div>'; _eachCustomWidget.innerHTML = _elementControl; _instanceChkTristateCount++; }); var parentCheckboxElements = document.querySelectorAll('.parent-checkbox'); [].slice.call(parentCheckboxElements).forEach(function (eachParent) { eachParent.addEventListener('click', checkboxEvent); eachParent.addEventListener('keyup', checkboxEvent); }); var childElements = document.querySelectorAll('.child-checkbox'); [].slice.call(childElements).forEach(function (eachChildElement) { eachChildElement.addEventListener('click', checkboxEvent); eachChildElement.addEventListener('keyup', checkboxEvent); }); } } buildCheckboxTristate(); function checkboxEvent(event) { if (event.keyCode == 13 || event.keyCode == 32 || !event.keyCode) { var element = event.currentTarget; if (element.getAttribute('aria-checked') == 'true') { element.setAttribute('aria-checked', 'false'); element.classList.remove('active'); } else { element.setAttribute('aria-checked', 'true'); element.classList.add('active'); } if (element.getAttribute('data-childs')) { var childElements = document.querySelectorAll('.' + element.getAttribute('data-childs')); [].slice.call(childElements).forEach(function (eachChildElement) { if (element.classList.contains('parent-checkbox')) { eachChildElement.setAttribute('aria-checked', element.getAttribute('aria-checked')); } }); } var parentElement = document.querySelector('#' + element.parentElement.getAttribute('data-group')); if (parentElement) { var innerParent = parentElement.querySelector('.parent-checkbox'); if (innerParent) { var checkboxCounter = parentElement.querySelectorAll('.child-checkbox[aria-checked="true"]').length; var _childElements = parentElement.querySelectorAll('.child-checkbox'); if (checkboxCounter == 0) { innerParent.setAttribute('aria-checked', 'false'); } else if (checkboxCounter < _childElements.length) { innerParent.setAttribute('aria-checked', 'mixed'); } else if (checkboxCounter == _childElements.length) { innerParent.setAttribute('aria-checked', 'true'); } } } } } function setCheckboxData(element, value) { element.setAttribute('aria-checked', value); var dataElement = document.getElementById('checkboxTristateData[' + element.getAttribute('aria-labelledby') + ']'); if (dataElement) dataElement.value = value; } function getCheckboxData(element) { var dataElement = document.getElementById('checkboxTristateData[' + element.getAttribute('aria-labelledby') + ']'); return dataElement ? dataElement.value : null; } function toggleOn(element) { setCheckboxData(element, 'true'); } function toggleOff(element) { setCheckboxData(element, 'false'); } function toggleMixed(element) { setCheckboxData(element, 'mixed'); } function createSingleCheckbox(checkbox, isChecked) { var onChange = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : function () { }; checkbox.setAttribute('tabindex', '0'); checkbox.setAttribute('role', 'checkbox'); var indicator = document.createElement('span'); indicator.classList.add('deque-checkbox-indicator'); checkbox.appendChild(indicator); var labelText = checkbox.getAttribute('aria-labelledby'); var label = document.getElementById(labelText); //label.setAttribute('aria-hidden', 'true'); // prevents double readout label.classList.add('deque-checkbox-label'); var hiddenCheckbox = document.createElement('input'); hiddenCheckbox.type = 'hidden'; hiddenCheckbox.name = 'checkboxTristateData[' + labelText + ']'; hiddenCheckbox.id = 'checkboxTristateData[' + labelText + ']'; hiddenCheckbox.classList.add('deque-checkbox-data'); checkbox.appendChild(hiddenCheckbox); /*checkbox.addEventListener('focus', function () { var allCheckboxElements = document.querySelectorAll('.deque-checkbox-tristate-parent'); [].slice.call(allCheckboxElements).forEach(element => { element.setAttribute('aria-hidden', 'true'); }); });*/ if (isChecked) { toggleOn(checkbox); } else { toggleOff(checkbox); } function changeHandler(e) { e.stopPropagation(); e.preventDefault(); toggle(checkbox); broadcastChange(); } function broadcastChange() { onChange({ element: checkbox, isToggledOn: isToggledOn(label) }); } checkbox.parentNode.addEventListener('click', changeHandler); (0, _keyboardUtils.onElementSpace)(checkbox, changeHandler); (0, _keyboardUtils.onElementEnter)(checkbox, changeHandler); checkbox.parentNode.addEventListener('focus', function () { checkbox.classList.add('deque-checkbox-focused'); }); checkbox.parentNode.addEventListener('blur', function () { checkbox.classList.remove('deque-checkbox-focused'); }); return checkbox; } function createSingleCheckboxForRadio(checkbox, checkboxLabel, isChecked) { var onChange = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : function () { }; checkbox.setAttribute('tabindex', '0'); checkbox.setAttribute('role', 'checkbox'); var indicator = document.createElement('span'); indicator.classList.add('deque-checkbox-indicator'); checkbox.appendChild(indicator); var labelText = checkbox.getAttribute('aria-labelledby'); var label = document.getElementById(labelText); //label.setAttribute('aria-hidden', 'true'); // prevents double readout label.classList.add('deque-checkbox-label'); var hiddenRadio = document.createElement('input'); hiddenRadio.type = 'hidden'; hiddenRadio.name = 'checkboxTristateData[' + labelText + ']'; hiddenRadio.id = 'checkboxTristateData[' + labelText + ']'; hiddenRadio.classList.add('deque-checkbox-radio-data'); checkbox.appendChild(hiddenRadio); checkbox.appendChild(label); if (isChecked) { toggleOn(checkbox); } else { toggleOff(checkbox); } function changeHandler(e) { e.stopPropagation(); e.preventDefault(); toggle(checkbox); broadcastChange(); } function broadcastChange() { onChange({ element: checkbox, isToggledOn: isToggledOn(label) }); } checkbox.addEventListener('click', changeHandler); checkbox.addEventListener('keydown', changeHandler); checkboxLabel.addEventListener('click', changeHandler); (0, _keyboardUtils.onElementSpace)(checkbox, changeHandler); checkbox.addEventListener('focus', function () { checkbox.classList.add('deque-radio-focused'); }); checkbox.addEventListener('blur', function () { checkbox.classList.remove('deque-radio-focused'); }); return checkbox; } function createCheckboxGroup(parent, children) { var onChange = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : function () { }; parent = createSingleCheckbox(parent, false, function (e) { onChange(e); rootClicked(getCorrectRootState()); }); children = Array.prototype.slice.call(children); children = children.map(function (child) { return createSingleCheckbox(child, false, function () { if (onChange) { onChange(child); } setCorrectRootState(); }); }); var rootClickHandlers = { 'true': function _true() { children.forEach(toggleOff); toggleOff(parent); }, 'false': function _false() { children.forEach(toggleOn); toggleOn(parent); }, 'mixed': function mixed() { children.forEach(toggleOn); toggleOn(parent); } }; function rootClicked(rootState) { rootClickHandlers[rootState](); } function getCorrectRootState() { if (children.every(isToggledOn)) { return 'true'; } else if (children.every(function (child) { return !isToggledOn(child); })) { return 'false'; } else { return 'mixed'; } } var leafClickHandlers = { 'true': function _true() { return toggleOn(parent); }, 'false': function _false() { return toggleOff(parent); }, 'mixed': function mixed() { return toggleMixed(parent); } }; function setCorrectRootState() { leafClickHandlers[getCorrectRootState()](); } } function activateAllCheckboxes() { var checkboxes = document.querySelectorAll('.deque-checkbox-aria'); for (var i = 0; i < checkboxes.length; i++) { var childNode = checkboxes[i].querySelector('.deque-checkbox-data'); if (!checkboxes[i].contains(childNode)) { createSingleCheckbox(checkboxes[i], false); } } var tristates = document.querySelectorAll('.deque-checkbox-tristate-group'); for (var j = 0; j < tristates.length; j++) { var parentGroup = tristates[j].querySelector('.deque-checkbox-tristate-parent'); var parent = parentGroup.querySelector('.deque-checkbox-tristate'); var childrenGroup = tristates[j].querySelector('.deque-checkbox-tristate-children'); var children = childrenGroup.querySelectorAll('.deque-checkbox-tristate'); childNode = childrenGroup.querySelector('.deque-checkbox-data'); if (!childrenGroup.contains(childNode)) { createCheckboxGroup(parent, children); } } } activateAllCheckboxes(); 

In the @keyboardUtils section:

 var KEYS = exports.KEYS = { BACKSPACE: 8, TAB: 9, ENTER: 13, SHIFT: 16, CTRL: 17, ALT: 18, ESCAPE: 27, SPACE: 32, LEFT: 37, RIGHT: 39, UP: 38, DOWN: 40, F10: 121, HOME: 36, END: 35, PAGE_UP: 33, PAGE_DOWN: 34 }; function bindElementToEventValue(element, eventName, testValue, handler) { function localHandler(e) { if (e.which === testValue) { handler(e); } } element.addEventListener(eventName, localHandler); return function () { element.removeEventListener(eventName, localHandler); }; } function bindElementToKeypressValue(element, testValue, handler) { return bindElementToEventValue(element, 'keypress', testValue, handler); } function bindElementToKeydownValue(element, testValue, handler) { return bindElementToEventValue(element, 'keydown', testValue, handler); } function onElementEnter(element, handler) { return bindElementToKeydownValue(element, KEYS.ENTER, handler); } function onElementEscape(element, handler) { return bindElementToKeydownValue(element, KEYS.ESCAPE, handler); } function onElementSpace(element, handler) { return bindElementToKeypressValue(element, KEYS.SPACE, handler); } function onElementLeft(element, handler) { return bindElementToKeydownValue(element, KEYS.LEFT, handler); } function onElementRight(element, handler) { return bindElementToKeydownValue(element, KEYS.RIGHT, handler); } function onElementUp(element, handler) { return bindElementToKeydownValue(element, KEYS.UP, handler); } function onElementDown(element, handler) { return bindElementToKeydownValue(element, KEYS.DOWN, handler); } function onElementHome(element, handler) { return bindElementToKeydownValue(element, KEYS.HOME, handler); } function onElementEnd(element, handler) { return bindElementToKeydownValue(element, KEYS.END, handler); } function onElementPageUp(element, handler) { return bindElementToKeydownValue(element, KEYS.PAGE_UP, handler); } function onElementPageDown(element, handler) { return bindElementToKeydownValue(element, KEYS.PAGE_DOWN, handler); } function onElementF10(element, handler) { return bindElementToKeydownValue(element, KEYS.F10, handler); } function isAlphaNumeric(charCode) { return charCode >= 48 && charCode <= 90 /* numbers, uppercase letters */ || charCode >= 97 && charCode <= 122 /* lowercase letters */; } function onElementCharacter(element, handler) { function localHandler(e) { var charCode = e.which; if (isAlphaNumeric(charCode)) { handler(e); } } element.addEventListener('keypress', localHandler); return function () { element.removeEventListener('keypress', localHandler); }; } function trapEnter(element) { onElementEnter(element, function (e) { e.stopPropagation(); e.preventDefault(); }); } 

In the @guidUtils section:

 /* note - not a true guid. I prepend 'g' because the ID of an element cannot start with a numeral */ function generateGuid() { var S4 = function S4() { return ((1 + Math.random()) * 0x10000 | 0).toString(16).substring(1); }; return 'g' + (S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4()); } 

In the @containerUtils section:

 function elementIsChildOfElement(child, potentialParent) { while (child) { if (child === potentialParent) { return true; } child = child.parentNode; } return false; } function createFieldset(label) { var fieldset = document.createElement('fieldset'); var legend = document.createElement('legend'); legend.classList.add('legend'); // for easy lookup regardless of mode legend.id = (0, _guidUtils.generateGuid)(); legend.innerText = label; fieldset.appendChild(legend); return fieldset; } function createLiveRegion() { var level = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'polite'; var classes = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; var output = document.createElement('span'); classes.forEach(function (c) { return output.classList.add(c); }); output.id = (0, _guidUtils.generateGuid)(); output.setAttribute('aria-live', level); output.classList.add('deque-visuallyhidden'); output.innerText = ''; output.notify = function (text) { // TODO: Clean this up...no need to extend the element prototype while (output.firstChild) { output.removeChild(output.firstChild); } var msg = document.createElement('div'); msg.innerHTML = text; output.appendChild(msg); }; return output; } 

Note: No additional JavaScript initialization code is necessary for this pattern. All elements with class="deque-checkbox-tristate-group" will be initialized automatically by the external JavaScript file.

Checkbox (Tri-State) (2024)

FAQs

Can a checkbox have 3 states? ›

Description. The indeterminate property sets or returns whether the state of a checkbox has changed. Checkboxes actually has three states: true, false and indeterminate which indicates that a checkbox is neither "on" or "off".

What is a tri-state checkbox? ›

A tri-state checkbox can be checked, not checked, or partially checked. The condition of being partially checked is based on the selection of child elements. If all child elements are selected, the parent checkbox is checked. If some child elements are selected, the parent checkbox is partially checked.

What are the three state checkboxes in UX? ›

Multiple checkboxes: This type of checkbox allows the user to select multiple options from a list by selecting multiple checkboxes. Tri-state checkbox: This type of checkbox has three states: selected, unselected, and indeterminate.

How do I manage the state of a checkbox? ›

To manage the checked state of the Checkbox:
  1. Use its checked property.
  2. Handle the onChange event.
  3. Pass the new value through the props.

What is the most number of states a checkbox? ›

The most number of states a checkbox can have is 3.

It is one of the fundamentals of Visual Basic. It is programming software developed by Microsoft that was released in 1991 for Component object modeling which was declared as a legacy in 2008.

Does checkbox allow multiple selection? ›

Radio Buttons and Select fields only allow the users to choose one item, while Checkboxes and Multiselects allow multiple items to be chosen from the list.

How many states does a checkbox have? ›

A check box is used to select or deselect action items. It can be used for a single item or for a list of multiple items that a user can choose from. The control has three selection states: unselected, selected, and indeterminate.

What is the code for a checkbox? ›

The <input type="checkbox"> defines a checkbox. The checkbox is shown as a square box that is ticked (checked) when activated.

How to check checkbox state? ›

You can use isChecked() to query whether or not a checkbox is checked. In addition to the usual checked and unchecked states, QCheckBox optionally provides a third state to indicate "no change". This is useful whenever you need to give the user the option of neither checking nor unchecking a checkbox.

What is the rule of 3 in UX design? ›

At its core, the Rule of Thirds is a compositional principle that helps UX/UI designers create balanced and visually engaging layouts. It involves dividing your design area into a 3×3 grid, creating nine equally sized quadrants. The main idea is to position key elements along these gridlines or at their intersections.

What is the best practice for checkbox? ›

Standard Checkbox

Design Tips: Ensure adequate spacing between options to avoid accidental selection. Provide visual cues, such as color changes or checkmarks, to indicate the selected state. Make the selected Checkbox easily distinguishable from unselected options.

What are the different states of checkbox? ›

The Essential JS 2 CheckBox contains 3 different states visually, they are:
  • Checked.
  • Unchecked.
  • Indeterminate.

Which method do you use to change the state of checkbox? ›

onChecked will be used to change the state , It is a boolean .

What is the focus state of a checkbox? ›

Focus state

Indicates that the focus in the document is currently on the checkbox. For the sake of aesthetics, it can be tempting to remove default browser focus styles with outline: 0; Do not do this before adding your own custom focus styles!

How to save state of checkbox in JavaScript? ›

  1. <ul class="list-group">
  2. <li class="list-group-item"><input type="checkbox" class="save-cb-state" name="mycheckbox" value="yes"> Must I save my state?</ ...
  3. <li class="list-group-item"><input type="checkbox" name="anothercheckbox" value="1"> Try and save this</li>

How many values a checkbox can have options? ›

Checkbox is used to make a choice between two options. Input from each checkbox is made to a single variable. A checkbox can pass one of the two values to this variable - one value for the checked state and another one for the unchecked state.

What are the states of checkboxes? ›

Use the indeterminate state
StatePropertyValue
checkedIsCheckedtrue
uncheckedIsCheckedfalse
indeterminateIsCheckednull
Oct 24, 2022

What are the 2 states of a check box? ›

One difference between the two is that a checkbox has 3 states (checked, unchecked, and indeterminate) while a switch has 2 states (on and off.) When using them, we were told that the main difference is action: is there an action associated to the state change? Then it is a switch, otherwise use a checkbox.

Top Articles
Latest Posts
Article information

Author: Horacio Brakus JD

Last Updated:

Views: 6030

Rating: 4 / 5 (51 voted)

Reviews: 82% of readers found this page helpful

Author information

Name: Horacio Brakus JD

Birthday: 1999-08-21

Address: Apt. 524 43384 Minnie Prairie, South Edda, MA 62804

Phone: +5931039998219

Job: Sales Strategist

Hobby: Sculling, Kitesurfing, Orienteering, Painting, Computer programming, Creative writing, Scuba diving

Introduction: My name is Horacio Brakus JD, I am a lively, splendid, jolly, vivacious, vast, cheerful, agreeable person who loves writing and wants to share my knowledge and understanding with you.