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:

  1. mousedown or keydown
  2. mouseup or keyup
  3. if not checked, set the checked property now
  4. click
  5. input (only if state is changed)
  6. change (only if state is changed)

Now in which event to perform the mentioned uncheck?

  • mouseup or mousedown: 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 or change: 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:

  1. read the previous state in mouseup
  2. 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, then mouseup: 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:

  1. element.appendChild(radio) - if you enable deselect on all radios in DOMContentLoaded event, this dynamically added radio is not affected
  2. 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:

  1. Require form element
  2. 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>