Tri-State Check Box in HTML

Checkbox with three states

Thanks to @Hagner, the solution was to create a control that toggles between 3 different states on a click event.

HTML:

<div class="switch switch-group" [ngClass]="getClasses()"  appAttribute [states]="states" (click)="changeState()" [attr.disabled]="(disabled == 'disabled') ? 'disabled' : null" >
<input #inputSwitch type="checkbox" appAttribute [states]="states" class="switch-checkbox">
<span class="switch-warper">
</span>
<label class="switch-label">Example</label>
</div>

TS:

  changeState() {
if(this.states == 2) {
if(this.inputSwitch.nativeElement.checked) {
this.inputSwitch.nativeElement.checked = false;
} else {
this.inputSwitch.nativeElement.checked = true;
}
}
else if (this.states == 3) {

switch(this.inputSwitch.nativeElement.checked)
{
case false:
if(!this.inputSwitch.nativeElement.indeterminate) {
this.inputSwitch.nativeElement.checked = true;
break;
}
else if(this.inputSwitch.nativeElement.indeterminate) {
this.inputSwitch.nativeElement.checked = false;
this.inputSwitch.nativeElement.indeterminate = false;
break;
}
case true:
this.inputSwitch.nativeElement.checked = false;
this.inputSwitch.nativeElement.indeterminate = true;
break;
}

}

}

SASS:

input[type=checkbox].switch-checkbox {
display: none;
}

Check box with 3 states

The regular checkbox is not capable of doing it. You need to use a flag for this. What I would do is to use a counter flag, that is tied to the element:

$(function() {  $(".check").data("state", 0).addClass("unchecked");  $(".multi-checkbox").click(function() {    var states = ["unchecked", "partial", "checked"];    var curState = $(this).find(".check").data("state");    curState++;    $(this).find(".check").removeClass("unchecked partial checked").addClass(states[curState % states.length]).data("state", curState % states.length);  });});
.multi-checkbox .check {  display: inline-block;  vertical-align: top;  width: 20px;  height: 20px;  border: 1px solid #333;  margin: 3px;  text-align: center;  vertical-align: middle;  cursor: pointer;}
.multi-checkbox .check.unchecked i { display: none;}
.multi-checkbox .check.partial i.fa-link { display: none;}
.multi-checkbox .check.checked i.fa-chain-broken { display: none;}
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" /><script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><div class="multi-checkbox">  <span class="check">    <i class="fa fa-chain-broken"></i>    <i class="fa fa-link"></i>  </span> Check me</div>

Sending Values of 3 State Checkbox

If you're doing this in a classic form and always want to receive a value, a fairly standard approach is not to send the checkbox's value at all; instead, have a hidden form field with the value you want to send.

<input type="checkbox" id="LEVL1LES1" onclick="changeBoxValues(this)">
<input type="hidden" name="LEVL1LES1" value='<?php echo $row["LEVL1LES1"]?>'>

Notice that the checkbox has no name, and so will not be included in the form when submitted. Also note that the hidden field has a name which matches the id of the checkbox. I use that in changeBoxValues below to relate the two fields, but it's just one way to do it; another would be a data-* attribute or even just cb.nextElementSibling and ensuring that the hidden field is always the next element after the checkbox element.

Then in changeBoxValues:

function changeBoxValues(cb) {
var hidden = document.querySelector('[name="' + cb.id + '"]');
if (hidden.value == 0)
{
hidden.value = 1;
}
else if (hidden.value == 1)
{
hidden.value = 2;
}
else
{
counter = 0; // ?? Where did counter come from??
hidden.value = counter;
}
setCheckBoxes(cb, hidden.value);
}

...and setCheckBoxes uses the value passed in rather than cb.value to determine which state to set:

function setCheckBoxes(cb, value) {
if (value == 0)
{
cb.checked = false;
}
else if (value == 1)
{
cb.checked = true;
}
else
{
cb.indeterminate=true;
}
}

It's worth noting that both your original approach and the above rely on JavaScript, so you'll need to require that JavaScript be enabled on the page.

3 state checkbox required=true but checked and unchecked are valid. Intermediate should be invalid

About indeterminate, here's a brief note from CSS Tricks:

The indeterminate state is visual only. The checkbox is still either
checked or unchecked as a state. That means the visual indeterminate
state masks the real value of the checkbox, so that better make sense
in your UI! Like the checkboxes themselves, indeterminate state looks
different in different browsers.

I don't think there's any browser supported validation for indeterminate. Since you're adding it yourself through jQuery, why not validate it as well?

Remove required attribute from your HTML and replace the code when effectiveness is 2 with:

$("#effectiveness").prop("indeterminate", true).prop("checked", false).attr("required", "true").on('change', function() {
$(this).removeAttr('required');
});

Above code will effectively cause the browser to consider it invalid.

How to create 3 state checkbox button with bootstrap 4

I figured out a way of having as many states as wanted to be toggled with one button while the button indicates the state. This is what I was looking for - most of the code is proudly found and put together from different parts of the internet!

If you have a suggestion how to make this even shorter AND easier to read, please comment or directly edit it :)

