开发者

How to check/uncheck radio button on click?

开发者 https://www.devze.com 2023-02-09 13:22 出处:网络
I want to be able to uncheck a radio button by clicking on it开发者_如何学运维. So, if a radio button is unchecked, I want to check it, if it is checked, I want to uncheck it.

I want to be able to uncheck a radio button by clicking on it开发者_如何学运维.

So, if a radio button is unchecked, I want to check it, if it is checked, I want to uncheck it.

This does not work:

$('input[type=radio]:checked').click(function(){
    $(this).attr('checked', false);
});

I am not able to check a radio button now.


This is not to replace a checkbox, it's to allow a radio group to go back to an unselected state. This was tricky because the radio selection doesn't act like a normal event.

The browser handles radio buttons outside the normal event chain. So, a click handler on a radiobutton with event.preventDefault() or event.stopPropagation() will NOT prevent the radiobutton from being checked. The .removeAttr('checked') must be done in a setTimeout to allow the event to finish, and the browser to check the radiobutton (again), and then the setTimeout will fire.

This also correctly handles the case where a user starts the mousedown but leaves the radiobutton before mouseup.

//$ = jQuery;
$(':radio').mousedown(function(e){
  var $self = $(this);
  if( $self.is(':checked') ){
    var uncheck = function(){
      setTimeout(function(){$self.removeAttr('checked');},0);
    };
    var unbind = function(){
      $self.unbind('mouseup',up);
    };
    var up = function(){
      uncheck();
      unbind();
    };
    $self.bind('mouseup',up);
    $self.one('mouseout', unbind);
  }
});

I hope this helps


try this:

$('input[type=radio]').click(function(){
    if (this.previous) {
        this.checked = false;
    }
    this.previous = this.checked;
});


The accepted answer does not work on mobile devices. It relies on setTimeout and bindings that are related to mouse events that just don't fire on tablets/mobile devices.
My solution is to manually track the selected state using the "click" event handler and a custom class state.

  1. handle the click events on the radio input elements
  2. check if the "selected" class exists on the clicked element
  3. if it does, (meaning it has been clicked before), remove the class and uncheck the element, return
  4. if it doesn't, remove the class from all elements of the same name and add it to only the clicked element.

No need to prevent default behaviors. Here is the code in Jquery:

$("input:radio").on("click",function (e) {
    var inp=$(this); //cache the selector
    if (inp.is(".theone")) { //see if it has the selected class
        inp.prop("checked",false).removeClass("theone");
        return;
    }
    $("input:radio[name='"+inp.prop("name")+"'].theone").removeClass("theone");
    inp.addClass("theone");
});

http://jsfiddle.net/bhzako65/


I must be missing it but after all these years AFAIK none of the solutions above seem to work or maybe I'm just dumb.

There is absolutely no reason to use a radio button unless there is more than one radio button in the same group. If it's a lone radio button then just use a checkbox. The reason to use a radio button is for selecting one of mutually exclusive options. That means any solution which only looks at individual buttons will fail since clicking a one button will effect the state of the other buttons

In other words since we're using radioboxes the solution needs to work for

<input type="radio" name="foo" id="foo1"><label for="foo1">foo1</label>
<input type="radio" name="foo" id="foo2"><label for="foo2">foo2</label>
<input type="radio" name="foo" id="foo3"><label for="foo3">foo3</label>

Here's one that looks at all the buttons.

This seems to work

document.querySelectorAll(
    'input[type=radio][name=foo]').forEach((elem) => {
  elem.addEventListener('click', allowUncheck);
  // only needed if elem can be pre-checked
  elem.previous = elem.checked;
});

function allowUncheck(e) {
  if (this.previous) {
    this.checked = false;
  }
  // need to update previous on all elements of this group
  // (either that or store the id of the checked element)
  document.querySelectorAll(
      `input[type=radio][name=${this.name}]`).forEach((elem) => {
    elem.previous = elem.checked;
  });
}
body { font-size: xx-large; }
label, input {
  /* added because a second click to unselect radio often
     appears as a double click to select text */
  -webkit-user-select: none;
  user-select: none;
}
<input type="radio" name="foo" id="foo1"><label for="foo1">foo1</label>
<input type="radio" name="foo" id="foo2"><label for="foo2">foo2</label>
<input type="radio" name="foo" id="foo3"><label for="foo3">foo3</label>

note if elem.previous worries you you could use elem.dataset.previous

Another solution would be to store which button is checked

