Destaque de entrada de texto, estilo do TripAdvisor

O designer criou recentemente um estilo de entrada de texto, como a entrada de pesquisa no TripAdvisor . Eu gosto muito disso. Eu vou compartilhar minha solução como um guia passo a passo para que você possa construí-lo sozinho.

A implementação envolve CSS e JavaScript. Para a nossa versão, vou assumir que você tem um conhecimento básico de SCSS e Reagir.

Aqui está o CodePen concluído:

Vamos construí-lo a partir do zero

Vamos construí-lo

Primeiro, criaremos um componente React simples e o renderizaremos para o DOM:

 O aplicativo de classe estende React.Component { 
render () {
Retorna (
<div className = 'input-wrapper'>
<input
espaço reservado = 'Pesquisar ...'
spellCheck = {false}
/>
</ div>
);
}
}
 ReactDOM.render ( 
<App />,
document.getElementById ('root')
);

Adicione algum CSS a ele:

 $ input-font-size: 30px; 
$ input-line-height: 70px;
$ font-family: Roboto Slab, sans-serif;
 corpo { 
background-color: # 222222;
}
 .input-wrapper { 
largura: 500px;
margem: 50px auto;
}
 entrada { 
altura: 60px;
largura: 100%;
largura mínima: 100%;
preenchimento: 0;
limite de borda: 0;
altura da linha: $ input-line-height;
cor de fundo: transparente;
cor branca;
font-size: $ input-font-size;
fronteira: nenhuma;
Esboço: nenhum;
borda inferior: 3px sólido # 333333;
font-family: $ font-family;
}

Adicione um contêiner HTML para ReactDOM para renderizar:

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

Isso nos traz a entrada de texto básica com a borda inferior.

Agora vamos adicionar a vida à fronteira!

A dificuldade de implementar o destaque é que a largura precisa ser nivelada com o fim do texto. Ele também precisa trabalhar com qualquer font-family e font-size .

Uma vez que a width elemento de entrada é fixa, precisamos de algum outro truque para detectar onde o final do texto é em qualquer momento.

Digamos que podemos usar um segundo elemento com largura dinâmica – no nosso exemplo, será um elemento span com input-highlight classe input-highlight . Em seguida, vamos copiar o texto de entrada e colocá-lo dentro do span .

Eu mudei a entrada de não controlado para controlado , fornecendo um suporte de value .

Nosso componente React agora se parece com isto:

 O aplicativo de classe estende React.Component { 
render () {
Retorna (
<div className = 'input-wrapper'>
<input
espaço reservado = 'Pesquisar ...'
spellCheck = {false}
valor = 'entrada básica, borda inferior'
/>
<span className = 'input-highlight'>
entrada básica, borda inferior
</ span>
</ div>
);
}
}

Adicione as seguintes regras CSS para input-highlight

Nota: usamos as variáveis ??SCSS aqui para garantir que as propriedades da font sejam as mesmas entre input e span :

 .input-highlight { 
font-size: $ input-font-size;
altura da linha: $ input-line-height;
font-family: $ font-family;
largura máxima: 100%;
}

Isso nos trouxe aqui:

Em seguida, vamos adicionar uma borda superior no span e posicioná-lo de modo que sua borda se sobrepõe à borda inferior da entrada. Além disso, uma vez que o input-highlight agora tem position: absolute , o elemento pai precisará da position: relative regra position: relative .

 .input-highlight { 
font-size: $ input-font-size;
altura da linha: $ input-line-height;
font-family: $ font-family;
largura máxima: 100%;
 border-top: 3px sólido branco; 
posição: absoluta;
esquerda: 0;
fundo: 0;
altura: 0;
}
 .input-wrapper { 
largura: 500px;
margem: 50px auto;
posição: relativa;
}

Legal certo?

O elemento span termina com o texto. Isso faz da sua largura uma medida perfeita da largura do texto na entrada!

Agora, a parte fácil: vamos usar o JavaScript para atualizar o texto no intervalo sempre que as mudanças no conteúdo da entrada. Usaremos o state React para atualizar simultaneamente o valor da entrada e do intervalo.

Aqui está o nosso componente atualizado:

 O aplicativo de classe estende React.Component { 
construtor () {
super();
 this.state = { 
valor de entrada: ''
};

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

Retorna (
<div className = 'input-wrapper'>
<input
onChange = {this.onInputChange}
espaço reservado = 'Pesquisar ...'
value = {inputValue}
spellCheck = {false}
/>
<span className = 'input-highlight'>
{inputValue.replace (/ / g, " u00a0")}
</ span>
</ div>
);
}
}

A parte .replace(/ /g, "u00a0") é necessária para Reagir para lidar corretamente com os espaços.

Em seguida, esconda a extensão adicionando as seguintes linhas ao seletor CSS de input-highlight :

 cor: transparente; 
usuário-selecionar: nenhum;
transbordamento: oculto;

Nós precisamos do overflow: hidden no span para limitar a sua largura (caso contrário, o contêiner se esticará horizontalmente – obrigado Prasanna e Andrea por apontar os comentários!)

Terminando

Já funciona muito bem. O último toque é adicionar uma cor onFocus diferente para o destaque.

Para realizar isso, precisamos de uma forma de modelar a extensão com base no estado de foco da entrada. O input e span são irmãos, então usaremos o seletor de irmãos CSS ( + ).

Aqui está o código para o seletor de input completo, incluindo o seletor de irmãos para input-highlight :

 entrada { 
altura: 60px;
largura: 100%;
largura mínima: 100%;
preenchimento: 0;
limite de borda: 0;
altura da linha: $ input-line-height;
cor de fundo: transparente;
cor branca;
font-size: $ input-font-size;
fronteira: nenhuma;
Esboço: nenhum;
borda inferior: 3px sólido # 333333;
font-family: $ font-family;
 &: focus { 
+ .input-highlight {
border-top: 3px sólido # fbc91b;
}
}
}

Obrigado por ficar por aí! Se você gosta desse post, compartilhe-o com mais pessoas, recomendando-o.

Texto original em inglês.