Entrada de texto destacada, estilo de TripAdvisor

Hace poco, un diseñador me pidió que creara un estilo de entrada de texto como la entrada de búsqueda en TripAdvisor . Me gustó mucho. Voy a compartir mi solución como una guía paso a paso para que pueda construirla usted mismo.

La implementación incluye tanto CSS como JavaScript. Para nuestra versión, voy a suponer que tienes un conocimiento básico de SCSS y React.

Aquí está el CodePen terminado:

Lo construiremos desde cero

Vamos a construirlo

Primero, crearemos un componente Reaccionar simple y lo renderizaremos al DOM:

 la aplicación de la clase se extiende React.Component { 
render () {
regreso (
<div className = 'input-wrapper'>
<entrada
placeholder = 'Buscar ...'
spellCheck = {false}
/>
</ div>
);
}
}
 ReactDOM.render ( 
<App />,
document.getElementById ('root')
);

Añádele algo de CSS:

 $ input-font-size: 30px; 
$ input-line-height: 70px;
$ font-family: Roboto Slab, sans-serif;
 cuerpo { 
color de fondo: # 222222;
}
 .input-wrapper { 
ancho: 500px;
margen: 50px auto;
}
 entrada { 
altura: 60px;
ancho: 100%;
min-ancho: 100%;
relleno: 0;
border-radius: 0;
line-height: $ input-line-height;
color de fondo: transparente;
color blanco;
font-size: $ input-font-size;
borde: ninguno;
esquema: ninguno;
borde inferior: 3px sólido # 333333;
font-family: $ font-family;
}

Agregue un contenedor HTML para que ReactDOM lo represente:

 <div id = "root"> </ div> 

Esto nos da la entrada de texto básico con el borde inferior.

¡Ahora agreguemos vida al borde!

La dificultad de implementar el resaltado es que el ancho debe estar al nivel del final del texto. También necesita trabajar con cualquier font-family font-size .

Como el width elemento de entrada es fijo, necesitamos otro truco para detectar dónde está el final del texto en un momento dado.

Digamos que podemos usar un segundo elemento con ancho dinámico ; en nuestro ejemplo, será un elemento span con input-highlight clase de input-highlight . A continuación, copiaremos el texto de entrada y lo colocaremos dentro del span .

Cambié la entrada de no controlada a controlada , proporcionando un value prop.

Nuestro componente React ahora se ve así:

 la aplicación de la clase se extiende React.Component { 
render () {
regreso (
<div className = 'input-wrapper'>
<entrada
placeholder = 'Buscar ...'
spellCheck = {false}
value = 'entrada básica, borde inferior'
/>
<span className = 'input-highlight'>
entrada básica, borde inferior
</ span>
</ div>
);
}
}

Agregue las siguientes reglas de CSS para input-highlight

Nota: utilizamos variables SCSS aquí para asegurarnos de que las propiedades de la font sean las mismas entre la input y el span :

 .input-highlight { 
font-size: $ input-font-size;
line-height: $ input-line-height;
font-family: $ font-family;
ancho máximo: 100%;
}

Esto nos trajo aquí:

A continuación, agreguemos un borde superior en el span y colóquelo de modo que su borde se superponga al borde inferior de la entrada. Además, como input-highlight ahora tiene position: absolute , el elemento padre necesitará la position: relative regla position: relative .

 .input-highlight { 
font-size: $ input-font-size;
line-height: $ input-line-height;
font-family: $ font-family;
ancho máximo: 100%;
 borde superior: 3px blanco sólido; 
posición: absoluta;
izquierda: 0;
abajo: 0;
altura: 0;
}
 .input-wrapper { 
ancho: 500px;
margen: 50px auto;
posición: relativa;
}

Genial, ¿verdad?

El elemento span termina con el texto. ¡Esto hace que su ancho sea una medida perfecta del ancho del texto en la entrada!

Ahora, la parte fácil: usemos JavaScript para actualizar el texto en el lapso cada vez que cambia el contenido de la entrada. Utilizaremos el state Reaccionar para actualizar el valor de la entrada y el tramo simultáneamente.

Aquí está nuestro componente actualizado:

 la aplicación de la clase se extiende React.Component { 
constructor () {
súper();
 this.state = { 
valor de entrada: ''
};

this.onInputChange = this.onInputChange.bind (this);
}
 onInputChange (e) { 
const {value} = e.target;
 this.setState ({ 
inputValue: value
});
}
 render () { 
const {inputValue} = this.state;

regreso (
<div className = 'input-wrapper'>
<entrada
onChange = {this.onInputChange}
placeholder = 'Buscar ...'
value = {inputValue}
spellCheck = {false}
/>
<span className = 'input-highlight'>
{inputValue.replace (/ / g, " u00a0")}
</ span>
</ div>
);
}
}

La parte .replace(/ /g, "u00a0") es necesaria para que Reaccionar se ocupe de los espacios correctamente.

Luego, oculte el tramo agregando las siguientes líneas al selector CSS de input-highlight :

 color: transparente; 
user-select: none;
desbordamiento: oculto;

Necesitamos el overflow: hidden en el tramo para limitar su ancho (de lo contrario, hará que el contenedor se estire horizontalmente, ¡gracias Prasanna y Andrea por señalarlo en los comentarios!)

Terminándolo

Ya funciona muy bien. El último toque es agregar un color diferente en onFocus para resaltar.

Para lograr esto, necesitamos una forma de diseñar el tramo en función del estado de enfoque de la entrada. La entrada y el span son hermanos, por lo que usaremos el selector de hermanos CSS ( + ).

Aquí está el código para el selector de input completo, incluido el selector de hermanos para input-highlight :

 entrada { 
altura: 60px;
ancho: 100%;
min-ancho: 100%;
relleno: 0;
border-radius: 0;
line-height: $ input-line-height;
color de fondo: transparente;
color blanco;
font-size: $ input-font-size;
borde: ninguno;
esquema: ninguno;
borde inferior: 3px sólido # 333333;
font-family: $ font-family;
 &:atención { 
+ .input-highlight {
border-top: 3px sólido # fbc91b;
}
}
}

¡Gracias por quedarte! Si te gusta esta publicación, compártela con más personas recomendándola.