function makeRadioboxGroupUnCheckable(groupSelector) {

  let currentId;

  document.querySelectorAll(groupSelector).forEach((elem) => {
    elem.addEventListener('click', allowUncheck);
    // only needed if can be pre-checked
    if (elem.checked) {
      currentId = elem.id;
    }
  });

  function allowUncheck(e) {
    if (this.id === currentId) {
      this.checked = false;
      currentId = undefined;
    } else {
      currentId = this.id;
    }
  }
}

makeRadioboxGroupUnCheckable('input[type=radio][name=foo]');
body { font-size: xx-large; }
label, input {
  /* added because a second click to unselect radio often
     appears as a double click to select text */
  -webkit-user-select: none;
  user-select: none;
}
<input type="radio" name="foo" id="foo1"><label for="foo1">foo1</label>
<input type="radio" name="foo" id="foo2"><label for="foo2">foo2</label>
<input type="radio" name="foo" id="foo3"><label for="foo3">foo3</label>


This is the real solution ...

var check;

$('input[type="radio"]').hover(function() {
    check = $(this).is(':checked');
});

$('input[type="radio"]').click(function() {
    check = !check;
    $(this).attr("checked", check);
});

Try it, it works for me!


Found this somewhere while looking for a solution, do like the simplicity of it...

var checkedradio;
function docheck(thisradio) {
    if (checkedradio == thisradio) {
        thisradio.checked = false;
        checkedradio = null;
    }
    else {checkedradio = thisradio;}
}

Use with:

<input type='radio' onclick='docheck(this);'>

It does seem to require a double click to deselect when you have multiple radio groups in a form though, but this could by solved by having a function for each group I suppose...


I know this question is old, but I just came across this problem and decided to have a go myself and didn't want to use a modifier button like ctrl.

See fiddle at http://jsfiddle.net/ArktekniK/yhbw469f/ For toggling a radio button by clicking on the radio button itself

$('input[type=radio]').on('mousedown', function(e){
  var wasChecked = $(this).prop('checked');
  this.turnOff = wasChecked;
  $(this).prop('checked', !wasChecked);
});

$('input[type=radio]').on('click', function(e){
  $(this).prop('checked', !this.turnOff);
  this['turning-off'] = !this.turnOff;
});

For toggling a radio button by clicking on the label or radio button itself

$('label:has(input[type=radio])').on('mousedown', function(e){
  var radio = $(this).find('input[type=radio]');
  var wasChecked = radio.prop('checked');
  radio[0].turnOff = wasChecked;
  radio.prop('checked', !wasChecked);
});

$('label:has(input[type=radio])').on('click', function(e){
  var radio = $(this).find('input[type=radio]');
  radio.prop('checked', !radio[0].turnOff);
  radio[0]['turning-off'] = !radio[0].turnOff;
});


Who those who are seeking for a pure JavaScript solution I modified the code from Jase in ATL's answer.

I wrote this code for proposal of use with a CSS styled 3 position switch which provides 4 state of toggling (On, Off, Neutral and Unactivated).

function toggle_radio(ev) {
	var radio = ev.target;
	var cut_pos = radio.className.indexOf(' switcher-active');

	// the switch itself
	if (cut_pos !== -1) { // if the button checked it is '.switcher-active'
		radio.checked = false; // toggle its state
		radio.className = radio.className.slice(0, cut_pos); // remove '.switcher-active'
		return true; // work is done. Break the function
	}

	// the button was inactive before it was clicked. Then it should became '.switcher-active'
	radio.className = radio.className + ' switcher-active';

	// we need to remove '.switcher-active' if either the left or right radio button was clicked. This part is uggly but I don't bother, because I'm late for barber
	var radios = document.getElementsByName(radio.name); // get all these radio siblings as a collection of elements
	for (var i=0; i < radios.length; i++) { // iterate through the collection
		if (radios[i].className.indexOf('switcher-radio-neutral') !== -1)
			continue; // skip the '.switcher-neutral' radio input

		radios[i].onclick = function(ev2) {
			sib_radios = document.getElementsByName(ev2.target.name); // get a group of radio inputs linked by the name

			// get the '.switcher-neutral'
			for (var k=0, cut_pos = -1, sib; k < sib_radios.length; k++) {
				sib = sib_radios[k];
				cut_pos = sib.className.indexOf(' switcher-active');
				if (cut_pos !== -1)
					sib.className = sib.className.slice(0, cut_pos);
			}
		}
	}
}

var switchers = document.getElementsByClassName('switcher-radio-neutral');

for (var i=0; i < switchers.length; i++) { // bind onclick handlers
	switchers[i].onclick = toggle_radio;
}
.switcher {
	position: relative;
	display: inline-block;
	margin: 1px 10px;
	height: 20px;
	width: 58px;
	z-index: 1;
}

