How to make a radio button unchecked by clicking it?
Solution 1:
You can set HTML object's property checked
to false
like this:
document.getElementById('desiredInput').checked = false;
Examples
Plain JavaScript
:
var radios = document.getElementsByTagName('input');
for(i=0; i<radios.length; i++ ) {
radios[i].onclick = function(e) {
if(e.ctrlKey || e.metaKey) {
this.checked = false;
}
}
}
<input type="radio" name="test" value="1" />
<input type="radio" name="test" value="2" checked="checked" />
<input type="radio" name="test" value="3" />
jQuery
:
$('input').click(function(e){
if (e.ctrlKey || e.metaKey) {
$(this).prop('checked', false);
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="radio" name="test" value="1" />
<input type="radio" name="test" value="2" checked="checked" />
<input type="radio" name="test" value="3" />
Hold down Ctrl (⌘ on mac) key to uncheck.
Solution 2:
Radio buttons are meant to be used in groups, as defined by their sharing the same name
attribute. Then clicking on one of them deselects the currently selected one. To allow the user to cancel a “real” selection he has made, you can include a radio button that corresponds to a null choice, like “Do not know” or “No answer”.
If you want a single button that can be checked or unchecked, use a checkbox.
It is possible (but normally not relevant) to uncheck a radio button in JavaScript, simply by setting its checked
property to false, e.g.
<input type=radio name=foo id=foo value=var>
<input type=button value="Uncheck" onclick=
"document.getElementById('foo').checked = false">
Solution 3:
This is my answer (though I made it with jQuery but only for the purpose of selecting elements and to add and remove a class, so you can easily replace it with pure JS selectors & pure JS add attribute )
<input type='radio' name='radioBtn'>
<input type='radio' name='radioBtn'>
<input type='radio' name='radioBtn'>
$(document).on("click", "input[name='radioBtn']", function(){
thisRadio = $(this);
if (thisRadio.hasClass("imChecked")) {
thisRadio.removeClass("imChecked");
thisRadio.prop('checked', false);
} else {
thisRadio.prop('checked', true);
thisRadio.addClass("imChecked");
};
})
Solution 4:
How unchecking radio does (not) work
You cannot easily implement uncheck trivially via if(this.checked) this.checked = false
, (if you really want to, see the hacker way at the end) because the events fire in this order:
-
mousedown
orkeydown
-
mouseup
orkeyup
- if not checked, set the checked property now
click
-
input
(only if state is changed) -
change
(only if state is changed)
Now in which event to perform the mentioned uncheck?
-
mouseup
ormousedown
: then in the step 3 the value is set back to true and change and input event doesn't even fire as the state didn't change when they are called in the sequence - so you can't uncheck it here -
click
: then the state is always false and input and change also doesn't fire - so you can't check it -
input
orchange
: it doesn't fire when the state is not changed and clicking selected element doesn't change the state - so you can't do anything useful here
The naive way
As you can learn from the sequence above, the way is:
- read the previous state in
mouseup
- set the state in
click
as negation of previous state
If you want to store the previous state in data attribute, keep in mind that it is saved as string, whereas the checked attribute is boolean. So you can implement it like:
radio.onmouseup = function() { this.dataset.checked = this.checked? 1 : ""; }
radio.onclick = function() { this.checked = !this.dataset.checked; }
It seemingly works, but you should not do this for these reasons:
- the user may
mousedown
somewhere else, then hover above radio button, thenmouseup
: in this case mouseup fires and click does not - the user may use Tab to focus radio group, then arrows to change: mouseup doesn't fire and click does
The proper way
There is another issue: dynamically added radio buttons. There are two ways:
-
element.appendChild(radio)
- if you enable deselect on all radios inDOMContentLoaded
event, this dynamically added radio is not affected -
element.innerHTML+= '<input type="radio">'
- effectively replaces the HTML contents of the element and re-creates DOM inside it - so all event listeners are discarded
To solve (2), I recommend onclick content attribute. Note that element.onclick = fn
and element.setAttribute("onclick", "fn()")
are two different things. Also note that onclick
fires everytime the user activates the radio, regardless of the interface he used.
Yet another issue: if you enable deselect, then you should also enable switching by Space to mimic checkboxes behaviour. The following code solves all mentioned issues:
function deselectableRadios(rootElement) {
if(!rootElement) rootElement = document;
if(!window.radioChecked) window.radioChecked = {};
window.radioClick = function(e) {
const obj = e.target, name = obj.name || "unnamed";
if(e.keyCode) return obj.checked = e.keyCode!=32;
obj.checked = window.radioChecked[name] != obj;
window.radioChecked[name] = obj.checked ? obj : null;
}
rootElement.querySelectorAll("input[type='radio']").forEach( radio => {
radio.setAttribute("onclick", "radioClick(event)");
radio.setAttribute("onkeyup", "radioClick(event)");
});
}
deselectableRadios();
<label><input type="radio" name="tag1">one</label>
<label><input type="radio" name="tag1">two</label>
<label><input type="radio" name="tag1">three</label>
<br><br>
<label><input type="radio" name="tag2">one</label>
<label><input type="radio" name="tag2">two</label>
<label><input type="radio" name="tag2">three</label>
Now you can call deselectableRadios()
anytime you dynamically add content and calling it on radios multiple times doesn't break it. You can also specify the rootElement
to update only a subtree of HTML DOM and make your web faster. If you don't like the global state, you can use the hacker way:
The hacker way
The point is to abuse setTimeout
on mouseup
to call it after the checked property is set:
function deselectable() {
setTimeout(checked => this.checked = !checked, 0, this.checked);
}
Now you can make any radio button deselectable:
radio.onmouseup = deselectable;
But this simple one-liner works just with clicking and doesn't solve the issues mentioned above.
The abandoned future
Deselectable radio is basically checkbox where only one in the group can be checked. There was a promising straightforward hope to code it as
<input type="checkbox" name="foo" style="appearance: radio">
However, the radio
value is now defined as compat-auto type which is treated like auto
, i.e. no visual change. It seems that there will be no progress here in the future.
Solution 5:
Wrapped up in a plugin
Limitations:
- Require form element
- Must trigger click event when changing radio button programmatically
(function($) {
$.fn.uncheckableRadio = function() {
var $root = this;
$root.each(function() {
var $radio = $(this);
if ($radio.prop('checked')) {
$radio.data('checked', true);
} else {
$radio.data('checked', false);
}
$radio.click(function() {
var $this = $(this);
if ($this.data('checked')) {
$this.prop('checked', false);
$this.data('checked', false);
$this.trigger('change');
} else {
$this.data('checked', true);
$this.closest('form').find('[name="' + $this.prop('name') + '"]').not($this).data('checked', false);
}
});
});
return $root;
};
}(jQuery));
$('[type=radio]').uncheckableRadio();
$('button').click(function() {
$('[value=V2]').prop('checked', true).trigger('change').trigger('click');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<form>
<label><input name="myRadio" type="radio" value="V1" /> R1</label>
<label><input name="myRadio" type="radio" value="V2" /> R2</label>
<label><input name="myRadio" type="radio" value="V3" /> R3</label>
<button type="button">Change R2</button>
</form>