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
Allow 2 Decimal Places in ≪Input Type="Number"≫
Flexbox Not Giving Equal Width to Elements
How to *Really* Justify a Horizontal Menu in Html+Css
Is Type="Text/Css" Necessary in a ≪Link≫ Tag
Why Should I Use 'Li' Instead of 'Div'
Common Header/Footer With Static Html
Stretch Text to Fit Width of Div
Float Does Not Work in a Flex Container
Css ''Background-Color" Attribute Not Working on Checkbox Inside ≪Div≫
How to Style Part of an Input Field'S Value
How to Update Non-Jsf Components (Plain Html) With Jsf Ajax
Text-Overflow Is Not Working When Using Display:Flex
Full Width Backgrounds on Outlook '07/'10/'13