Select
Selector desplegable para elegir un único valor de una lista de opciones, con búsqueda opcional y participación en formularios.
Úsalo cuando necesites un desplegable de un solo valor construido con hijos
<r-option>, con búsqueda opcional y participación en formularios nativos.<r-select>se encarga de abrir, filtrar y entregar el valor aFormData.
Inicio rápido
Uso básico
Las opciones se dan como hijos <r-option> en el slot. El atributo value de cada opción es su valor, y su texto es la etiqueta que se muestra.
<r-select style="width: 120px; height: 40px" defaultValue="185">
<r-option value="185">Mike</r-option>
<r-option value="186">Tom</r-option>
<r-option value="187">Lucy</r-option>
</r-select>Referencia de la API
Propiedades
| Propiedad | Tipo | Por defecto | Descripción |
|---|---|---|---|
label |
string |
'' |
Rótulo fijo sobre el campo (el mismo patrón que el label de r-input), para que un select con etiqueta se alinee con un input con etiqueta dentro de un formulario |
value |
string |
'' |
Valor seleccionado. Asignarlo actualiza la etiqueta en estado cerrado; se ignora mientras esté disabled |
defaultValue |
string |
'' |
Valor seleccionado al inicio, cotejado con el value de las opciones |
disabled |
boolean |
false |
Si el select está deshabilitado |
type |
string |
'' |
text dibuja un disparador sin borde ni fondo y sin flecha; en otro caso, con borde |
open |
boolean |
false |
Si el desplegable está a la vista. Esto es el estado: asígnalo para abrir o cerrar el panel |
placement |
string |
'bottom' |
Por qué lado se abre el desplegable, con una alineación opcional: bottom, bottom-end, top-center, … |
showSearch |
boolean |
false |
Muestra un buscador incorporado que filtra las opciones por su etiqueta |
getPopupContainerId |
string |
'' |
id del elemento donde montar el desplegable (por defecto, document.body) |
dropdownclass |
string |
'' |
Clase personalizada aplicada al panel desplegable |
trigger |
string |
'click' |
Cómo se abre el desplegable: click, hover o click,hover (en móvil se ignora hover) |
required |
boolean |
false |
Si hace falta una selección para que el formulario se envíe |
sheet |
string |
'' |
CSS inyectado en el shadow DOM |
Nota:
defaultValueyshowSearchson reactivos: cambiarlos después de que el elemento se haya conectado se vuelve a procesar (junto convalue,disabledysheet) enattributeChangedCallback. ActualizardefaultValuereaplica la selección que coincida; alternarshowSearchconecta o desconecta el buscador incorporado.
Propiedades de las opciones
Las opciones se dan mediante elementos hijos <r-option>.
| Propiedad | Tipo | Por defecto | Descripción |
|---|---|---|---|
value |
string |
'' |
Valor de la opción; se emite como valor del select cuando se elige |
disabled |
boolean |
false |
Marca la opción como no seleccionable; el select la salta tanto con clic como con teclado |
sheet |
string |
'' |
CSS inyectado en el shadow DOM de la opción |
Las opciones con etiquetas o valores repetidos registran un console.warn.
Etiqueta label
Un rótulo fijo dibujado encima del campo: siempre visible, nunca se solapa con el contenido contiguo. Usa los mismos tokens y la misma maquetación que el label de r-input, así que un select con etiqueta y un input con etiqueta puestos uno al lado del otro en un formulario quedan alineados (misma altura, mismo borde superior).
<r-select label="País" defaultValue="185">
<r-option value="185">Estados Unidos</r-option>
<r-option value="186">Canadá</r-option>
<r-option value="187">México</r-option>
</r-select>Valor inicial defaultValue
<r-select style="width: 120px; height: 40px" defaultValue="185">
<r-option value="185">Mike</r-option>
<r-option value="186">Tom</r-option>
<r-option value="187">Lucy</r-option>
</r-select>Estado deshabilitado disabled
<r-select style="width: 120px; height: 40px" disabled defaultValue="185">
<r-option value="185">Mike</r-option>
<r-option value="186">Tom</r-option>
<r-option value="187">Lucy</r-option>
</r-select>Tipo texto type
<r-select style="width: 120px; height: 40px" type="text" defaultValue="185">
<r-option value="185">Mike</r-option>
<r-option value="186">Tom</r-option>
<r-option value="187">Lucy</r-option>
</r-select>Dirección del desplegable placement
placement es una preferencia, no una garantía: cuando el disparador está cerca del borde de la ventana y al lado preferido le falta sitio, el desplegable voltea solo al otro lado y se desplaza en horizontal para no salirse de la pantalla. Esto solo se aplica al montaje por defecto a nivel de body; con getPopupContainerId puesto, elige un placement que quepa en el contenedor.
Un lado puede llevar un sufijo de alineación: bottom-end, top-center, etcétera, la misma gramática que acepta r-popover. Un lado a secas significa -start, que alinea el borde inicial del panel con el del disparador.
El sufijo solo cambia algo cuando el panel tiene una anchura distinta a la de su disparador, ya que por defecto el panel sigue el ancho del disparador. Ensánchalo (r-dropdown::part(dropdown), al que se llega mediante dropdownclass, porque el panel se portaliza a <body> en vez de vivir en el shadow root del select) y la alineación se calcula contra lo que se pinta de verdad:
<style>
r-dropdown.wide::part(dropdown) {
min-width: 220px;
}
</style>
<!-- el borde derecho del panel sobre el borde derecho del disparador -->
<r-select placement="bottom-end" dropdownclass="wide" style="width: 80px">
<r-option value="a">Una etiqueta de opción bien larga</r-option>
</r-select>Ten en cuenta que el desplazamiento por el borde manda sobre la alineación: a un disparador lo bastante cerca del borde de la ventana se le empuja el panel de vuelta a la pantalla, sea cual sea la alineación pedida.
<r-select style="width: 120px; height: 40px" defaultValue="185" placement="top">
<r-option value="185">Mike</r-option>
<r-option value="186">Tom</r-option>
<r-option value="187">Lucy</r-option>
</r-select>Estado de apertura open
open es el estado del desplegable, reflejado como atributo igual que en <details open> y <dialog open>. Nada deduce el estado del display del panel (que va por detrás lo que dure la animación de salida), así que el atributo, aria-expanded y lo que se ve en pantalla no pueden contradecirse.
Eso lo convierte en una forma admitida de gobernar el componente, y en algo a lo que dar estilo y sobre lo que hacer aserciones:
<r-select id="picker" open>
<r-option value="185">Mike</r-option>
</r-select>
<script>
const picker = document.getElementById('picker');
picker.open = true; // o picker.show()
picker.open = false; // o picker.hide()
picker.toggle();
</script>
<style>
/* el disparador, mientras su panel está abierto */
r-select[open]::part(selection) {
border-color: var(--ran-color-primary);
}
</style>show(), hide() y toggle() son envoltorios finos sobre él, para cuando un método se lee mejor que una asignación.
Función de búsqueda showSearch
<r-select style="width: 120px; height: 40px" showSearch="true">
<r-option value="185">Mike</r-option>
<r-option value="186">Tom</r-option>
<r-option value="187">Lucy</r-option>
</r-select>Forma de abrir trigger
<!-- Apertura con clic (por defecto) -->
<r-select trigger="click">
<r-option value="185">Mike</r-option>
<r-option value="186">Tom</r-option>
<r-option value="187">Lucy</r-option>
</r-select>
<!-- Apertura al pasar el cursor (se ignora en móvil) -->
<r-select trigger="hover">
<r-option value="185">Mike</r-option>
<r-option value="186">Tom</r-option>
<r-option value="187">Lucy</r-option>
</r-select>
<!-- Clic y cursor a la vez -->
<r-select trigger="click,hover">
<r-option value="185">Mike</r-option>
<r-option value="186">Tom</r-option>
<r-option value="187">Lucy</r-option>
</r-select>Contenedor de montaje getPopupContainerId
Por defecto, el desplegable se portaliza a document.body. Pasa el id de otro elemento para montarlo allí.
<r-select getPopupContainerId="my-container">
<r-option value="185">Mike</r-option>
<r-option value="186">Tom</r-option>
<r-option value="187">Lucy</r-option>
</r-select>Clase personalizada del desplegable dropdownclass
<r-select dropdownclass="custom-dropdown">
<r-option value="185">Mike</r-option>
<r-option value="186">Tom</r-option>
<r-option value="187">Lucy</r-option>
</r-select>Eventos
change
Se dispara al elegir una opción. event.detail es { value, label }, donde value es el valor de la opción elegida y label su texto visible. Seleccionar el defaultValue inicial no dispara change.
<r-select id="picker">
<r-option value="185">Mike</r-option>
<r-option value="186">Tom</r-option>
<r-option value="187">Lucy</r-option>
</r-select>
<script>
document.getElementById('picker').addEventListener('change', (e) => {
console.log(e.detail.value, e.detail.label); // p. ej. "186" "Tom"
});
</script>search
Se dispara solo con showSearch activo, mientras se escribe en el buscador (con limitación de frecuencia). event.detail es { value }, el texto de búsqueda actual. El componente además filtra por dentro las opciones visibles según su etiqueta.
<r-select showSearch="true" id="searchable">
<r-option value="185">Mike</r-option>
<r-option value="186">Tom</r-option>
<r-option value="187">Lucy</r-option>
</r-select>
<script>
document.getElementById('searchable').addEventListener('search', (e) => {
console.log(e.detail.value);
});
</script>show / after-show / hide / after-hide
Se disparan alrededor de las transiciones del panel. show y hide anuncian la intención, cuando la transición empieza; after-show y after-hide se disparan una vez que el panel ha llegado de verdad y cualquier animación ha terminado. Ese es el par que hay que escuchar cuando algo debe ocurrir solo después de que el panel se haya ido del todo.
No llevan detail.
<script>
const picker = document.getElementById('picker');
picker.addEventListener('show', () => console.log('abriendo'));
picker.addEventListener('after-hide', () => console.log('cerrado, y con la animación terminada'));
</script>Lo que se espera es la animación de la propia hoja de estilos, no una duración copiada al script, así que bajo prefers-reduced-motion (donde el panel no tiene animación que reproducir) after-hide sigue a hide de inmediato en vez de tras un retardo fijo.
Asociación con formularios
r-select es un elemento personalizado asociado a formularios (static formAssociated = true). Transmite el value seleccionado a través de ElementInternals, así que new FormData(form) lo recoge bajo el name del select cuando es descendiente real de un <form> nativo. El valor del formulario se siembra con la selección inicial al conectarse y se mantiene sincronizado conforme el valor cambia.
Reinicio: un form.reset() nativo restaura la selección de defaultValue si hay una, y si no, borra la selección por completo, mediante formResetCallback().
Validación: required hace que una selección vacía sea inválida a través de ElementInternals.setValidity(), visible para form.checkValidity() / form.reportValidity(); un select disabled nunca bloquea la validación. checkValidity(), reportValidity(), validity y validationMessage están expuestos en el elemento, igual que en un campo nativo.
<form>
<r-select name="country" required>
<r-option value="us">Estados Unidos</r-option>
<r-option value="ca">Canadá</r-option>
</r-select>
<button type="submit">Enviar</button>
</form>Slots
| Slot | Descripción |
|---|---|
| (por defecto) | Acepta elementos <r-option> que definen las opciones seleccionables |
Parts CSS
| Part | Descripción |
|---|---|
select |
Envoltorio raíz del select |
selection |
La caja del disparador (borde, fondo, maquetación) |
icon |
La flecha del desplegable |
selection-item |
Elemento que muestra la etiqueta de la opción elegida |
search |
El campo de búsqueda incorporado (visible con showSearch) |
label |
La etiqueta fija sobre el campo (existe cuando hay label) |
Antes de que cargue el script
Hasta que se ejecuta el script del componente, el navegador coloca los <r-option> como texto normal. ranui.css los oculta si la página carga esa hoja de estilos antes del primer pintado. Marcar cada opción con hidden permite que el navegador las oculte por sí mismo, así que tampoco aparecen sin la hoja de estilos. <r-select> lee la etiqueta y el valor de cada opción y dibuja su propia lista, así que el atributo no cambia nada una vez definido.
<r-select placeholder="Language">
<r-option hidden value="en">English</r-option>
<r-option hidden value="zh-CN">中文</r-option>
</r-select>Buenas prácticas
- Muchas opciones: activa
showSearchpara que se pueda filtrar por etiqueta. - Forma de abrir: ajusta
triggera lo que la gente espera; en móvil se ignorahover, así que dejaclickdisponible. - Dónde se monta: en maquetaciones con desplazamiento o con recorte por desbordamiento, usa
getPopupContainerIdpara controlar dónde se monta el desplegable. - Estilos propios: usa
dropdownclasso los nombres de::part()expuestos para rediseñar el disparador y el desplegable. - Formularios: dale un
nameal select para que su valor se recoja enFormDatadentro de un<form>nativo.