I have been looking around for a 3 state toggle switch but haven’t had much luck.
Basically I need a switch that has the states:
| ON | N/A | OFF |
The slider by default starts in the middle, and once the user slides to left or right, they can’t go back to the N/A (not answered) state.
Anyone have any idea on how to handle this?
3
Try something like this:
.switch-toggle {
width: 10em;
}
.switch-toggle label:not(.disabled) {
cursor: pointer;
}
<link href="https://cdn.jsdelivr.net/css-toggle-switch/latest/toggle-switch.css" rel="stylesheet" />
<div class="switch-toggle switch-3 switch-candy">
<input id="on" name="state-d" type="radio" checked="" />
<label for="on" onclick="">ON</label>
<input id="na" name="state-d" type="radio" disabled checked="checked" />
<label for="na" class="disabled" onclick=""> </label>
<input id="off" name="state-d" type="radio" />
<label for="off" onclick="">OFF</label>
<a></a>
</div>
This will start with N/A
as the default option (via checked="checked"
), but make it unselectable later (by using disabled
)
JSFiddle Demo (Simplified)
12
.switch-toggle {
float: left;
background: #242729;
}
.switch-toggle input {
position: absolute;
opacity: 0;
}
.switch-toggle input + label {
padding: 7px;
float:left;
color: #fff;
cursor: pointer;
}
.switch-toggle input:checked + label {
background: green;
}
<div class="switch-toggle switch-3 switch-candy">
<input id="on" name="state-d" type="radio" checked="" />
<label for="on" onclick="">ON</label>
<input id="na" name="state-d" type="radio" checked="checked" />
<label for="na" class="disabled" onclick="">N/A</label>
<input id="off" name="state-d" type="radio" />
<label for="off" onclick="">OFF</label>
</div>
2
If you’d like a No / Unset / Yes toggle with coloring, something like this:
Then use the answer from DarkAjax above, but add the following CSS:
.switch-toggle input:checked.toggle-no ~ a {
background-color: red;
}
.switch-toggle input:checked.toggle-yes ~ a {
background-color: green;
}
.switch-toggle input:checked.toggle-unset ~ a {
background-color: grey;
}
And add the respective classes (class=”toggle-no” , etc.) to the respective radio button inputs. And you can also add icons and such if you’d like.
Hopefully this helps someone!
As a jQuery plugin
function filterme(value) {
value = parseInt(value, 10); // Convert to an integer
if (value === 1) {
$('#RangeFilter').removeClass('rangeAll', 'rangePassive').addClass('rangeActive');
$('span').text('Active');
} else if (value === 2) {
$('#RangeFilter').removeClass('rangeActive', 'rangePassive').addClass('rangeAll');
$('span').text('All');
} else if (value === 3) {
$('#RangeFilter').removeClass('rangeAll', 'rangeActive').addClass('rangePassive');
$('span').text('Passive');
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p class="range-field" style=" width:60px">
<input type="range" id="RangeFilter" name="points" onchange="filterme(this.value);" min="1" class="rangeAll" max="3" value="2">
</p>
<span>All</span>
(function($) {
$.fn.removeClasses = function(classes) {
return this.removeClass(classes.join(' '));
};
$.fn.switchify = function(config) {
config = config || {};
var prefix = config.prefix || 'range-';
var onCls = prefix + (config.onCls || 'on' );
var offCls = prefix + (config.offCls || 'off' );
var unsetCls = prefix + (config.unsetCls || 'unset');
var $self = this;
return this.on('change', function(e) {
var value = parseInt(this.value, 10);
switch (value) {
case 1 : return $self.removeClasses([unsetCls, offCls]).addClass(onCls);
case 2 : return $self.removeClasses([onCls, offCls]).addClass(unsetCls);
case 3 : return $self.removeClasses([onCls, unsetCls]).addClass(offCls);
default : return $self;
}
});
};
})(jQuery);
$('#range-filter').switchify({
onCls : 'active',
offCls : 'passive',
unsetCls : 'all'
}).on('change', function(e) {
var $self = $(this);
if ($self.hasClass('range-active')) $('span').text('Active');
else if ($self.hasClass('range-passive')) $('span').text('Passive');
else if ($self.hasClass('range-all')) $('span').text('All');
else $('span').text('Error!');
});
.range-field { width: 60px; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p class="range-field">
<input type="range" id="range-filter" name="points" min="1" class="rangeAll" max="3" value="2">
</p>
<span>All</span>
2
$(document).ready(function(){
$("#toggle-button1").addClass("active");
$(".tri-state-toggle-button").click(function(){
$(".tri-state-toggle-button").removeClass("active");
var id = $(this).attr('id');
$("#" + id).addClass("active");
});
});
body {
margin-left: 100px;
margin-top: 100px;
}
.tri-state-toggle {
background: rgba(0,0,0,0.8);
box-shadow: inset 0 2px 8px 0 rgba(165,170,174,0.25);
border-radius: 24px;
display: inline-block;
overflow: hidden;
display: inline-flex;
flex-direction: row;
transition: all 500ms ease;
}
.tri-state-toggle-button {
border-radius: 22px;
height: 44px;
display: flex;
align-items: center;
justify-content: center;
width: 64px;
background-color: transparent;
border: 0px solid transparent;
margin: 2px;
color: white;
cursor: pointer;
/* -webkit-transition: all 0.5s ease-in-out;
-moz-transition: all 0.5s ease-in-out;
-ms-transition: all 0.5s ease-in-out;
-o-transition: all 0.5s ease-in-out; */
transition: all 0.5s ease;
}
.tri-state-toggle-button.active {
background-image: linear-gradient(
90deg,
rgba(229, 3, 87, 1) 0%,
rgba(229, 3, 56, 1) 35%,
rgba(219, 101, 17, 1) 100%
);
border: 0px solid rgba(207,207,207,0.6);
box-shadow: 0 8px 16px 0 rgba(0,0,0,0.1);
color: white;
font-weight: 500;
transition: all .5s ease-in;
}
.tri-state-toggle-button:focus {
outline: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h3>Select any one option:</h3>
<div class="tri-state-toggle">
<button class="tri-state-toggle-button" id="toggle-button1">
Song
</button>
<button class="tri-state-toggle-button" id="toggle-button2">
Video
</button>
<button class="tri-state-toggle-button" id="toggle-button3">
Party
</button>
</div>
Additionally to gat’s reply, it’s possible to model this as a grouped radio button through Bootstrap:
<div class="btn-group" data-toggle="buttons">
<label class="btn btn-primary">
<input type="radio" name="options" id="On" />ON</label>
<label class="btn btn-primary">
<input type="radio" name="options" id="NA" />N/A</label>
<label class="btn btn-primary">
<input type="radio" name="options" id="Off" />OFF</label>
</div>
JSFiddle: http://jsfiddle.net/p7DGe/1/
I created a JSFiddle that demonstrates a fully functional three state switch. Please note that the javascript window in JSfiddle was not working properly so the script is loaded in the html window.
function togglebutton(range) {
var val = range.value;
if (val == 1) {
//change color of slider background
range.className = "rangeFalse";
//alter text
$('.toggle-false-msg').attr('id', 'textActive');
$('.toggle-neutral-msg').attr('id', '');
$('.toggle-true-msg').attr('id', '');
} else if (val == 2) {
//change color of slider background
range.className = "rangeNeutral";
//alter text
$('.toggle-false-msg').attr('id', '');
$('.toggle-neutral-msg').attr('id', 'textActive');
$('.toggle-true-msg').attr('id', '');
} else if (val == 3) {
//change color of slider background
range.className = "rangeTrue";
//alter text
$('.toggle-false-msg').attr('id', '');
$('.toggle-neutral-msg').attr('id', '');
$('.toggle-true-msg').attr('id', 'textActive');
}
}
.test_div {
height: 50px;
width: 50px;
background: #204d75 !important;
top: 100px;
position: relative;
display: block;
}
.toggle-container {
position: relative;
width: 8em;
margin: 1em;
padding: 0.25em;
border: thin solid lightgrey;
text-align: center;
}
.range-field {
display: inline-block;
width: 100px;
margin: 0px;
border-radius: 2px;
}
input[type=range] {
-webkit-appearance: none;
margin: 0;
width: 100%;
padding: 0px;
outline: none;
border: none;
}
.toggle-false-msg {
display: none;
opacity: .2;
transition: .5s opacity;
display: inline-block;
position: relative;
top: -8px;
}
.toggle-true-msg {
display: none;
opacity: .2;
transition: .5s opacity;
display: inline-block;
position: relative;
top: -8px;
}
.toggle-neutral-msg {
display: none;
opacity: .2;
transition: .5s opacity;
display: none;
position: relative;
top: -8px;
}
#rangeActive {
background-color: blue;
}
#textActive {
opacity: 1;
color: black;
}
input[type=range]:focus {
outline: none;
}
input[type=range]::-webkit-slider-runnable-track {
width: 100%;
height: 30px;
cursor: pointer;
animate: 0.2s;
box-shadow: 0px 0px 0px #000000;
background: #3071A9;
border-radius: 0px;
border: 0px solid #000000;
}
input[type=range]::-webkit-slider-thumb {
box-shadow: 0px 0px 0px #000000;
border: 0px solid #000000;
height: 30px;
width: 19px;
border-radius: 0px;
background: #FFFFFF;
cursor: pointer;
-webkit-appearance: none;
margin-top: 0px;
}
input[type=range]:focus::-webkit-slider-runnable-track {
background: #3071A9;
}
input[type=range]::-moz-range-track {
width: 100%;
height: 30px;
cursor: pointer;
animate: 0.2s;
box-shadow: 0px 0px 0px #000000;
background: #3071A9;
border-radius: 0px;
border: 0px solid #000000;
}
input[type=range]::-moz-range-thumb {
box-shadow: 0px 0px 0px #000000;
border: 0px solid #000000;
height: 30px;
width: 19px;
border-radius: 0px;
background: #FFFFFF;
cursor: pointer;
}
input[type=range]::-ms-track {
width: 100%;
height: 30px;
cursor: pointer;
animate: 0.2s;
background: transparent;
border-color: transparent;
color: transparent;
}
input[type=range]::-ms-fill-lower {
background: #3071A9;
border: 0px solid #000000;
border-radius: 0px;
box-shadow: 0px 0px 0px #000000;
}
input[type=range]::-ms-fill-upper {
background: #3071A9;
border: 0px solid #000000;
border-radius: 0px;
box-shadow: 0px 0px 0px #000000;
}
input[type=range]::-ms-thumb {
box-shadow: 0px 0px 0px #000000;
border: 0px solid #000000;
height: 30px;
width: 19px;
border-radius: 0px;
background: #FFFFFF;
cursor: pointer;
}
input[type=range]:focus::-ms-fill-lower {
background: #3071A9;
}
input[type=range]:focus::-ms-fill-upper {
background: #3071A9;
}
.rangeFalse::-webkit-slider-runnable-track {
background: #5d0a0a !important;
}
.rangeFalse::-webkit-slider-thumb {
background: white !important;
}
.rangeNeutral::-webkit-slider-runnable-track {
background: #204d75 !important;
}
.rangeNeutral::-webkit-slider-thumb {
background: white !important;
}
.rangeTrue::-webkit-slider-runnable-track {
background: #0e4e1f !important;
}
.rangeTrue::-webkit-slider-thumb {
background: white !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="toggle-container">
<div class="toggle-false-msg">Off</div>
<div class="range-field" style=" width:60px">
<input type="range" name="points" min="1" class="" max="3" value="2"
onchange="togglebutton(this);">
</div>
<div class="toggle-neutral-msg">Neutral</div>
<div class="toggle-true-msg">On</div>
</div>
Instead of using radio buttons this switch utilizes a range and passes the value into JavaScript to determine the actions of the switch. The biggest hurdle in designing this was changing the pseudo element of the range (specifically the background color of its track). This can be achieved by setting different classes with pseduo elements and using java to rotate through the classes.
Information on modifying pseudo elements can be found in the link below. I used method 1 described in the article.
Resources
- http://pankajparashar.com/posts/modify-pseudo-elements-css/
- https://jsfiddle.net/buzzyearlight/u7sg8oLa/
I reworked a bit @mustafa bagwala answer, so here is the same example with three state button, possibility to set between two but get between three and different colors.
And the output can be something like this:
Here are the CSS code and the HTML code:
.switch-toggle input {
opacity: 0;
height: 25px;
position: absolute;
border-radius: 15px;
}
.switch-toggle input + label {
float: left;
color: #fff;
height: 25px;
font-size: 12px;
cursor: pointer;
}
.switch-toggle input[value="na"] + label {
pointer-events: none;
opacity: 0.5;
}
.switch-toggle input[value="na"]:checked + label {
background: grey;
}
.switch-toggle input[value="on"]:checked + label {
background: green;
}
.switch-toggle input[value="off"]:checked + label {
background: red
}
<link href="https://cdn.jsdelivr.net/css-toggle-switch/latest/toggle-switch.css" rel="stylesheet" />
<div class="switch-toggle switch-3 switch-candy">
<input id="on" name="state-d" type="radio" value="on" />
<label for="on">ON</label>
<input id="na" name="state-d" type="radio" value="na" checked="checked" />
<label for="na">N/A</label>
<input id="off" name="state-d" type="radio" value="off" />
<label for="off">OFF</label>
<a></a>
</div>
This is a 3 way React switch with forward, reverse, an unlocked track, and keyboard controls and you can see it in action at CodePen here. It starts at the left position, called Option 1, but you can modify it to start wherever you’d like.
/*React Component*/
class ThreeWayToggleSwitchComponent extends React.Component {
constructor(props) {
super(props);
this.threeWayToggleSwitchComponentDivRef = React.createRef();
this.option1Ref = React.createRef();
this.option3Ref = React.createRef();
this.slidingButtonDivRef = React.createRef();
this.state = {
selectedOption: "Option 1"
};
this.handleOptionChange = this.handleOptionChange.bind(this);
this.keyboardPress = this.keyboardPress.bind(this);
}
handleOptionChange(event) {
if (event.target.value === "Option 1") {
if (this.state.selectedOption === "Option 2") {
this.setState({
selectedOption: event.target.value
});
this.slidingButtonDivRef.current.style.transform = "translate(0px)";
this.slidingButtonDivRef.current.style.background = "green";
}
else if (this.state.selectedOption === "Option 3") {
this.setState({
selectedOption: "Option 2"
});
this.slidingButtonDivRef.current.style.transform = "translate(40px)";
this.slidingButtonDivRef.current.style.background = "yellow";
}
}
else if (event.target.value === "Option 2") {
this.setState({
selectedOption: "Option 2"
});
this.slidingButtonDivRef.current.style.transform = "translate(40px)";
this.slidingButtonDivRef.current.style.background = "yellow";
}
else if (event.target.value === "Option 3") {
if (this.state.selectedOption === "Option 1") {
this.setState({
selectedOption: "Option 2"
});
this.slidingButtonDivRef.current.style.transform = "translate(40px)";
this.slidingButtonDivRef.current.style.background = "yellow";
}
else if (this.state.selectedOption === "Option 2") {
this.setState({
selectedOption: "Option 3"
});
this.slidingButtonDivRef.current.style.transform = "translate(80px)";
this.slidingButtonDivRef.current.style.background = "red";
}
}
}
keyboardPress(event) {
if (event.keyCode === 37) {
this.option1Ref.current.click();
}
else if (event.keyCode === 39) {
this.option3Ref.current.click();
}
}
componentDidMount() {
this.threeWayToggleSwitchComponentDivRef.current.focus();
}
render() {
return(
<div ref={this.threeWayToggleSwitchComponentDivRef} id="three-way-toggle-switch-component-div" tabIndex="0" onKeyDown={this.keyboardPress}>
<div id="radio-buttons-and-sliding-button-container-div">
<div id="radio-buttons-div">
<label id="option-1-label" className="single-option-label" for="option-1">Label 1
<input type="radio" ref={this.option1Ref} id="option-1" className="radio-input-class" name="radio-input" value="Option 1" checked={this.state.selectedOption === "Option 1"} onChange={this.handleOptionChange}/>
</label>
<label id="option-2-label" className="single-option-label" for="option-2">Label 2
<input type="radio" id="option-2" className="radio-input-class" name="radio-input" value="Option 2" checked={this.state.selectedOption === "Option 2"} onChange={this.handleOptionChange}/>
</label>
<label id="option-3-label" className="single-option-label" for="option-3">Label 3
<input type="radio" ref={this.option3Ref} id="option-3" className="radio-input-class" name="radio-input" value="Option 3" checked={this.state.selectedOption === "Option 3"} onChange={this.handleOptionChange}/>
</label>
</div>
<div ref={this.slidingButtonDivRef} id="sliding-button-div">
</div>
<div>
<div id="selected-option">Option Selected: {this.state.selectedOption}
</div>
<div id="keyboard-message">
You can control the sliding button via the left arrow key or the right arrow key on your keyboard.
</div>
</div>
);
}
};
ReactDOM.render(<ThreeWayToggleSwitchComponent/>, document.getElementById("react-component-div"));
/*CSS Styling*/
#three-way-toggle-switch-component-div:focus {
outline: none;
}
#radio-buttons-and-sliding-button-container-div {
position: relative;
display: inline-block;
height: 30px;
width: 120px;
border: 2px solid black;
border-radius: 5px;
}
#sliding-button-div {
position: absolute;
display: inline-block;
top: 0;
height: 30px;
width: 40px;
background: green;
transition: 0.8s;
border-radius: 3px;
}
#radio-buttons-div {
display: flex;
justify-content: space-between;
}
.single-option-label {
width: 100%;
color: transparent;
}
.radio-input-class {
display: none;
}
With Bootstrap 5 and JQuery :
$(document).on('click', '.switch-3', function(event) {
/* get switch slider */
let slider = $(this).find('.switch-3-slider');
/* get switch height */
let height = $(this).height();
/* get coord of click */
let clickPos = event.pageY - $(this).offset().top;
/* get 1/3 of switch height */
let heightThird1 = height / 3;
let heightThird2 = heightThird1 + height / 3;
if (clickPos > heightThird2) {
$(this).removeClass('switch-state-top');
$(this).removeClass('switch-state-center');
$(this).addClass('switch-state-bottom');
$(this).find('.switch-3-label').eq(2).find('.switch-3-radio').prop('checked', true);
} else if (clickPos > heightThird1 && clickPos <= heightThird2) {
$(this).removeClass('switch-state-top');
$(this).removeClass('switch-state-bottom');
$(this).addClass('switch-state-center');
$(this).find('.switch-3-label').eq(1).find('.switch-3-radio').prop('checked', true);
} else {
$(this).removeClass('switch-state-bottom');
$(this).removeClass('switch-state-center');
$(this).addClass('switch-state-top');
$(this).find('.switch-3-label').eq(0).find('.switch-3-radio').prop('checked', true);
}
});
.switch-3 {
width: 3rem;
height: 7.5rem;
overflow: hidden;
}
.switch-3 .switch-3-slider {
width: 3rem;
height: 7.5rem;
left: 0;
right: 0;
transition: ease .4s all;
}
.switch-3 .switch-3-cover {
width: 3rem;
height: 2.5rem;
}
.switch-3 .switch-3-key {
width: 3rem;
height: 2.5rem;
background-color: rgba(0, 0, 0, .25);
}
.switch-3-label {
flex: 0 1 2.5rem;
}
.switch-3.switch-state-top .switch-3-slider {
bottom: 5rem;
}
.switch-3.switch-state-center .switch-3-slider {
bottom: 2.5rem;
}
.switch-3.switch-state-bottom .switch-3-slider {
bottom: 0rem;
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="position-relative d-inline-block switch-3 switch-state-bottom" role="button">
<div class="h-100 w-100 d-flex flex-column flex-nowrap text-light bg-primary">
<label class="d-flex justify-content-center align-items-center switch-3-label">
<small>ON</small>
<input class="switch-3-radio" type="radio" value="on" />
</label>
<label class="d-flex justify-content-center align-items-center switch-3-label">
<small>N/A</small>
<input class="switch-3-radio" type="radio" value="na" />
</label>
<label class="d-flex justify-content-center align-items-center switch-3-label">
<small>OFF</small>
<input class="switch-3-radio" type="radio" value="off" checked />
</label>
</div>
<div class="position-absolute d-block switch-3-slider">
<span class="d-block bg-primary switch-3-cover"></span>
<span class="d-block bg-primary switch-3-cover"></span>
<span class="d-block switch-3-key"></span>
<span class="d-block bg-primary switch-3-cover"></span>
<span class="d-block bg-primary switch-3-cover"></span>
</div>
</div>
You can always use concentric ternary operator.
Here’s the catch..
className = {title === 'One' ? classes.one : (title === 'Two : classes.two : classes.three)
and so on..for n number of classes.
I used “SWITCH” for switching the state. From Off to Mid, Mid to High, High to Mid, and Mid to Off. Sliding effect is applied for natural interaction. Here is my JSfiddle.
const switchContainer = document.querySelector(".switch-container");
const switchButton = document.querySelector(".switch-button");
const switchIndicator = document.querySelector(".switch-indicator");
const switchLabel = document.querySelector(".switch-label");
let currentState = 0;
switchContainer.addEventListener("click", function() {
switch (currentState) {
case 0: // From A to B
switchButton.classList.add("mid");
switchIndicator.classList.add("mid");
switchLabel.textContent = "Mid";
currentState = 1;
break;
case 1: // From B to C
switchButton.classList.remove("mid");
switchButton.classList.add("high");
switchIndicator.classList.remove("mid");
switchIndicator.classList.add("high");
switchLabel.textContent = "High";
currentState = 2;
break;
case 2: // From C to B
switchButton.classList.remove("high");
switchButton.classList.add("mid");
switchIndicator.classList.remove("high");
switchIndicator.classList.add("mid");
switchLabel.textContent = "Mid";
currentState = 3;
break;
case 3: // From B to A
switchButton.classList.remove("mid");
switchIndicator.classList.remove("mid");
switchLabel.textContent = "Off";
currentState = 0;
break;
}
});
switchButton.addEventListener("mousedown", (e) => {
dragging = true;
initialX = e.clientX;
});
document.addEventListener("mousemove", (e) => {
if (dragging) {
const deltaX = e.clientX - initialX;
if (deltaX > 20) {
updateSwitchState();
initialX = e.clientX;
} else if (deltaX < -20) {
currentState = (currentState - 1 + 4) % 4;
updateSwitchState();
initialX = e.clientX;
}
}
});
document.addEventListener("mouseup", () => {
dragging = false;
});
// Set the initial label text
switchLabel.textContent = "Off";
.switch-component {
display: inline-block;
position: relative;
}
.switch-container {
display: inline-block;
position: relative;
width: 40px;
height: 24px;
border-radius: 12px;
border: 1px solid #d9d9d9;
}
.switch-button {
position: absolute;
top: 0;
left: 0;
width: 40px;
height: 24px;
border-radius: 12px;
background-color: #f7f7f7;
transition: background-color 0.3s;
}
.switch-indicator {
position: absolute;
top: 4px;
left: 4px;
width: 16px;
height: 16px;
border-radius: 8px;
background-color: white;
transition: left 0.3s;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
}
.switch-button.mid {
background-color: #77ff77;
}
.switch-button.high {
background-color: #ff7777;
}
.switch-indicator.mid {
left: 12px;
}
.switch-indicator.high {
left: 24px;
}
.switch-wrapper {
display: flex;
}
.switch-label {
font-family: 'Roboto', sans-serif;
font-size: 12px;
color: #777;
text-align: center;
margin-top: 5px;
}
<div class="switch-component">
<div class="switch-container">
<div class="switch-wrapper">
<div class="switch-button"></div>
<div class="switch-indicator"></div>
</div>
</div>
<div class="switch-label"></div>
</div>