javascripthtmlcssvuejs3tailwind-css

Expand select option from other element


Background:

I was trying to do some custom styling for my select element by changing the color and arrow example:

<select :id="'select' + index" class="w-full px-4 py-2 pr-8 bg-theme text-white rounded-md hover:bg-theme/50 appearance-none" @focus="dropdownOpen = true" @blur="dropdownOpen = false">
  <option value="">Select</option>
  <option v-for="subject in subjects" :key="subject" :value="subject">{{ subject }}</option>
</select>
<span class="absolute right-2 top-1/2 transform -translate-y-1/2 text-white cursor-default" @click.stop="toggleDropdown(index)">
    &#9660;
</span>

const dropdownOpen = ref(false)
const toggleDropdown = (index: number) => {
    dropdownOpen.value = !dropdownOpen.value;
    if (dropdownOpen.value && document.getElementById(`select${index}`)) {
        const evt = new MouseEvent('click')
        document.getElementById(`select${index}`)?.dispatchEvent(evt)
    }
}

Problem:

Currently when trying to expand the select option from another element not working out. You can see on the snippet below itself.

function expand() {
    const evt = new MouseEvent('focus')
    const el = document.getElementById(`selectbox`);
    el.dispatchEvent(evt)
}
<select id="selectbox">
  <option id="option1">A</option>
  <option id="option2">B</option>
  <option id="option3">C</option>
</select>


<button id='test' onclick="expand()">abc</button>

How can we expand select element from another element?


Solution

  • With Vue, there is very rarely a need to ever use the Document API (e.g. using getElementById). You should use a template ref instead.

     <!-- id no longer needed -->
    <select
      ref="select"
      class="w-full px-4 py-2 pr-8 bg-theme text-white rounded-md hover:bg-theme/50 appearance-none"
      @focus="dropdownOpen = true"
      @blur="dropdownOpen = false"
    >
    
    const select = useTemplateRef('select')
    

    I assume the select is inside a v-for loop, since you're using index value. When using a template ref inside of a v-for, the template ref will become an array in order to hold the multiple references created inside the loop. Calling the showPicker function is then as easy as:

    const toggleDropdown = (index: number) => {
      select.value[index].showPicker()
    }