קודקודייל
  • קודקודייל
  • מי אתם קודקודייל?
  • קורסים בחינם
  • צרו קשר
  • בניית אתרים
    • וורדפרס
  • נגישות אתרים
  • כל הקטגוריות
    • אנגולר
    • HTML
    • CSS
    • Javascript
    • Typescript
    • NodeJs
    • בלוקציין
  • קודקודייל
  • מי אתם קודקודייל?
  • קורסים בחינם
  • צרו קשר
  • בניית אתרים
    • וורדפרס
  • נגישות אתרים
  • כל הקטגוריות
    • אנגולר
    • HTML
    • CSS
    • Javascript
    • Typescript
    • NodeJs
    • בלוקציין
קודקודייל
  • קודקודייל
  • מי אתם קודקודייל?
  • קורסים בחינם
  • צרו קשר
  • בניית אתרים
    • וורדפרס
  • נגישות אתרים
  • כל הקטגוריות
    • אנגולר
    • HTML
    • CSS
    • Javascript
    • Typescript
    • NodeJs
    • בלוקציין
  • קודקודייל
  • מי אתם קודקודייל?
  • קורסים בחינם
  • צרו קשר
  • בניית אתרים
    • וורדפרס
  • נגישות אתרים
  • כל הקטגוריות
    • אנגולר
    • HTML
    • CSS
    • Javascript
    • Typescript
    • NodeJs
    • בלוקציין
ראשי ♦ Javascript ♦ רכיב: שדה בחירה (סלקט) נגיש ב-JS

רכיב: שדה בחירה (סלקט) נגיש ב-JS

עידן יצחקי 3 במאי 2024 אין תגובות

לבנות סלקט ידנית זה הבסיס לרכיבים כמו בחירה מרובה, השלמה מתוך אופציות ועוד הרבה סוגים של בחירות.

לכן, אם נדע איך לבנות אותו, נוכל בשינויים נוספים לבנות את כל השאר.

אם כבר אנחנו בונים סלקט אז שיהיה גם נגיש (כי יש לזה חשיבות גדולה עבור המשתמשים שלנו).

אני אבנה אותו ב-javascript נקי לחלוטין כך שיהיה כל להטמיע אותו בכל מקום.

<div class="select-wrapper" id="random_id">
        <button class="select-btn" onkeydown="accessibilitySelect.onEsc(event)"
            onkeyup="accessibilitySelect.onKeyUp(event)" onclick="accessibilitySelect.onClick(event)"
            aria-controls="random_list_id" aria-haspopup="listbox" role="combobox" aria-expanded="false">בחר</button>

        <div class="select-list-wrapper" id="random_list_id" role="listbox">
            <ul class="select-list">
                <li class="select-item" tabindex="-1" role="option" onmouseenter="accessibilitySelect.itemHover(event)"
                    data-value="1" aria-selected="false" onclick="accessibilitySelect.optionClick(event)"><span
                        class="select-option">1</span></li>

                <li class="select-item" tabindex="-1" role="option" onmouseenter="accessibilitySelect.itemHover(event)"
                    data-value="2" aria-selected="false" onclick="accessibilitySelect.optionClick(event)"><span
                        class="select-option">2</span></li>

                <li class="select-item" tabindex="-1" role="option" onmouseenter="accessibilitySelect.itemHover(event)"
                    data-value="3" aria-selected="false" onclick="accessibilitySelect.optionClick(event)"><span
                        class="select-option">3</span></li>

                <li class="select-item" tabindex="-1" role="option" onmouseenter="accessibilitySelect.itemHover(event)"
                    data-value="4" aria-selected="false" onclick="accessibilitySelect.optionClick(event)"><span
                        class="select-option">4</span></li>

                <li class="select-item" tabindex="-1" role="option" onmouseenter="accessibilitySelect.itemHover(event)"
                    data-value="5" aria-selected="false" onclick="accessibilitySelect.optionClick(event)"><span
                        class="select-option">5</span></li>

                <li class="select-item" tabindex="-1" role="option" onmouseenter="accessibilitySelect.itemHover(event)"
                    data-value="1a" aria-selected="false" onclick="accessibilitySelect.optionClick(event)"><span
                        class="select-option">1a</span></li>

                <li class="select-item" tabindex="-1" role="option" onmouseenter="accessibilitySelect.itemHover(event)"
                    data-value="2a" aria-selected="false" onclick="accessibilitySelect.optionClick(event)"><span
                        class="select-option">2a</span></li>

                <li class="select-item" tabindex="-1" role="option" onmouseenter="accessibilitySelect.itemHover(event)"
                    data-value="3a" aria-selected="false" onclick="accessibilitySelect.optionClick(event)"><span
                        class="select-option">3a</span></li>

                <li class="select-item" tabindex="-1" role="option" onmouseenter="accessibilitySelect.itemHover(event)"
                    data-value="4a" aria-selected="false" onclick="accessibilitySelect.optionClick(event)"><span
                        class="select-option">4a</span></li>

                <li class="select-item" tabindex="-1" role="option" onmouseenter="accessibilitySelect.itemHover(event)"
                    data-value="5a" aria-selected="false" onclick="accessibilitySelect.optionClick(event)"><span
                        class="select-option">5a</span></li>

            </ul>
        </div>
    </div>

