عنصر بومی <select> در گذشته بهسختی سبکبندی میشد و فقط میتوانست گزینههای نوشتار ساده را دربرگیرد. ویژگی appearance: base-select روشی اعلانی و فقط CSS برای انتخاب حالت قابلسفارشیسازی برای عنصر <select> ارائه میدهد. این ویژگی به توسعهدهندگان امکان میدهد محتوای HTML غنی (مانند تصاویر، SVG، و چیدمانهای پیچیده) را در عناصر <option> بگنجانند و درعینحال، دسترسیپذیری صفحهکلید بومی و ادغام فرم را حفظ کنند. از این الگو برای جایگزین کردن مؤلفههای انتخاب سنگین و سفارشیسازیشده با عناصر استاندارد و بومی استفاده کنید.
نحوه پیادهسازی
برای پیادهسازی انتخابگر رسانه تعاملی بااستفاده از Customizable Select API:
- موافقت با سبکهای پایه:
appearance: base-selectرا بااستفاده از شبهعنصر::picker(select)هم به عنصر<select>و هم به انتخابگر داخلی آن اعمال کنید. این کار تجزیهکننده HTML مرورگر را برای محتوای داخل<select>تغییر میدهد. - تعریف محتوای دکمه: از عناصر استاندارد
<button>و<selectedcontent>در داخل<select>استفاده کنید تا آنچه را که هنگام بسته شدن انتخابگر نشان داده میشود تعریف کنید. عنصر<selectedcontent>بهطور خودکار محتوای گزینه انتخابشده را منعکس میکند. اگر میخواهید محتوای غنی انتخابشده فعلی را در دکمه نمایش دهید، این مورد لازم است. - استفاده از محتوای غنی در گزینهها: اکنون میتوانید تصاویر، SVG، و سایر برچسبهای HTML را در برچسبهای
<option>قرار دهید. پیشاز این میانای برنامهسازی کاربردی، مرورگر برچسبها را از برچسبهای<option>حذف میکرد و فقط نوشتار ساده را ارائه میداد. - سبکبندی Popover: با هدف قرار دادن شبهعنصر
::picker(select)، فهرست گزینههای کرکرهای را سبکبندی کنید. این عنصر در لایه بالایی ارائه میشود، یعنی نیازی نیست باz-indexکلنجار بروید. گزینهها بااستفاده از Anchor Positioning API بهصورت بومی جایگذاری میشوند.
کد نمونه: انتخابگر نقش غنی
<label for="role-picker">Select your role</label>
<select class="custom-select" name="role" id="role-picker">
<button>
<selectedcontent></selectedcontent> <!-- Mirrors the selected option's content automatically so you do not need JS to update the button -->
</button>
<!-- Define concise aria-label values on options whose mirrored rich content would otherwise read awkwardly as a concatenated string -->
<option value="frontend" aria-label="Frontend Developer">
<svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect>
</svg>
<div class="option-text">
<span class="option-title">Frontend Developer</span>
<span class="option-desc">React, Vue, CSS</span>
</div>
</option>
<option value="backend" aria-label="Backend Developer">
<svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<rect x="2" y="12" width="20" height="14" rx="2" ry="2"></rect>
</svg>
<div class="option-text">
<span class="option-title">Backend Developer</span>
<span class="option-desc">Node.js, Python</span>
</div>
</option>
</select>
select.custom-select,
select.custom-select::picker(select) {
appearance: base-select; /* MUST opt-in both the select and its picker to enable the customizable state, otherwise browser standard rendering applies */
}
select.custom-select {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 12px;
background-color: #1e293b;
border: 1px solid #334155;
border-radius: 8px;
color: #f1f5f9;
cursor: pointer;
}
select.custom-select::picker(select) {
background-color: #0f172a;
border: 1px solid #334155;
border-radius: 8px;
padding: 8px;
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.5);
width: anchor-size(width); /* Uses Anchor Positioning API to keep the dropdown precisely aligned to the button trigger width */
}
select.custom-select option {
display: flex;
align-items: center;
gap: 12px;
padding: 10px;
border-radius: 4px;
cursor: pointer;
}
select.custom-select option:hover {
background-color: #1e293b;
}
/* Remove standard OS checkmark for base-select */
select.custom-select option::before {
display: none;
}
/* MANDATORY: Provide multiple visual indicators (e.g., prominent background color and bold title font) to communicate the checked state cleanly */
select.custom-select option:checked {
background-color: #3b82f6;
color: #ffffff;
}
select.custom-select option:checked .option-title {
font-weight: 700;
}
پیادهسازی استراتژیک و روالهای مطلوب
- باید از
appearance: base-selectاستفاده کنید وقتی به چیدمانهای پیچیده گزینهها (نمادها، توضیحات، تصاویر) نیاز دارید که قبلاً فقط با چارچوبهای سنگین واسط کاربر جاوا اسکریپت امکانپذیر بود. - اگر متوجه تأخیر در عملکرد شدید، از عناصر موقت استفاده نکنید؛ مرورگر تمرکز صفحهکلید بومی را بهصورت بومی مدیریت میکند.
- باید پرداز زدن لایه بالایی را درنظر بگیرید. انتخابگر در لایه بالایی ارائه میشود، یعنی
z-indexنسبی محتوای صفحه را لغو میکند. - باید جزئیات ثانویه (مانند توضیحات) را در وضعیت دکمه پنهان کنید اگر فضای زیادی اشغال میکنند، بااستفاده از سبکدهی
.custom-select selectedcontent .option-desc { display: none; }. - رفتار چیدمان را آزمایش کنید. تنظیم
appearance: base-selectرفتار پیشفرض مرورگر را برای اندازهگیری انتخاب براساس طولانیترین عرض گزینه حذف میکند. ممکن است لازم باشد عرض ثابتی تنظیم کنید یا از محدودیتهای flex/grid برای جلوگیری از تغییرات طرحبندی استفاده کنید. - مطمئن شوید
<select>دارای مشخصهnameو<label>مرتبط باشد. این کار تضمین میکند که حتی با واسط کاربر سفارشی، مؤلفه برای صفحهخوانها دسترسپذیر باقی بماند و با ارسالهای فرم استاندارد بهدرستی کار کند. - از مشخصههای استاندارد
valueدر گزینهها استفاده کنید تا مطمئن شوید ارسال فرم دقیقاً مثل قبل کار میکند.
راهبردهای جایگزین
جایگزینها و پشتیبانی مرورگر برای <select> قابلسفارشیسازی
برای مرورگرهایی که هنوز از appearance: base-select پشتیبانی نمیکنند، عنصر <select> بهطور مناسب به منو کرکرهای سیستمعامل استاندارد تنزل مییابد.
- نادیده گرفتن محتوای غیرنوشتاری: مرورگرهای قدیمیتر تگهای HTML (مثل
<svg>یا<div>) را در تگهای<option>حذف میکنند و فقط گرههای نوشتاری را ارائه میدهند. مطمئن شوید محتوای نوشتاری<option>بهتنهایی خوانا و معنادار باشد. - مدیریت ساختار HTML: تجزیهکنندههای استاندارد ممکن است برچسبهای
<button>و<selectedcontent>را در<select>نادیده بگیرند یا آنها را نامعتبر تلقی کنند. اگر نوشتار استاندارد را بهعنوان جایگزین خوانا درنظر بگیرید، برای ارتقای پیشرو به شبیهسازهای مرورگر جاوا اسکریپت سنگین نیازی نیست.
document.addEventListener("DOMContentLoaded", () => {
// Check if browser supports base-select value
if (!CSS.supports("appearance", "base-select")) {
// Custom select overrides are not supported natively.
}
});