user2048994
user2048994

Reputation: 270

checkbox button does not work well when clicked on quickly multiple times

I have a jsfiddle Here: http://jsfiddle.net/zAFND/616

Now if you open up fiddle in IE (I use IE9) and firefox, if you double click on a check box button, it turns it on but does not turn it off. But if you open it up in opera, safarai and chrome, it works fine if you double click or click in quick succession.

My question is how to allow quick succession clicks to work correctly in firefox and IE9?

Code:

HTML:

<div id="ck-button"><label>
<input type="checkbox" name="options[]" id="optionA" value="A" /><span>A</span></label>
</div>

CSS:

#ck-button {
    margin:8px;
    background-color:#EFEFEF;
    border:1px solid #D0D0D0;
    overflow:auto;
    float:left;
    position: relative;
}

#ck-button label {
    float:left;
    width:4.0em;
    cursor:pointer;
}

#ck-button label span {
    text-align:center;
    padding:3px 0px;
    display:block;
}

#ck-button label input {
    position:absolute;
    top:-20px;
}

#ck-button input:checked + span {
    background-color:green;
    color:white;
}

Jquery/javasscript:

 $(document).ready(function(){
            $("body").css("-webkit-user-select","none");
            $("body").css("-moz-user-select","none");
            $("body").css("-ms-user-select","none");
            $("body").css("-o-user-select","none");
            $("body").css("user-select","none");
    });

Upvotes: 3

Views: 4120

Answers (2)

Fabr&#237;cio Matt&#233;
Fabr&#237;cio Matt&#233;

Reputation: 70189

This is a bug in Firefox. See Bug 608180 - Double/rapid clicking a checkbox label does not work as expected

IE has, for historical reasons (but fixed in more recent versions), a bugged event model that skips the second mousedown and click events on a double click. See bug 263 - beware of DoubleClick in IE.

I've made a plugin that fixes some bugs in jQuery UI button widget as well as working around the Firefox bug not long ago, shouldn't be hard to adapt it to your non-jQuery UI buttons.

Extracted the important part and adapted it for nested checkboxes inside labels:

(function () {
    var mdtarg, //last label mousedown target
        mdchecked, //checked property when mousedown fired
        fixedLabelSelector = '.fixedLabelCheckbox'; //edit as you see fit
    $(document).on('mousedown', fixedLabelSelector, function (e) {
        //only left clicks will toggle the label
        if (e.which !== 1) return;
        mdtarg = this;
        mdchecked = this.control ? this.control.checked : $(this).find('input')[0].checked;
        //reset mdtarg after mouseup finishes bubbling; prevents bugs with
        //incorrect mousedown-mouseup sequences e.g.
        //down IN label, up OUT, down OUT, up IN
        $(document).one('mouseup', function () {
            mdtarg = null;
        });
    }).on('mouseup', fixedLabelSelector, function (e) {
        if (e.which !== 1) return;
        if (mdtarg === this) {
            var ch = this.control || $(this).find('input')[0];
            //the click event is supposed to fire after the mouseup so
            //we wait until mouseup and click finish bubbling and check if it
            //had the desired effect
            setTimeout(function () {
                if (mdchecked === ch.checked) {
                    //else patch it manually
                    ch.checked = !ch.checked;
                    $(ch).change();
                }
            }, 0);
        }
    });
}());

Fiddle tested in Firefox.

You have to add the fixedLabelCheckbox class to all labels containing checkboxes that you'd like to fix with the code above.

It will work regardless of where you put the script and it also fixes dynamically added checkboxes as long as the label has the corresponding delegated class/selector.

Note that if you're using other libraries, this may not fire the change handlers bound outside of jQuery.

If you don't feel like adding extra classes to your markup, you can use this version (more code and less performance):

(function ($) {
    function getControl(lbl) { //fallback for non-HTML5 browsers if necessary
        return lbl.control || (lbl.htmlFor ? $('input[id="'+lbl.htmlFor+'"]')[0] : $(lbl).find('input')[0]);
    }
    var mdtarg, //last label mousedown target
        mdchecked; //checked property when mousedown fired
    $(document).on('mousedown', 'label', function (e) {
        //only left clicks will toggle the label
        if (e.which !== 1) return;
        var ch = getControl(this);
        if (!ch || ch.type !== 'checkbox') return;
        mdtarg = this;
        mdchecked = ch.checked;
        //reset mdtarg after mouseup finishes bubbling; prevents bugs with
        //incorrect mousedown-mouseup sequences e.g.
        //down IN label, up OUT, down OUT, up IN
        $(document).one('mouseup', function () {
            mdtarg = null;
        });
    }).on('mouseup', 'label', function (e) {
        if (e.which !== 1) return;
        if (mdtarg === this) {
            var ch = getControl(this);
            //the click event is supposed to fire after the mouseup so
            //we wait until mouseup and click finish bubbling and check if it
            //had the desired effect
            setTimeout(function () {
                if (mdchecked === ch.checked) {
                    //else patch it manually
                    ch.checked = !ch.checked;
                    $(ch).change();
                }
            }, 0);
        }
    });
}(jQuery));

Fiddle

As you can see from the code above, this version should work with both label's for attribute as well as nested inputs inside the label, without adding any extra markup.


About disabling selection: you can either put the user-select in the CSS as commented in your question, or, if browsers that don't support the user-select are also concerned, apply this answer on all labels that you want to have selection disabled.

Upvotes: 3

ameed
ameed

Reputation: 1170

You could add browser detection and then, if IE or Firefox, add the ondblclick event via JS to invert the checkbox.

You can't just set it unconditionally, since some browsers (Safari, Chrome) transmit two clicks and a dblclick, while others (IE, Firefox) transmit only one click and one dblclick. On the former, the two click events will invert the field twice. On the latter, only one click event fires and thus the field is only inverted once; to mitigate this, you need to make dblclick invert the field so that two clicks invert it an even number of times.

Hope this helps!!

Upvotes: 0

Related Questions