זה מבנה הסלקט שלנו , כעט ניתן לו מראה יותר דומה לסלקט

 .select-wrapper {
        display: inline-block;
        position: relative;
    }

    .select-btn {
        min-width: 100px;
        padding: 4px 10px;
        border: 1px solid #ededed;
        border-radius: 5px;
        background-color: transparent;
        text-align: right;
    }

    .select-btn::after {
        content: '\2B9F';
        position: absolute;
        left: 4px;
        top: 2px;
    }

    .select-wrapper.active .select-btn::after {
        transform: rotate(180deg);
    }

    .select-list-wrapper {
        width: 100%;
        position: absolute;
        left: 0;
        top: 100%;
        box-shadow: 1px 1px 4px #ededed;
        display: none;
    }

    .select-wrapper.active .select-list-wrapper {
        display: block;
    }

    .select-list {
        list-style-type: none;
        text-align: right;
        padding: 4px;
        margin: 0;
        max-height: 100px;
        overflow: auto;
    }

    .select-item {
        padding: 2px 0;
    }

    .select-item[aria-selected="true"] {
        background-color: lightblue;
    }

אין כאן משהו יותר מידי מיוחד, מדובר בעיצוב בסיסי רק כדי שהרכיב שלנו יראה כמו סלקט.

עכשיו זה הזמן לגרום לו גם לתפקד כמו סלקט אמיתי

const accessibilitySelect = new Object();
accessibilitySelect.selectWrapper = document.getElementById('random_id');
accessibilitySelect.selectMainBtn = accessibilitySelect.selectWrapper.getElementsByClassName('select-btn')[0];
accessibilitySelect.selectedItemValue = null;
accessibilitySelect.temporerSelectedItemValue = null;
accessibilitySelect.selectItems = Array.from(accessibilitySelect.selectWrapper.getElementsByClassName('select-item'));

accessibilitySelect.onKeyUp = (event) => {
    if (event.keyCode === 13 || event.which === 13 ||
        event.keyCode === 32 || event.which === 32) {
        accessibilitySelect.selectWrapper.classList.toggle('active');
        accessibilitySelect.selectMainBtn.setAttribute('aria-expanded',
            accessibilitySelect.boolStringToggler(accessibilitySelect.selectMainBtn.getAttribute('aria-expanded')));
        accessibilitySelect.onToggleOptionsList();
    } // 13 enter and 32 space
    accessibilitySelect.keyboardMove(event);
}



accessibilitySelect.onEsc = (event) => {
    if (event.keyCode === 27 || event.which === 27) {
        accessibilitySelect.selectWrapper.classList.remove('active');
        accessibilitySelect.selectMainBtn.setAttribute('aria-expanded', 'false');
        accessibilitySelect.temporerSelectedItemValue = null;
    }
}


accessibilitySelect.onClick = (event) => {
    if (event.type === 'click' && event.pointerType === 'mouse') {
        accessibilitySelect.selectWrapper.classList.toggle('active');
        accessibilitySelect.selectMainBtn.setAttribute('aria-expanded',
            accessibilitySelect.boolStringToggler(accessibilitySelect.selectMainBtn.getAttribute('aria-expanded')));
        accessibilitySelect.onToggleOptionsList();
    }
}


