jquery ui 위젯을 사용해서 select box에 대한 커스텀을 쉽게 제작할 수 있습니다. 심지어 aria와 관련된 웹 접근성도 자동으로 추가되고 사용자의 액션에 따라 해당 값이 상황에 맞게 변경되기 때문에 접근성과 관련해서도 사용하기 좋다고 생각했습니다.
단점이라면 jquery 라이브러리 자체의 무거운 점을 빼면 문제가 없을 줄 알았으나 구글에서 지원하는 lighthouse에서 해당 위젯을 사용한 페이지를 검사 해보면 접근성과 관련하여 문제가 발견할 수 있었습니다
입력 필드에 엑세스 가능한 이름이 없고 role의 하위 요소에 필수로 등록되어야 할 하위 목록이 없다는 경고인데
위젯으로 생성되는 버튼 태그인 span에서 문제가 발생하여 원인을 찾아 해결하였습니다.
해당 작업은 Vue를 사용하여 Vue관련 속성이 같이 들어가 있지만 ui select 이슈와 무관하기 때문에 신경쓰지 않으셔도 됩니다.
// 위젯 적용 전 마크업
<select ref="deliveryMemo">
<option v-for="(item, index) in memoList" :key="index" :value="item.id">{{ item.name }}</option>
</select>
<div id="delivery-memolist-wrap"></div>
ui select 위젯을 적용하면 위에 마크업에서 아래 마크업처럼 dom에 자동으로 html tag가 추가됩니다. 또한 aria속성과 class 등 여러 속성들이 기본 값으로 들어가게 됩니다.
기존에 select는 display:none 처리가 되고 span으로 만들어진 버튼 태그와 ul과 li로 만들어진 리스트가 select와 option 태그를 대체합니다.
// ui select 위젯 적용 후 마크업
<select ref="deliveryMemo">
<option v-for="(item, index) in memoList" :key="index" :value="item.id">{{ item.name }}</option>
</select>
// 위젯으로 인한 버튼이 생성
<span tabindex="0" id="delivery-memo-list-button" role="combobox" aria-expanded="true" aria-autocomplete="list" aria-owns="delivery-memo-list-menu" aria-haspopup="listbox" class="ui-selectmenu-button ui-button ui-widget ui-selectmenu-button-open ui-corner-top" aria-activedescendant="ui-id-16" aria-labelledby="ui-id-16" aria-disabled="false">
<span class="ui-selectmenu-icon ui-icon ui-icon-triangle-1-s"></span>
<span class="ui-selectmenu-text">경비(관리)실에 맡겨주세요</span> // 현재 선택된 list
</span>
// select의 option을 토대로 list가 생성됨
<div id="delivery-memolist-wrap">
<div class="ui-selectmenu-menu ui-front ui-selectmenu-open">
<ul aria-hidden="false" aria-labelledby="delivery-memo-list-button" id="delivery-memo-list-menu" role="listbox" tabindex="0" class="ui-menu ui-corner-bottom ui-widget ui-widget-content" aria-activedescendant="ui-id-16" aria-disabled="false" style="width: 222px;">
<li class="ui-menu-item">
<div id="ui-id-11" tabindex="-1" role="option" class="ui-menu-item-wrapper">배송시 요청사항을 선택해주세요</div>
</li>
<li class="ui-menu-item">
<div id="ui-id-12" tabindex="-1" role="option" class="ui-menu-item-wrapper">문 앞에 놓아주세요</div>
</li>
<li class="ui-menu-item">
<div id="ui-id-13" tabindex="-1" role="option" class="ui-menu-item-wrapper">경비(관리)실에 맡겨주세요</div>
</li>
<li class="ui-menu-item">
<div id="ui-id-14" tabindex="-1" role="option" class="ui-menu-item-wrapper">택배함에 넣어주세요</div>
</li>
<li class="ui-menu-item">
<div id="ui-id-15" tabindex="-1" role="option" class="ui-menu-item-wrapper">직접 받겠습니다</div>
</li>
<li class="ui-menu-item">
<div id="ui-id-16" tabindex="-1" role="option" class="ui-menu-item-wrapper ui-state-active">직접입력</div>
</li>
</ul>
</div>
</div>
// select에 ui selectmenu 위젯을 적용
<script>
$(this.$refs.deliveryMemo).selectmenu({
// select의 option list 생성 위치
appendTo : '#delivery-memolist-wrap',
})
</script>
위에 위젯으로 인하여 생긴 태그를 보면 role="combobox"으로 지정된 span 태그와 관련된 수정을 몇개 해주면 해결이 가능합니다.
입력 필드에 대한 엑세스 가능한 이름을 지정해주는 것 입니다.
combobox버튼.setAttribute('aria-label', '버튼에 대한 설명')
role="combobox" 지정 시 input text가 필수로 사용 되어야 하기 때문에 접근성 관련하여 문제가 생긴 것입니다. 따라서 새로 추가한 input text에 aria 맞추어 list와 연결하기 위해 option list인 ul태그를 aria-controls로 지정해줘야 합니다. 또한 list의 값이 자동으로 들어가기 때문에 aria-autocomplete 속성으로 자동완성 기능을 지원하는 것을 명시해야 합니다.
// 변경된 마크업
// input이 추가되고 aria-label이 추가되어 접근성에 위배되지 않는다.
<span tabindex="0" id="delivery-memo-list-button" role="combobox" aria-label="배송메모 리스트 버튼" aria-expanded="true" aria-autocomplete="list" aria-owns="delivery-memo-list-menu" aria-haspopup="listbox" class="ui-selectmenu-button ui-button ui-widget ui-selectmenu-button-open ui-corner-top" aria-activedescendant="ui-id-16" aria-labelledby="ui-id-16" aria-disabled="false">
<span class="ui-selectmenu-icon ui-icon ui-icon-triangle-1-s"></span>
<span class="ui-selectmenu-text">직접입력</span>
<input type="text" aria-autocomplete="list" aria-controls="delivery-memo-list-menu" />
</span>
let button = document.querySelector('#delivery-memo-list-button')
let memoList = document.querySelector('#delivery-memo-list-menu')
// btn - selectmenu로 생성된 버튼
// memolist - selectmenu로 생성된 list ul태그
const selcAriaAdd = (btn, memolist) => {
let inputTag = document.createElement('input')
inputTag.setAttribute('type', 'text')
inputTag.setAttribute('aria-autocomplete', 'list')
inputTag.setAttribute('aria-controls', memolist)
// 위젯에서 기본적으로 true으로 들어가 있지만 목적을 명시하기 위해 listbox로 변경하였습니다.(필수 X)
// listbox는 ARIA 1.1에 추가된 속성 이기 때문에 브라우저 및 스크린 리더기의 지원 범위가 달라 확인 후 사용 해야 합니다.
btn.setAttribute('aria-haspopup', 'listbox')
btn.setAttribute('aria-label', '배송메모 리스트 버튼')
btn.appendChild(inputTag)
}
selcAriaAdd(button,memoList)