reidestis
reidestis

Reputation: 313

selectItem in a SelectOneRadio reseting tabindex count

I have the following SelectOneRadio:

<p:selectOneRadio id="sex" tabindex="3" value="#{signUp.gender}" required="true">  
    <f:selectItem itemLabel="Male" itemValue="M"  />
    <f:selectItem itemLabel="Female" itemValue="F" />
</p:selectOneRadio>

Although the tabindex of the next element in my form is 4, when I tab to the "sex" component, and select any of the selectItem with either the space bar or with a mouse click, when I press the tab key again, the focus goes to the element with tabindex = 1, instead of the next one. How can I fix this?

I'm using primefaces 4.0

Thanks!

Upvotes: 3

Views: 1334

Answers (1)

I was able to reproduce the error using Primfaces showcase and I've also found a bug regarding the spacebar selection which is still opened.

As a work around you can override Primefaces default SelectOneRadio widget and make the required changes. To test that approach I've created a file called customradio.js with the following content:

PrimeFaces.widget.SelectOneRadio = PrimeFaces.widget.BaseWidget.extend({
    init: function(cfg) {
        this._super(cfg);

        //custom layout
        if (this.cfg.custom) {
            this.inputs = $('input:radio[name="' + this.id + '"]:not(:disabled)');
            this.outputs = this.inputs.parent().next('.ui-radiobutton-box:not(.ui-state-disabled)');
            this.labels = $();
            this.icons = this.outputs.find('.ui-radiobutton-icon');

            //labels
            for (var i = 0; i < this.outputs.length; i++) {
                this.labels = this.labels.add('label[for="' + this.outputs.eq(i).parent().attr('id') + '"]');
            }
        }
        //regular layout
        else {
            this.outputs = this.jq.find('.ui-radiobutton-box:not(.ui-state-disabled)');
            this.inputs = this.jq.find(':radio:not(:disabled)');
            this.labels = this.jq.find('label:not(.ui-state-disabled)');
            this.icons = this.jq.find('.ui-radiobutton-icon');
        }

        this.checkedRadio = this.outputs.filter('.ui-state-active');

        this.bindEvents();

        //pfs metadata
        this.inputs.data(PrimeFaces.CLIENT_ID_DATA, this.id);
    },
    bindEvents: function() {
        var $this = this;

        this.outputs.on('mouseover.selectOneRadio', function() {
            $(this).addClass('ui-state-hover');
        })
                .on('mouseout.selectOneRadio', function() {
                    $(this).removeClass('ui-state-hover');
                })
                .on('click.selectOneRadio', function() {
                    var radio = $(this),
                            input = radio.prev().children(':radio');

                    if (!radio.hasClass('ui-state-active')) {
                        $this.unselect($this.checkedRadio);
                        $this.select(radio);
                        input.trigger('click');
                        input.trigger('change');
                    }
                    else {
                        input.trigger('click');
                    }
                });

        this.labels.on('click.selectOneRadio', function(e) {
            var target = $(PrimeFaces.escapeClientId($(this).attr('for'))),
                    radio = null;

            //checks if target is input or not(custom labels)
            if (target.is(':input'))
                radio = target.parent().next();
            else
                radio = target.children('.ui-radiobutton-box'); //custom layout

            radio.trigger('click.selectOneRadio');

            console.log("click.selectOneRadio");

            e.preventDefault();
        });

        this.inputs.on('focus.selectOneRadio', function() {
            var input = $(this),
                    radio = input.parent().next();

            if (input.prop('checked')) {
                radio.removeClass('ui-state-active');
            }

            radio.addClass('ui-state-focus');
        })
                .on('blur.selectOneRadio', function() {
                    var input = $(this),
                            radio = input.parent().next();

                    if (input.prop('checked')) {
                        radio.addClass('ui-state-active');
                    }

                    radio.removeClass('ui-state-focus');
                })
                .on('keydown.selectOneRadio', function(e) {
                    var input = $(this),
                            currentRadio = input.parent().next(),
                            index = $this.inputs.index(input),
                            size = $this.inputs.length,
                            keyCode = $.ui.keyCode,
                            key = e.which;

                    switch (key) {
                        case keyCode.UP:
                        case keyCode.LEFT:
                            var prevRadioInput = (index === 0) ? $this.inputs.eq((size - 1)) : $this.inputs.eq(--index),
                                    prevRadio = prevRadioInput.parent().next();

                            input.blur();
                            $this.unselect(currentRadio);
                            $this.select(prevRadio);
                            prevRadioInput.trigger('focus').trigger('change');
                            e.preventDefault();
                            break;

                        case keyCode.DOWN:
                        case keyCode.RIGHT:
                            var nextRadioInput = (index === (size - 1)) ? $this.inputs.eq(0) : $this.inputs.eq(++index),
                                    nextRadio = nextRadioInput.parent().next();
                            input.blur();
                            $this.unselect(currentRadio);
                            $this.select(nextRadio);
                            nextRadioInput.trigger('focus').trigger('change');
                            e.preventDefault();
                            break;
                        case keyCode.SPACE:
                            if (!currentRadio.hasClass('ui-state-active')) {
                                $this.unselect($this.checkedRadio);
                                $this.select(currentRadio);
                                input.trigger('change');
                            }
                            break;
                    }
                });

        if (this.cfg.behaviors) {
            PrimeFaces.attachBehaviors(this.inputs, this.cfg.behaviors);
        }
    },
    unselect: function(radio) {
        radio.prev().children(':radio').prop('checked', false);
        radio.removeClass('ui-state-active').children('.ui-radiobutton-icon').removeClass('ui-icon ui-icon-bullet');
    },
    select: function(radio) {
        this.checkedRadio = radio;
        radio.addClass('ui-state-active').children('.ui-radiobutton-icon').addClass('ui-icon ui-icon-bullet');
        radio.prev().children(':radio').prop('checked', true).focus();
    }
});

The major changes were:

  1. Added a case for keyCode.SPACE on keydown binding and
  2. Added and extra .focus() when selecting the radio on select function.

I hope that can help you.

Upvotes: 2

Related Questions