Приклади випадаючих списків

Набір атрибутів із "label"

Атрибут "aria-labelledby" забезпечує обов'язкову вимогу WCAG.
<!-- Відповідний код HTML виглядає так -->
<p id="label-1">Оберіть варіант</p>
<select class="select_common" id="input-select-1" aria-labelledby="label-1">
    <option value="var_1">Варіант 1</option>
    <option value="var_2">Варіант 2</option>
    <option value="var_3">Варіант 3</option>
</select>

Оберіть варіант

Набір атрибутів із "aria-describedby"

Атрибут "aria-describedby" вказує на додаткову підказку, яка буде озвучена скрінрідером.
<!-- Відповідний код HTML виглядає так -->
<p id="label-2">Оберіть варіант</p>
<p class="sr-only" id="description-2">Оберіть одну опцію з цього списку</p>
<select class="select_common" id="input-select-1" aria-labelledby="label-2" aria-describedby="description-2">
    <option value="var_1">Варіант 1</option>
    <option value="var_2">Варіант 2</option>
    <option value="var_3">Варіант 3</option>
</select>

<style>
.sr-only {
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    height: 1px;
    overflow: hidden;
    position: absolute;
    white-space: nowrap;
    width: 1px;
}
</style>

Оберіть варіант

Оберіть одну опцію з цього списку

Можливість множинного вибору

Цей кастомний елемент, щоб відповідати вимогам вебдоступності,
повинен мати наступні aria-атрибути.
<!-- Відповідний код HTML виглядає так -->
<p id="label-3">Оберіть потрібні варіанти їжі зі списку</p>
<div class="multi-dropdown" id="multi-dropdown-1">
    <button class="multi-dropdown-button" id="multi-dropdown-button" aria-haspopup="listbox" aria-expanded="false" aria-labelledby="label-3 multi-dropdown-button-text">
        <div style="display: flex; flex-direction: row; justify-content: space-between; align-items: center">
            <div id="multi-dropdown-button-text">Не обрано</div>
            <div aria-hidden="true"></div>
        </div>
    </button>
    <ul class="multi-dropdown-options" role="listbox" aria-label="Список варіантів їжі" aria-multiselectable="true" id="multi-dropdown-options" tabindex="-1">
        <li role="option" class="multi-dropdown-option" tabindex="0" aria-selected="false">Фрукти</li>
        <li role="option" class="multi-dropdown-option" tabindex="0" aria-selected="false">Овочі</li>
        <li role="option" class="multi-dropdown-option" tabindex="0" aria-selected="false">Горіхи</li>
        <li role="option" class="multi-dropdown-option" tabindex="0" aria-selected="false">Напої</li>
    </ul>
</div>

<style>
.multi-dropdown {
    position: relative;
    display: inline-block;
    width: 200px;
}

.multi-dropdown-button {
    background-color: #fafafa;
    color: #000;
    border: 1px solid #000;
    width: 100%;
    padding: 10px;
    cursor: pointer;
    text-align: left;
}.multi-dropdown-button:focus {
    outline: 2px solid #4444FF;
    outline-offset: 2px;
}

.multi-dropdown-options {
    display: none;
    background-color: white;
    border: 1px solid #ccc;
    list-style-type: none;
    padding: 0px;
    position: absolute;
    top: 0px;
    left: 0px;
    right: 0px;
    width: 100%;
    z-index: 1;
}

.multi-dropdown-option {
    padding: 10px;
    cursor: pointer;
    background-color: #fafafa;
    border-bottom: 1px solid #ddd;
}.multi-dropdown-option:hover {
    filter: brightness(0.9);
}.multi-dropdown-option[aria-selected="true"] {
    background-color: #e0f7fa;
}.multi-dropdown-option:focus {
    outline: 2px solid #4444FF;
    outline-offset: 2px;
}
</style>

<script>
/*
Увага: цей код показаний як орієнтовний приклад реалізації поведінки елемента.
*/

const multiDropdownButton = document.getElementById("multi-dropdown-button");
const multiDropdownButtonText = document.getElementById("multi-dropdown-button-text");
const multiDropdownOptions = document.getElementById("multi-dropdown-options");
const options = document.querySelectorAll(".multi-dropdown-option");

// відкриття/закриття випадаючого списку:
multiDropdownButton.addEventListener("click", () => {
    MultiDropdown.toggleOptionsList();
});

// закриття списку при натисканні за його межами:
document.addEventListener("click", (event) => {
    if (!multiDropdownButton.contains(event.target) && !multiDropdownOptions.contains(event.target)) {
        multiDropdownButton.setAttribute("aria-expanded", "false");
        multiDropdownOptions.setAttribute("aria-expanded", "false");
    }
});

// навігація між елементами (клавіатура/миша):
options.forEach((option, index) => {
    option.addEventListener("keydown", (event) => {
        switch (event.key) {
            case "ArrowDown":
            case "ArrowRight":
    event.preventDefault();
    const nextOption = options[index + 1] || options[0];
    nextOption.focus();
    break;
            case "ArrowUp":
            case "ArrowLeft":
    event.preventDefault();
    const prevOption = options[index - 1] || -1;
    if (prevOption == -1) {
        MultiDropdown.toggleOptionsList("close");
        multiDropdownButton.focus();
    } else {
        prevOption.focus();
    }
    break;
            case "Escape":
    MultiDropdown.toggleOptionsList("close");
            case " ":
            case "Enter":
    event.preventDefault();
    MultiDropdown.toggleOption(option);
    break;
        }
    });
    
    option.addEventListener("click", () => {
        MultiDropdown.toggleOption(option);
    });
});

class MultiDropdown
{
    static signChecked = " ✔";
    static signCheckedRegExp = new RegExp(MultiDropdown.signChecked + "$");
    
    static init() {
        this.updateTextLabel();
    }
    
    static recalculateOptionsListPosition() {
        multiDropdownOptions.style.top = multiDropdownButton.getBoundingClientRect().height;
    }
    
    static toggleOptionsList(force="no") {
        const expanded = multiDropdownButton.getAttribute("aria-expanded") === "true" || false;
        if (expanded || force === "close") {
            multiDropdownButton.setAttribute("aria-expanded", "false");
            multiDropdownOptions.setAttribute("aria-expanded", "false");
            multiDropdownOptions.style.display = "none";
            multiDropdownButton.focus();
        } else {
            multiDropdownButton.setAttribute("aria-expanded", "true");
            multiDropdownOptions.setAttribute("aria-expanded", "true");
            multiDropdownOptions.style.display = "block";
            options[0].focus();
        }
        
        this.recalculateOptionsListPosition();
    }
    
    static toggleOption(option) {
        const isSelected = option.getAttribute("aria-selected") === "true";
        if (isSelected) {
            option.setAttribute("aria-selected", "false");
            option.innerText = option.innerText.replace(this.signCheckedRegExp, "");
        } else {
            option.setAttribute("aria-selected", "true");
            option.innerText = option.innerText + this.signChecked;
        }
        
        this.updateTextLabel();
    }
    
    static updateTextLabel() {
        const selectedOptions = Array.from(options).filter(option => option.getAttribute("aria-selected") === "true");
        const selectedText = selectedOptions.map(option => option.textContent.replace(this.signCheckedRegExp, "")).join(", ");
        multiDropdownButtonText.innerText = (selectedOptions.length > 0) ? `Обрано: ${selectedText}` : "Не обрано";
        this.recalculateOptionsListPosition();
    }
}

MultiDropdown.init();
</script>

Оберіть потрібні варіанти їжі зі списку

  • Фрукти
  • Овочі
  • Горіхи
  • Напої