accessibilitySelect.optionClick = (event) => {
    accessibilitySelect.temporerSelectedItemValue = event.target.dataset.value;
    accessibilitySelect.selectWrapper.classList.toggle('active');
    accessibilitySelect.selectMainBtn.setAttribute('aria-expanded',
        accessibilitySelect.boolStringToggler(accessibilitySelect.selectMainBtn.getAttribute('aria-expanded')));
    accessibilitySelect.onToggleOptionsList();
}


accessibilitySelect.itemHover = (event) => {
    accessibilitySelect.temporerSelectedItemValue = event.target.dataset.value;
    accessibilitySelect.syncTempSelectedItem();
}


accessibilitySelect.syncTempSelectedItem = () => {
    accessibilitySelect.selectItems.forEach(item => {
        item.setAttribute('aria-selected', item.dataset.value === accessibilitySelect.temporerSelectedItemValue);
    });
}


accessibilitySelect.keyboardMove = (event) => {
    const itemIdx = accessibilitySelect.selectItems.findIndex(item => item.getAttribute('aria-selected') === 'true');


    if (event.keyCode === 38 || event.which === 38) {
        if (itemIdx === -1 || itemIdx === 0) {
            accessibilitySelect.temporerSelectedItemValue = accessibilitySelect.selectItems[0].dataset.value;
            accessibilitySelect.selectItems[0].scrollIntoView();
        } else {
            accessibilitySelect.temporerSelectedItemValue = accessibilitySelect.selectItems[itemIdx - 1].dataset.value;
            accessibilitySelect.selectItems[itemIdx - 1].scrollIntoView();
        }
        accessibilitySelect.syncTempSelectedItem();
    } // UP


    if (event.keyCode === 40 || event.which === 40) {
        if (itemIdx === -1) {
            accessibilitySelect.temporerSelectedItemValue = accessibilitySelect.selectItems[0].dataset.value;
            accessibilitySelect.selectItems[0].scrollIntoView();
        } else {
            if (itemIdx === (accessibilitySelect.selectItems.length - 1)) {
                accessibilitySelect.temporerSelectedItemValue = accessibilitySelect.selectItems[itemIdx].dataset.value;
                accessibilitySelect.selectItems[itemIdx].scrollIntoView();
            } else {
                accessibilitySelect.temporerSelectedItemValue = accessibilitySelect.selectItems[itemIdx + 1].dataset.value;
                accessibilitySelect.selectItems[itemIdx + 1].scrollIntoView();
            }
        }
        accessibilitySelect.syncTempSelectedItem();
    } // DOWN
}


accessibilitySelect.updateSelectedItem = () => {
    if (accessibilitySelect.temporerSelectedItemValue !== null) {
        accessibilitySelect.selectedItemValue = accessibilitySelect.temporerSelectedItemValue;
        accessibilitySelect.selectWrapper.getElementsByClassName('select-btn')[0].innerText = accessibilitySelect.selectItems.find(item =>
            item.dataset.value === accessibilitySelect.selectedItemValue).children[0].innerText;
    }
}


accessibilitySelect.onToggleOptionsList = () => {
    if (accessibilitySelect.selectWrapper.classList.contains('active')) {
        if (accessibilitySelect.selectedItemValue !== null) {
            accessibilitySelect.temporerSelectedItemValue = accessibilitySelect.selectedItemValue;
            accessibilitySelect.syncTempSelectedItem();
        }
    } else {
        accessibilitySelect.updateSelectedItem();
    }
}


accessibilitySelect.boolStringToggler = (checker) => {
    if (checker === "true") {
        return "false";
    }
    return "true";
}

אם צורת הכתיבה הזו לא ברורה, אז אני אסביר מה היתרון שלה.

בשורה 1 : יצרנו אוביקט, הוא יחזיק את הכל, יהיו בו כל הפונקציות וכל המשתנים.

זה אומר, שכל עוד אף אחד לא ידרוס את האוביקט עם אוביקט באותו השם אין סכנה לדליפות.

לא מסקריפטים אחרים ולא מהסקריפט שלנו לפונקציות אחרות.

