如何检测单选按钮取消选择事件?

63

有没有一种简单的方法在单选按钮上附加“取消选择”事件?似乎只有在选中按钮时才会触发更改事件。

HTML

<input type="radio" id="one" name="a" />
<input type="radio" id="two" name="a" />

JavaScript

$('#one').change(function() {
    if(this.checked) {
        // do something when selected
    } else { // THIS WILL NEVER HAPPEN
        // do something when deselected
    }
});​

jsFiddle


用户无法取消选择单选按钮...您想要做什么? - charlietfl
38
用户可以通过选择另一个单选按钮来取消选择一个已选中的单选按钮。 - tskuzzy
10
当单选框组的选择发生变化时,想知道之前选择的是哪个单选框并不是不合理的要求。当只有两个单选框时,这似乎是微不足道的,因为很明显它总是那个没有刚被点击的单选框,但在一个单选框组中有超过两个单选框是相当普遍的情况。 - nnnnnn
@undefined:是的,有点像那样来跟踪先前的选择 - 结合holodoc的答案,您可以为刚刚取消选中的单选按钮触发自定义“取消选择”事件。 (注意:在我的工作中,我从未真正需要这样做,但似乎这就是OP所要求的。) - nnnnnn
6
收音机可以被取消选择。关于收音机的'onchange'事件的奇怪之处在于它不是 'onChange',而是 'onChangeToTrue'。他们当前的实现与'<select /> + <option />'没有区别,除了装饰/表示方式。逻辑上讲,当选择另一个按钮时,它就会改变!为什么不调用'onchange'?相反,应该使用每个单选按钮组中的每个处理程序来调用 'target.checked === false',但刚刚选择的除外。 - Cody
显示剩余4条评论
10个回答

31
为什么不创建一个自定义事件,比如说deselect,让它在所有被单击的单选框组成员中触发,除了被点击的元素本身?这样更容易利用jQuery提供的事件处理API。

HTML

<!-- First group of radio buttons -->
<label for="btn_red">Red:</label><input id="btn_red" type="radio" name="radio_btn" />
<label for="btn_blue">Blue:</label><input id="btn_blue"  type="radio" name="radio_btn" />
<label for="btn_yellow">Yellow:</label><input id="btn_yellow" type="radio" name="radio_btn" />
<label for="btn_pink">Pink:</label><input id="btn_pink"  type="radio" name="radio_btn" />
<hr />
<!-- Second group of radio buttons -->
<label for="btn_red_group2">Red 2:</label><input id="btn_red_group2" type="radio" name="radio_btn_group2" />
<label for="btn_blue_group2">Blue 2:</label><input id="btn_blue_group2"  type="radio" name="radio_btn_group2" />
<label for="btn_yellow_group2">Yellow 2:</label><input id="btn_yellow_group2" type="radio" name="radio_btn_group2" />
<label for="btn_pink_group2">Pink 2:</label><input id="btn_pink_group2"  type="radio" name="radio_btn_group2" />

jQuery

// Attaching click event handlers to all radio buttons...
$('input[type="radio"]').bind('click', function(){
    // Processing only those that match the name attribute of the currently clicked button...
    $('input[name="' + $(this).attr('name') + '"]').not($(this)).trigger('deselect'); // Every member of the current radio group except the clicked one...
});

$('input[type="radio"]').bind('deselect', function(){
    console.log($(this));
})

取消选择事件仅在具有相同 name 属性的单选按钮组成员之间触发。

jsFiddle 解决方案

编辑:为了考虑所附标签标记(包装单选按钮元素或通过 id 选择器附加)的所有可能放置位置,最好使用 onchange 事件来触发处理程序。感谢Faust指出这一点。

