In VueJs, how to get event @focusout of a complete radio button group

1.5k Views Asked by At

I have built my custom radio button group component, and it mostly works fine. However, I want to emit a "group focusout" event, when the focus leaves outside of any of the contained radio buttons.

However, the @focusout of the buttons all fire individually, and I can not find a way to determine whether the target is outside the group.

Component code

I am basically doing (simplified):

<div v-for="(obj, index) in items" :key="index">      
        <label :for="id + '-' + obj.value">
            <input
                :name="name ? name : id"
                :key="index"
                :id="id + '-' + obj.value"
                :value="obj.value"
                :checked="obj.selected"
                @focusout="onFocusout"
                @change="updateRadiobuttons($event.target.value)"
            />
            {{ $t(obj.text) }}
        </label>       
</div>

In the @focusout handler I do some validation (not shown) and simply pass the event:

private onFocusout(value) {
    console.debug('onFocusout', value);
    //...validation...
    this.$emit('focusout', value);
}

Events emitted

I am logging the received events and watch the log (with Chrome F12)

Here's what I get when I just change the option inside the radio group:

FocusEvent {isTrusted: true, relatedTarget: input#caseBestaDataSearchResultSchule-3.form-control, view: Window, detail: 0, sourceCapabilities: null, …}
bubbles: true
cancelBubble: false
cancelable: false
composed: true
currentTarget: null
defaultPrevented: false
detail: 0
eventPhase: 0
isTrusted: true
path: (20) [input#caseBestaDataSearchResultSchule-2.form-control, label, div.radio.radio--left, div.radio-inline, div.col-sm-9, div.form-group, div.form-group, fieldset, form.form-horizontal, span, div, div, div#content.col-sm-12, div.row, div.container-fluid, div.container.container-main, body.mod.mod-layout.skin-layout-template-contentpage, html, document, Window]
relatedTarget: input#caseBestaDataSearchResultSchule-3.form-control
returnValue: true
sourceCapabilities: null
srcElement: input#caseBestaDataSearchResultSchule-2.form-control
target: input#caseBestaDataSearchResultSchule-2.form-control
timeStamp: 46397.884999983944
type: "focusout"
view: Window {window: Window, self: Window, document: document, name: "", location: Location, …}
which: 0
__proto__: FocusEvent

Here's what I get when I click into the surrounding space:

FocusEvent {isTrusted: true, relatedTarget: null, view: Window, detail: 0, sourceCapabilities: InputDeviceCapabilities, …}
bubbles: true
cancelBubble: false
cancelable: false
composed: true
currentTarget: null
defaultPrevented: false
detail: 0
eventPhase: 0
isTrusted: true
path: (20) [input#caseBestaDataSearchResultSchule-3.form-control, label, div.radio.radio--left, div.radio-inline, div.col-sm-9, div.form-group, div.form-group, fieldset, form.form-horizontal, span, div, div, div#content.col-sm-12, div.row, div.container-fluid, div.container.container-main, body.mod.mod-layout.skin-layout-template-contentpage, html, document, Window]
relatedTarget: null
returnValue: true
sourceCapabilities: InputDeviceCapabilities {firesTouchEvents: false}
srcElement: input#caseBestaDataSearchResultSchule-3.form-control
target: input#caseBestaDataSearchResultSchule-3.form-control
timeStamp: 54147.71499999915
type: "focusout"
view: Window {window: Window, self: Window, document: document, name: "", location: Location, …}
which: 0
__proto__: FocusEvent

How to get a single event @focusout of the whole group?

1

There are 1 best solutions below

0
SampsonM On

I'm assuming that you currently fire the focus event when cycling through each radio using up & down arrows, or selecting another option via touch.

If that is the case the only way you can truly know the focus status of the other radio buttons is to look at them, using ref's to compare each element to document.activeElement or storing each radios focus state.

You could also check the class names if you have any focus classes active.

Heres an example of using the data object to store focus state.

Also worth mentioning you need to use nextTick here or the data object won't have updated before you read the data object.

  new Vue({
el: '#app',
data() {
  return {
    maleFocused: false,
    femaleFocused: false,
    otherFocused: false
  } 
},
methods: {
  focusIn(e) {
    this[`${e.srcElement.id}Focused`] = true
  },
  async focusOut(e) {
    this[`${e.srcElement.id}Focused`] = false

    await this.$nextTick()

    const radioMaleFocused = this.maleFocused
    const radioFemaleFocused = this.femaleFocused
    const radioOtherFocused = this.otherFocused
    
    const radiosFocused = radioMaleFocused || radioFemaleFocused || radioOtherFocused
    
    if (!radiosFocused) {
      console.log('run your code here!')
    }
  }
}
  })
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <fieldset>
<legend>Radios</legend>

<input type="radio" id="male" name="gender" value="male" @focusin="focusIn" @focusout="focusOut">
<label for="male">Male</label><br>

<input type="radio" id="female" name="gender" value="female" @focusin="focusIn" @focusout="focusOut">
<label for="female">Female</label><br>

<input type="radio" id="other" name="gender" value="other" @focusin="focusIn"@focusout="focusOut">
<label for="other">Other</label>
  </fieldset>
</div>