אנחנו תמיד נפנה לאוביקט ומשם לפונקציה או למשתנה שאנחנו רוצים. כך אנחנו יכולים להקים משתנים גלובלים לסקריפט שלנו מבלי שהם יהיו גלובלים לכל האתר.

אם ירצו לגשת אליהם מבחוץ, זה אכן אפשרי, אבל רק דרך האוביקט.

בשאר השורות ניתן לראות פונקציות שמטפלות בלחיצות מיקלדת כמו esc או רווח ו-enter , כמו כן יש גם טיפול בחצים למעלה ולמטה.

שימו לב, שהפוקוס אף פעם לא עוזב את הכפתור פתיחה/סגירה, אנחנו רק מסמלצים פוקוס ובחירה על אחד האופציות של רכיב הבחירה.

ניתן לראות איך הפונקציות קוראות אחת לשניה בתוך הסקריפט, אחד הדברים החשובים כאן הוא להשתדל ליצור פונקציות אשר כל אחת אחראית על פעולה מסויימת ואם צריך, היא תקרא לפונקציה אחרת שעושה פעולה נוספת.

כך השליטה שלנו עולה בקוד ובמידה שצריך לעשות שינוי , יהיה יותר קל.

הרכיב נבדק על מחשב עם ובלי קורא מסך, במקביל הוא נבדק בנייד עם קורא מסך והכל עובד ומקריא תקין.

אפשר להוסיף למי שרוצה, פונקציונליות שכאשר לוחצים בחוץ אז הסלקט ייסגר.

פוסטים קשורים:

תמונת אווירה של דפי הודעותרכיבים נגישים: טאבים תמונת אווירה של רשימת סימוןרכיבים: בניה של רכיב MULTI-SELECT נגיש באנגולר תמונת אווירה של מטריה כתומהרכיבים: בניה של רכיב SELECT נגיש באנגולר תמונת אווירה של אופציותרכיבים: בניה של רכיב AUTOCOMPLETE נגיש באנגולר
javascript נגישות

אודות המחבר

עידן יצחקי להציג את כל הפוסטים של עידן יצחקי


« פוסט קודם
פוסט הבא »

השארת תגובה

ביטול

חיפוש באתר
בחירת העורכים
29 בדצמבר 2023 עידן יצחקי

שדה טקסט עשיר עם תמונות

אתם הולכים להיות מופתעים עד כמה HTML יכול להיות חכם ולבצע משהו כל כך מורכב, שאם אנחנו היינו רוצים ליצור

1 באוקטובר 2021 עידן יצחקי

איך למשוך דינמית favicon של אתרים אחרים ב-JS

בפוסט זה נראה איך אפשר על פי לינקים בדף למשוך את ה-favicon מהדומיין שלהם באופן דינמי, בדיקה של תקינות התמונה

פופולרי
Javascript functions – היכרות עם סוגי פונקציות
Javascript
21 בדצמבר 2024 אין תגובות
Nested routing in angular standalone component
Typescript
15 בנובמבר 2024 תגובה אחת
בחרו לפי תגיות
angular blockchain css ethers express front-end fullstack GQL html javascript next js nextjs nodejs react hooks reactjs solidity webgl אנגולר בלוקציין וורדפרס לימודי אנגולר לימודי וורדפרס לימוד ריאקט מדריך front-end מדריך GQL מדריך אנגולר מדריך וורדפרס מדריך חינם react מדריך ריאקט מפתח בלוק מפתח בלוקציין מתכנת front-end מתכנת בלוקציין מתכנת פרונט סולידיטי קורס front end קורס fullstack קורס nextjs קורס אנגולר קורס בלוקציין קורס בלוקציין בחינם קורס סולידיטי קורס ריאקט קורס תכנות קורס תכנות בחינם
סינון על פי קטגוריות
CSS fullstack HTML IIS Javascript nodeJs SEO Typescript אנגולר בלוקציין בניית אתרים וורדפרס חיפוש עבודה כלים נוספים כללי נגישות קורסים ריאקט תלת מימד תקלות ופתרונות
צור קשר
כל הזכויות שמורות לקודקודייל
ליצירת קשר: @ קודקודייל
גלילה לראש העמוד