7

What I have
A custom DropDown with a filter text input above. The DropDown can be opened independently from the filter text input.

What I want
The intended behavior would be, that the dropdown closes when the filter input loses focus and also when I click with the mouse outside of the DropDown, so that the DropDown loses the focus.

What I tried

  • Bind to the blur event on my root div element in the control, which doesn't fire at all.
  • I also couldn't find anything about internal component methods which I could override.

Code

  <div @blur="onRootLostFocus">
    ...
  </div>

  ...
  ...
  ...

  onRootLostFocus() {
    console.log('LostFocus');
    this.deactivateSearchPanel();
    this.deactivateSelectionPanel();
  }

Solution

I missed, that a div needs tabindex="0" to be focusable, this fixed my problem

2
  • Could you provide some code example?
    – vchan
    Commented Aug 17, 2020 at 9:07
  • @vchan Updated my question accordingly
    – TobiasW
    Commented Aug 17, 2020 at 9:15

2 Answers 2

16

Something like this?

Answer: You need to set tabindex="0" to make it focusable.

Here an custom dropdown how you could do it:

Vue.component("dropdown", {
   props: ["value"],
   data(){
      return {
         open: false,
         options: ["BMW", "Fiat", "Citroen", "Audi", "Tesla"]
      }
   },
   methods: {
      toggleDropdown() {
         this.open = !this.open;
      },
      closeDropdown(){
         this.open = false;
      },
      selectOption(option) {
         this.$emit("input", option);
      }
   },
   template: `<div class="dropdown">
   <div @blur="closeDropdown" tabindex="0" ref="dropdown" @click="toggleDropdown" class="select">
   {{ value }}
   </div>
   <div class="options" :style="{'max-height': open ? '300px' : '0px'}">
      <div v-for="option in options" @mousedown="selectOption(option)" class="option">
         {{ option }}
      </div>
   </div>
</div>`
})

new Vue({
   el: "#app",
   data: {
      selectedCar: "BMW"
   }
})
.dropdown {
   width: 200px;
   position: relative;
}

.select {
   height: 40px;
   position: absolute;
   left: 0;
   width: 100%;
   background: green;
   display: flex;
   justify-content: center;
   align-items: center;
   color: white;
   cursor: pointer;
}
.option {
   width: 100%;
   height: 40px;
   background: darkgreen;
   color: white;
   display: flex;
   justify-content: center;
   align-items: center;
   cursor: pointer;
}
.option:hover {
   background: green;
}
.options {
   overflow: hidden;
   transition: max-height 200ms;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"> <p>
   {{ selectedCar }}</p>
   <dropdown v-model="selectedCar" />
  
</div>

6
  • you dont have a component in there
    – The Fool
    Commented Aug 17, 2020 at 9:28
  • @TheFool something like this? component with custom v-model on it
    – Ilijanovic
    Commented Aug 17, 2020 at 9:38
  • I think, he wants to propagate blur so i don't see why v-model should be required. Look here what I tried regarding his question but it didn't work. I want to $emit blur and listen to that on the parent. codesandbox.io/s/purple-dew-v87py?file=/src/App.vue
    – The Fool
    Commented Aug 17, 2020 at 9:40
  • This is what I want, but I don't see, why my blur event is not triggered on a div, while yours is getting fired.
    – TobiasW
    Commented Aug 17, 2020 at 9:43
  • @TheFool i see. divs need tabindex="0" to be focusable. i have said it in my answer aswell at the top
    – Ilijanovic
    Commented Aug 17, 2020 at 9:43
0

Note there is also tabindex="-1" to make it not reachable via sequential keyboard navigation.

Also consider using a <button> instead because of accessibility concerns.

See https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex

Not the answer you're looking for? Browse other questions tagged or ask your own question.