var $radios = $('input[type="radio"][name="high"]');
$('#result').html($radios.filter(':checked').val());

$('#toggler').click(function() {
var colorClasses = ["btn-danger", "btn-success", "btn-secondary"];
var $checked = $radios.filter(':checked');
var $next = $radios.eq($radios.index($checked) + 1);
if (!$next.length) {
$next = $radios.first();
}
$next.prop("checked", true);
var newValue = $radios.filter(':checked').val();
$(this)
.removeClass(colorClasses.join(" "))
.addClass(colorClasses[newValue]);
$('#result').html(newValue);
});
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" integrity="sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB" crossorigin="anonymous">

<div class="btn-group-toggle" data-toggle="buttons">
<input type="radio" name="high" value="2" checked hidden>
<input type="radio" name="high" value="1" hidden>
<input type="radio" name="high" value="0" hidden>
<button type="button" id="toggler" class="btn btn-secondary">High</button>
</div>
Result:
<div id='result'></div>

<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js" integrity="sha384-smHYKdLADwkXOn1EmN1qk/HfnUcbVRZyYmZ4qpPea6sjB/pTJ0euyQp0Mk8ck+5T" crossorigin="anonymous"></script>

Three state checkbox with angular 2

The indeterminate state of the checkbox can be set with property binding. Here is a simplified version of your markup, showing the indeterminate state binding:

<input type="checkbox" [indeterminate]="milestone?.status === 'crossed'" 
[ngModel]="milestone?.status === 'checked'" (ngModelChange)="checkMilestone(milestone)">

The checkMilestone method could be something like this:

checkMilestone(milestone) {
switch (milestone.status) {
case "checked": {
milestone.status = "unchecked";
break;
}
case "unchecked": {
milestone.status = "crossed";
break;
}
case "crossed": {
milestone.status = "checked";
break;
}
}
}

See this stackblitz for a demo.

React tri-state checkbox indeterminate null error

As I commented, you need to encapsulate the indeterminate logic in a separate component to be able to update the dom when value changes via useEffect as explained here

This is the component for a single indeterminate checkbox

IndeterminateInput.js

import React, { useCallback, useEffect, useRef } from 'react';

export default function IndeterminateInput({ id, name, value, onChange }) {
const el = useRef(null);

const onChangeHandler = useCallback(
event => {
onChange({ id, name, value: (value + 1) % 3 });
},
[id, name, value, onChange],
);

useEffect(() => {
el.current.checked = value === 1;
el.current.indeterminate = value === 2;
}, [value]);

return (
<input type="checkbox" name={name} ref={el} onChange={onChangeHandler} />
);
}

And then you render one IndeterminateInput for each item in your main component passing the necessary props

export default function App() {
const [state, setState] = ...

const onChange = useCallback((item) => {
const { id, value } = item;
setState((prevState) => {
return {
data: prevState.data.map((datum) => {
return id === datum.id
? {
...datum,
one: value
}
: datum;
})
};
});
}, []);

return (
<>
{state.data.map((item, i) => {
return (
<IndeterminateInput
key={item.id}
id={item.id}
name={item.name}
value={item.one}
onChange={onChange}
/>
);
})}
</>
);
}

you can see it working in here: https://codesandbox.io/s/stoic-euler-e97so?file=/src/App.js



Related Topics



Leave a reply



Submit