$('input[type="radio"]').on('change', function(){
    // ...
}

1
不错的解决方案,只是它没有指示先前选择的是哪个单选按钮。 - nnnnnn
2
我在关注主要问题,提供一个自定义取消选择事件的解决方案,当然OP可以根据自己的喜好进行修改。 - user188654
1
最好绑定到更改事件而不是单击事件。 - Kamil Szot
3
如果因为点击(非换行的)标签而更改了无线电,则无法工作。 - Faust
holodoc:如果您编辑您的回答,我就可以取消我的负评。 - Faust
显示剩余4条评论

22

你可以相对轻松地创建一个自定义的“取消选择”事件,但正如你已经发现的那样,标准的“更改”事件仅在新选中的单选按钮上触发,而不是在刚刚取消选中的先前选中的按钮上触发。

如果您想要能够执行类似以下操作:

$("#one").on("deselect", function() {
    alert("Radio button one was just deselected");
});

然后从你的文档就绪处理程序中运行类似以下函数的代码(或将代码直接放入文档就绪处理程序中):

function setupDeselectEvent() {
    var selected = {};
    $('input[type="radio"]').on('click', function() {
        if (this.name in selected && this != selected[this.name])
            $(selected[this.name]).trigger("deselect");
        selected[this.name] = this;
    }).filter(':checked').each(function() {
        selected[this.name] = this;
    });
}

工作示例: http://jsfiddle.net/s7f9s/2

该代码会在页面上的所有单选框上放置一个点击处理程序(这并不会阻止您在相同的单选框上添加自己的点击事件处理程序),它将检查在同一组中(即,具有相同名称的单选框)是否有先前选择的单选框,并在该单选框上触发“取消选择”事件。然后将刚刚单击的单选框保存为当前的单选框。如果单击已经选中的单选框或没有先前选中的单选框,则不会触发“取消选择”事件。末尾的.filter().each()代码段是为了记录哪些单选按钮已经被选中。(如果需要处理同一页上具有相同名称的独立单选框组的多个表单,请相应地更新上面的函数。)


1
+1 谢谢!!! 这对我来说是完美的解决方案! 我只需要将 'on' 函数调用更改为 'bind',因为我使用的是旧版本的 jQuery。 - Mark Whitfeld
1
最好绑定到更改事件而不是单击事件。 - Kamil Szot
1
@KamilSzot - 不会的。Click在所有浏览器中都可以使用,但在某些浏览器中,如果您通过键盘进行更改,则更改事件直到您切换控件后才会发生。 - nnnnnn
很棒的工作,但不幸的是,在移动版Safari中无法运行。 - Martin Röbert

4
我发现在不使用新框架来创建一个“取消选择”事件的情况下,最简单的方法是使任何单选按钮的更改触发其组中所有单选按钮上的update事件,然后在update事件中定义所需的行为。
缺点是即使之前没有选中单选按钮,取消选择分支中的代码也会运行。如果您只是简单地显示、隐藏或禁用UI元素,则这应该没什么关系。
以您的示例为例:
buttons = $('input[name="a"]');
buttons.change(function() {
    buttons.trigger('update:groupA');

}).bind('update:groupA', function(){
    if(this.checked) {
        //Do your checked things
    } else {
        //Do your unchecked things. Gets called whenever any other button is selected, so don't toggle or do heavy computation in here.
    }
});​

3
我认为你需要在输入层级别上添加更改函数,而不是在每个单选按钮上添加。

尝试这样做:

$("input[name='a']").change(function() {
  $("input[name='a']").each(function(){
    if(this.checked) {
        // do something when selected
    } else {
        // do something when deselected
    }
  });   
});​

0
我认为这可能是因为focus事件在change事件之前触发,所以你点击的下一个单选按钮会在先前选中的单选按钮触发更改事件之前获得焦点。不过这只是我的猜测...
你可以像这样做:
var isChecked = function(id) { alert(id + ': ' + $('#' + id).is(':checked')) }
$('input[name="a"]').change(function(){ isChecked('one') })

演示:http://jsfiddle.net/elclanrs/cD5ww/


0

我找到了一个解决方案,适用于我的特定情况,可能会有所帮助。当“取消选择”事件可以应用于所有未选中的单选按钮时,此方法有效。

我想要:

  1. 单选按钮被选中时向元素添加类,并且
  2. 在按钮“取消选择”时删除该类。

我碰巧发现了这个问题,因为我也遇到了同样的问题:

$('input:radio').on('change', function() {
    if( $(this).is(':checked') ) {
        $(this).addClass('my-class-for-selected-buttons')
    } else { // THIS WILL NEVER HAPPEN
        $(this).removeClass('my-class-for-selected-buttons')
    }
});​

但是,在我的情况下,解决方案要简单得多,因为我可以尝试使用jQuery从所有单选按钮中轻松地删除类,并将该类添加到所选的按钮:

$('input:radio').on('change', function() {
    $('input:radio').removeClass('my-class-for-selected-buttons') // Here!

    if( $(this).is(':checked') ) {
        $(this).addClass('my-class-for-selected-buttons')
    }
});​

通过这个简单的调整,我不需要找到触发“取消选择”事件的方法。
所以,如果在你的情况下,你可以将事件应用于所有未被选中的单选按钮,而不仅仅是刚刚被“取消选择”的那一个,你可以使用这个方法!

0

注意:我正在使用最新版本的jQuery:版本3.4.1。但是这也适用于旧版本。

这里的主要挑战是更改事件仅针对选中的单选按钮触发。下面的代码证实了这一点。

$("input[name^='account']").change(function() {
    console.log($(this).prop('id') + " was checked");
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>

<form action='#'>
    <input id='john' type='radio' name='account[]' value=''><label for='john'>John</label><br>
    <input id='jane' type='radio' name='account[]' value=''><label for='jane'>Jane</label><br>
    <input id='jeff' type='radio' name='account[]' value=''><label for='jeff'>Jeff</label><br>
    <input id='jude' type='radio' name='account[]' value=''><label for='jude'>Jude</label><br>
    <input type='text' name='amount' value=''><br>
    <input type='submit' value='submit'>
</form>

我的解决方案:在change事件处理程序中完成所有操作,分3个简单步骤:

  1. 处理当前选中的单选按钮的更改。
  2. 将自定义事件和处理程序附加到同一组中的所有其他单选按钮。
  3. 立即触发此自定义事件。

这里不需要玩转点击事件。 简单明了!

var radioBtns = $("input[name^='account']");
radioBtns.change(function() {
    // 1. handle changes for the currently checked radio button.
    console.log($(this).prop('id') + " was checked");
    // 2. attach custom event and handler to all other radio buttons in the same group.
    radioBtns.not(':checked').off('deselect').on('deselect', function() {
        $(this).each(function(i, e) {
            console.log($(e).prop('id') + " was not checked");
        });
    }).trigger('deselect'); // 3. immediately trigger this custom event.
     
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>

<form action='#'>
    <input id='john' type='radio' name='account[]' value=''><label for='john'>John</label><br>
    <input id='jane' type='radio' name='account[]' value=''><label for='jane'>Jane</label><br>
    <input id='jeff' type='radio' name='account[]' value=''><label for='jeff'>Jeff</label><br>
    <input id='jude' type='radio' name='account[]' value=''><label for='jude'>Jude</label><br>
    <input type='text' name='amount' value=''><br>
    <input type='submit' value='submit'>
</form>


0

我稍微尝试了一下这些ID。 公平地说,那可能是一种低效的解决方案。

<input type="radio" id="radio-1" name="a" value="initial 1"/>
<input type="radio" id="radio-2" name="a" value="initial 2"/>

let id;
$('input[id*="radio-"]').on('click', (function() {
      if (this.id != id && this.checked) {
        id = this.id;
        this.checked = true;
        console.log('selected');
      } else if (this.id == id && this.checked) {
        id = undefined;
        this.checked = false;
        console.log('deselected');
      }
}));

{{链接1:JSFiddle}}


0
你可以自己触发“change”事件。避免单选按钮无限地相互触发“change”事件有点棘手,但可以像这样完成:
$('input[type="radio"]').each(function() {
    var name = $(this).attr('name');
    var that = this;
    $('input[name="'+name+'"][type="radio"]').not(that)
        .on('change', function(e, alreadyTriggered) {
            if(!alreadyTriggered || alreadyTriggered.indexOf(this) == -1) {
                if(!alreadyTriggered) {
                    alreadyTriggered = [that];
                }
                alreadyTriggered.push(this);
                $(that).trigger('change', [alreadyTriggered]);
            }
    });
});

这是上面代码的工作示例,demo

-1

这个对你来说怎么样?

http://jsfiddle.net/WZND9/6/

 $('input').change(function() {
    if ($('#one').is(':checked')) {
        alert('checked');
    } else { 
        alert('not checked');
    }
 }); 

1
点赞以清除踩赞:不确定为什么会被踩赞,因为它显然是有效的。显然需要限制到特定的输入组,而不是所有的<input />。我知道这重复了其他答案,但它并没有错。那些踩赞的人能否请评论一下原因。 - Morvael
这个代码除了告诉你是否选中了ID为"One"的按钮之外,什么也不做。它不会告诉你先前选中的是哪个按钮,但由于只有两个按钮,所以简单的推断可以告诉你。 - RationalRabbit
更改仅针对选中的单选按钮触发。这并不能解决问题。 - Udo E.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接