.switcher-off {
	left: 1px;
	width: 33%;
	height: 100%;
}

.switcher-neutral {
	left: 33%;
	width: 33%;
	height: 100%;
}

.switcher-on{
	right: 1px;
	width: 33%;
	height: 100%;
}

.switcher-label {
	position: absolute;
	text-indent: -9999px;
	z-index: 2;
}

.switcher input {
	visibility: hidden;
	position: absolute;
}

.switcher-slider {
	height: 100%;
	width: 100%;
	border-radius: 10px;
	box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1) inset, 0 0 4px rgba(0, 0, 0, 0.5) inset, 0 2px 2px 1px rgba(0, 0, 0, 0.3) inset;
	transition: background-color 0.2s linear 0s;
}

.switcher-slider:after {
	transition: left 0.2s linear 0s, right 0.2s linear 0s;
	background: linear-gradient(#D0D0D0, #FDFDFD) repeat scroll 0 0 rgba(0, 0, 0, 0);
	content: "";
	position: absolute;
	top: 1px;
	border-radius: 50%;
	height: calc(100% - 2px);
	width: calc(100%/3 - 1px);
	box-shadow: 0 0 1px 1px #f4f4f4 inset, 0 0 3px 1px rgba(0, 0, 0, 0.6);
	left: 33%;
}

.switcher-radio-on:checked  ~ .switcher-slider {
	background-color: #81EA89;
}

.switcher-radio-neutral:checked  ~ .switcher-slider {
	background: #ddd;
}

.switcher-radio-off:checked  ~ .switcher-slider {
	background-color: #ED8282;
}

.switcher-radio-on:checked  ~ .switcher-slider:after {
	left: calc(2*(100%/3));
}

.switcher-radio-neutral:checked  ~ .switcher-slider:after {
	left: calc(1px + 100%/3);
}

.switcher-radio-off:checked  ~ .switcher-slider:after {
	left: 1px;
}
<form action="">
		<input type="radio" name="radio_input" value="1">
		<input type="radio" name="radio_input" class="switcher-radio-neutral" value="2">
		<input type="radio" name="radio_input" value="3">
</form>
<br><br>
<div class="switcher">
	<label  class='switcher-label switcher-off' for='off'>off</label>
	<input id='off' class='switcher-radio-off' type='radio' name='value' value='off'>
	
	<label class='switcher-label switcher-neutral' for='neutral'>neutral</label>
	<input id='neutral' class='switcher-radio-neutral' type='radio' name='value' value='neutral' data-neutral="">
	
	<label class='switcher-label switcher-on' for='on'>on</label>
	<input id='on' class='switcher-radio-on' type='radio' name='value' value='on'>
	<div class='switcher-slider'></div>
</div>


If there is only one radio button you needs to use checkbox instead.

There is no meaning to having one radio button they works with group.

probably you are looking for checkbox control.


It is better UX to have a default-selected "Select none" explicit option, and not let the user uncheck any radio button.

(List item 9 in this nngroup (neilsen) article:

http://www.nngroup.com/articles/checkboxes-vs-radio-buttons/


As HexInteractive mentioned, radio button is handled outside the normal event chain. So, following example judges the button state by class name, not by property.

var $self;

$('input[type=radio]').on('click', function() {
  $self = $(this);
  if ( $self.hasClass('is-checked') ) {
    $self.prop('checked', false).removeClass('is-checked');
  } else {
    $self.addClass('is-checked');
  }
});


Based on answer by Stephen - this deals with multiple radio button groups and also you mostly don't need to double click radio buttons. It isn't perfect as if you switch between groups the check for id will necessitate an extra click but works ok.

$("input[type='radio']").on('click', function (e) {
            if (this.previous && this.value == previousvalue && this.id == previousid) {
                this.checked = false;
            }
            this.previous = this.checked;
            previousvalue = this.value;
            previousid = this.id;

        });


Yes you can also do this on click checked, again click uncheck. Here is the logic of this:

$('input[name=check1]').prop('checked',!$('input[name=check1]').prop('checked'));


 var checked = {};
    $('.desectable-radio').each(function (index) {
        checked[index] = this.checked;
        $(this).click(function () {
            if (checked[index])
                this.checked = false;
            for (var i in checked) {
                checked[i] = false;
            }
            checked[index] = this.checked;
        });
    });


Here's a super lightweight script you can add to a page through the console window that allows you to deselect a radio button by holding down Ctrl while clicking it with the mouse.

document.addEventListener('click', function(e){
   if (e.ctrlKey == true && 
       e.target.tagName == 'INPUT' && 
       e.target.type == "radio" && 
       e.target.checked == true) {
       e.target.checked = false;
   }
});

Since it doesn't rely on jQuery, you can easily add it to any page to temporarily allow deselection.
For slightly more info, you can read an article I just wrote on How To Deselect A Radio Button.


Needing a solution to this issue, I settled on replacing the currently active radio button with a ‘cancel’ icon button, like this http://fontawesome.io/icon/ban/

Clicking this icon unchecked the active radio button, which then reappeared, effectively resetting the radio button group. The cancel icon was also removed from the DOM.

Alternatively the user could click another of the radio buttons in the group, which would behave as normal aside from the cancel button, which was then ‘moved’ to replace the new active radio button.

This kept the cancel icon in an intuitive location (i.e. where the user last clicked) and was safer than assuming the user would know to re-click the currently active radio button to deactivate the group.

Worst case if you have a long list of radio buttons, the user just has to click any of them twice to clear the group.


I think this is the shortest way. I tested it on Chrome and MS Edge.

$(document).on('click', 'input:radio', function () {
    var check = $(this).attr('checked')
    if (check) $(this).removeAttr('checked').prop('checked',false)
    else $(this).attr('checked', true).prop('checked',true)
})

This piece of code also works on AJAX loaded contents.


Using prop function with checked as key:

$("input:radio").prop("checked",false);


This should work for desktop and mobile. I tested on Chrome and Firefox. Based on the highest rated answer, but using newer JQuery and alot simpler implementation using $.one.

$(':radio').on("mousedown touchstart", function (e) {
    var mobile = true;
    if(e.type.toLowerCase() === 'mousedown'){
        mobile = false;
    }
    
    var $self = $(this);
    if($self.prop('checked')){
        var uncheck = function(){
            setTimeout(function(){
                $self.prop('checked',false);
            },10);
        };
        if(!mobile){
            $self.one('mouseup', uncheck);
        } else {
            $self.one('touchstart', uncheck);
        }
    }
});


<script type="text/javascript">
    $( document ).ready(function() {
        $('input[type=radio]').click(function(){
            if (this.previous) {
                this.checked = false;
                document.getElementById("nc").style.display = "block";
                this.previous=false;
            }
            else {
                this.previous = this.checked;
                document.getElementById("nc").style.display = "none";
            }
        });
    });
</script>


A javascript version featuring deselection via the for label and no double clicks.
The temporary state is attached to a parent div.

Parent.onclick = e => {
  rb = e.target;
  if (rb.type == "radio") {
    if (Parent.rb == rb) {
      rb.checked = false;
      Parent.rb = null;
    } else {
      Parent.rb = rb
    }
  }
}
<div id="Parent">
  <input type="radio" name="foo" id="foo1">
  <label for="foo1">foo1</label>
  <input type="radio" name="foo" id="foo2">
  <label for="foo2">foo2</label>
  <input type="radio" name="foo" id="foo3">
  <label for="foo3">foo3</label>
</div>


A pure css solution (without javascript)

label > input:checked,
label > input:checked + label {
    pointer-events: none;
}

#unselect {
    display: none;   
}
<input type="radio" name="foo" id="unselect">
<label for="unselect">
  <input type="radio" name="foo" id="foo1">
  <label for="foo1">foo1</label>
  <input type="radio" name="foo" id="foo2">
  <label for="foo2">foo2</label>
  <input type="radio" name="foo" id="foo3">
  <label for="foo3">foo3</label>
</label>

The radiobuttons are embedded into another radiobutton. When a radiobutton is selected, it doesn't accepted clicks anymore, instead the underlying radiobutton captures the click.

https://jsfiddle.net/w29rem7q/

original, now obsolete solution

To each radio button a dummy radio button is attached. If a radio button is selected, an invisible dummy will be above the radio button to catch the next click.

input:not([id^=foo]) {
  opacity: 0.0;
}
input[id^=foo]:checked {
  position: absolute;
}
input[id^=foo]:not(:checked) + input {
  display: none;
}
<input type="radio" name="foo" id="foo1">
<input type="radio" name="foo">
<label for="foo1">foo1</label>
<input type="radio" name="foo" id="foo2">
<input type="radio" name="foo">
<label for="foo2">foo2</label>
<input type="radio" name="foo" id="foo3">
<input type="radio" name="foo">
<label for="foo3">foo3</label>

Likely just for fun, while in practice js will be easier to maintain.
Also deselection via label click, would be more work arounds.

0

精彩评论

暂无评论...
验证码 换一张
取 消