今天来介绍一个实验性的 HTML 表单控件:。它比传统的 元素更容易设计样式。
一、select 控件现状
对于浏览器的内置表单控件(尤其是)是无法自定义外观以适合其网站的设计或用户体验的。根据调查显示,是web开发人员在 CSS 样式设计方面遇到问题最多的控件。当开发人员从头开始重新实现表单控件时,是无法利用浏览器来优化控件的性能、可靠性和可访问性的。提供完全可定制的 就允许 Web 开发人员更改它以适应他们的网站,同时为开发人员节省时间并改善与控件交互的用户体验。
虽然设置按钮的外观是相对容易的,但是设置选项的样式几乎是不可能的:
因此,设计系统和组件库都推出了自己的 select,使用定制的HTML、CSS和 JavaScript来从头开始制作 控件,以便能够与其他组件更好地集成。然而,正确定位弹出式菜单并不容易。多年来,web 开发人员花费了很多时间,试图一次又一次地解决同样的问题。
现在,是时候拥有一个可自定义样式的内置 select 控件了,这样就不需要再编写这些额外的代码了。
二、Open UI
<selectmenu>控件由 OpenUI 提出。Open UI 是一群开发人员、设计人员和浏览器实现者,他致力于解决这个问题,并且在他们解决这个问题的同时,还要解决其他缺失的控件。
Open UI 的目的是最终让 Web 开发人员能够设计和扩展内置 UI 控件(这包括 ,也包括下拉菜单、复选框、单选按钮等)。为了实现这一点,他们制定了有关如何在 Web 平台中实现这些控件以及它们应满足的可访问性要求的规范。
该项目仍处于起步阶段,但进展很快,正如我们将在下面看到的,令人兴奋的事情已经发生。
Open UI 官网:https://open-ui.org/
三、<selectmenu> 控件状态
基于 OpenUI 的 <select> 提议,Chromium 实现了一个新的 <selectmenu> 控件!这项工作由微软 Edge 团队与谷歌 Chrome 团队合作完成。它甚至已经在基于 Chromium 的浏览器(Chrome)中可用。
<selectmenu>是一个新的内置控件,提供选项选择用户体验,就像<select>一样,它有一个显示所选值标签的按钮、单击该按钮时出现的弹出窗口,以及显示的选项列表。
那为什么要起一个新的名字呢?而不直接替换现有的 <select> 控件呢?因为现有的<select> 控件已经在web上使用了很长时间,如果不想引起重大的兼容性问题,那最好不要以任何方式更改它。所以,计划(这一切都是实验性的)是将<selectmenu> 作为一个新控件,独立于<select>。
四、<selectmenu> 控件使用
1. 基本结构
因为 <selectmenu> 的各个部分都可以设置样式,所以了解其内部结构是很重要的。下面就来看看 <selectmenu> 控件的结构:
- <select>:包含按钮和列表框的根元素;
- <button>:触发列表框可见性的按钮元素;
- <selected-value>:显示当前选定选项值的元素(可选)。注意,此部分不一定必须位于该 <button> 部分内;
- <listbox>:包含<option>(s) 和<optgroup>(s) 的包装器;
- <optgroup>:一组<option>和 label(可选);
- <option>:可以有一个或多个,代表用户可以选择的选项值。
2. 默认行为
控件的默认行为和一样使用它,只需使用以下结构:
<selectmenu>
<option>Option 1</option>
<option>Option 2</option>
<option>Option 3</option>
</selectmenu>
当执行此操作时,会默认创建<button>, <selected-value> 和 <listbox>。
3. 设置控件的样式
我们可以使用 CSS中的 ::part() 伪元素来选择控件结构中希望设置样式的部分,以此实现控件样式的修改。下面来尝试设置按钮和列表框部分的样式:
<style>
.my-select-menu::part(button) {
color: white;
background-color: #f00;
padding: 5px;
border-radius: 5px;
}
.my-select-menu::part(listbox) {
padding: 10px;
margin-top: 5px;
border: 1px solid red;
border-radius: 5px;
}
</style>
<selectmenu class="my-select-menu">
<option>Option 1</option>
<option>Option 2</option>
<option>Option 3</option>
</selectmenu>
上述代码效果如下:
::part()可用于为控件的<button>、<selected-value>和<listbox>部分设置样式。
4. 使用自定义标签
如果以上方式还不能满足需求,可以通过使用自定义标签来替换默认标签,并扩展或重新排序部件,从而进一步自定义控件。
<selectmenu> 具有指定的插槽,可以引用这些插槽来替换默认零件。例如,要用自己的按钮替换默认按钮,可以执行以下操作:
<style>
.my-custom-select [slot='button'] {
display: flex;
align-content: center;
}
.my-custom-select button {
padding: 5px;
border: none;
background: #f06;
border-radius: 5px 0 0 5px;
color: white;
font-weight: bold;
}
.my-custom-select .label {
padding: 5px;
border: 1px solid #f06;
border-radius: 0 5px 5px 0;
}
</style>
<selectmenu class="my-custom-select">
<div slot="button">
<button behavior="button">Open</button>
<span class="label">Choose an option</span>
</div>
<option>Option 1</option>
<option>Option 2</option>
<option>Option 3</option>
</selectmenu>
外部 <div> 上的 slot="button" 属性告诉<selectmenu>将其默认按钮替换为<div>的内容。
内部<button>上的 behavior="button" 属性告诉浏览器,这个元素就是我们想要用作新按钮的元素。浏览器会自动将所有点击和键盘处理行为以及适当的可访问性语义应用于此元素。
上述代码效果如下:
注意,slot和behavior属性也可以用在同一个元素上。
我们可以以类似的方式来替换默认列表框部分:
<style>
.my-custom-select popup {
width: 300px;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
gap: 10px;
padding: 10px;
box-shadow: none;
margin: 10px 0;
border: 1px solid;
background: #f7f7f7;
}
</style>
<selectmenu class="my-custom-select">
<div slot="listbox">
<popup behavior="listbox">
<option>Option 1</option>
<option>Option 2</option>
<option>Option 3</option>
<option>Option 4</option>
<option>Option 5</option>
</popup>
</div>
</selectmenu>
这里使用的 <popup> 也是由OpenUI提出的,目前已经在 Chromium 中实现。
包含 behavior="listbox" 属性的元素必须是 <popup>。behavior="listbox" 属性是在告诉浏览器,在单击<selectmenu>按钮时打开此元素,用户可以使用鼠标、箭头键和触摸键在其中选择
上述代码效果如下:
5. 扩展标签
我们不仅可以自定义标签替换默认部分,还可以通过添加新元素来扩展控件的标签。这对于使用额外信息扩充列表框或按钮或添加新功能很有用。
来看例子:
<style>
.my-custom-select [slot='button'] {
display: flex;
align-items: center;
gap: 1rem;
}
.my-custom-select button {
border: none;
margin: 0;
padding: 0;
width: 2rem;
height: 2rem;
border-radius: 50%;
display: grid;
place-content: center;
}
.my-custom-select button::before {
content: '\25BC';
}
.my-custom-select popup {
padding: 0;
}
.my-custom-select .section {
padding: 1rem 0 0;
background: radial-gradient(ellipse 60% 50px at center top, #000a 0%, transparent 130%);
}
.my-custom-select h3 {
margin: 0 0 1rem 0;
text-align: center;
color: white;
}
.my-custom-select option {
text-align: center;
padding: 0.5rem;
}
</style>
<selectmenu class="my-custom-select">
<div slot="button">
<span class="label">Choose a plant</span>
<span behavior="selected-value" slot="selected-value"></span>
<button behavior="button"></button>
</div>
<div slot="listbox">
<popup behavior="listbox">
<div class="section">
<h3>Flowers</h3>
<option>Rose</option>
<option>Lily</option>
<option>Orchid</option>
<option>Tulip</option>
</div>
<div class="section">
<h3>Trees</h3>
<option>Weeping willow</option>
<option>Dragon tree</option>
<option>Giant sequoia</option>
</div>
</popup>
</div>
</selectmenu>
在这里,使用自定义标签来包装选项列表并创建我们自己的内容,效果如下所示:
6. 替换整个 shadow DOM
最后,如果上述内容还不够,还可以通过调用 attachShadow() 来完全替换默认的 shadow DOM 来扩展控件的元素。例如,上面的示例可以修改如下:
<selectmenu id="my-custom-select"></selectmenu>
<script>
const myCustomSelect = document.querySelector('#my-custom-select')
const shadow = myCustomSelect.attachShadow({ mode: 'closed' })
shadow.innerHTML = `
<style>
.button-container {
display: flex;
align-items: center;
gap: 1rem;
}
button {
border: none;
margin: 0;
padding: 0;
width: 2rem;
height: 2rem;
border-radius: 50%;
display: grid;
place-content: center;
}
button::before {
content: '\\0025BC';
}
popup {
padding: 0;
}
.section {
padding: 1rem 0 0;
background: radial-gradient(ellipse 60% 50px at center top, #000a 0%, transparent 130%);
}
h3 {
margin: 0 0 1rem 0;
text-align: center;
color: white;
}
option {
text-align: center;
padding: 0.5rem;
}
option:hover {
background-color: lightgrey;
}
</style>
<div class="button-container">
<span class="label">Choose a plant</span>
<span behavior="selected-value" slot="selected-value"></span>
<button behavior="button"></button>
</div>
<popup behavior="listbox">
<div class="section">
<h3>Flowers</h3>
<option>Rose</option>
<option>Lily</option>
<option>Orchid</option>
<option>Tulip</option>
</div>
<div class="section">
<h3>Trees</h3>
<option>Weeping willow</option>
<option>Dragon tree</option>
<option>Giant sequoia</option>
</div>
</popup>
`
</script>
通过这种方式编写,<selectmenu> 的自定义元素完全封装在其 shadow DOM 中。因此,<selectmenu> 可以放入任何页面,而不会受到周围内容样式的干扰。
五、总结
正如我们所见,新的实验性的 <selectmenu> 控件在设计样式甚至扩展传统的 <select> 时提供了很大的灵活性。因为它内置在浏览器中,所以可以处理可访问性和视口感知定位。
这是一项正在进行的工作,并且肯定会随着 Open UI 小组收到的反馈而发生改变。已经迫不及待地希望看到规范出现在 HTML 和 CSS 标准中,并且变得更加稳定,以及看到其他浏览器对该提议的支持。
附:
- Chrome 中 <selectmenu>的实现状态:https://chromestatus.com/feature/5737365999976448;
- Open UI 对 <selectmenu>的介绍:https://open-ui.org/prototypes/selectmenu;
- <selectmenu> 演示:https://microsoftedge.github.io/Demos/selectmenu/;
原文:https://css-tricks.com/the-selectmenu-element/
作者:Patrick Brosset
译者:CUGGZ