Um tutorial amigável de desenvolvimento web para capturar dados inseridos pelo usuário
Formulários HTML permitem que você colete informações dos
visitantes da sua página web. Listas de e-mails, formulários
de contato, e comentários em postagens de blogs são exemplos
comuns para páginas web pequenas, mas em organizações que
dependem da sua página web para obtenção de receita, os
formulários são sagrados e reverenciados.
Formulários são as “páginas do dinheiro”. São através deles que
páginas tipo e-comércio vendem os seus produtos, como companhias
que desenvolvem SaaS recebem os pagamentos por seus serviços, e
como grupos sem fins lucrativos conseguem levantar dinheiro on-line.
Muitas empresas medem o sucesso da sua página web pela efetividade
dos seus formulários porque eles respondem questões como “quantas
confirmações foram enviadas para nossa equipe de vendas?” e “quantas
pessoas assinaram para receber informações sobre nossos produtos na
última semana?”. Isso normalmente significa que os formulários são
temas de testes A/B infinitos e para otimização.
Existem dois aspectos de um formulário HTML funcional: a interface
com o usuário (frontend) e a comunicação com o servidor
(backend). O primeiro é responsável pela aparência
do formalário (conforme for definido pela HTML e CSS), enquanto o
segundo é o código que processa as informações (armazenando dados
em um banco de dados, enviando e-mail, etc). Vamos focar
inteiramente no frontend neste capítulo, deixando o
processamento do formulário no backend para um tutorial
futuro.
Configuração
Infelizmente, realmente não há como contornar o fato de que a
estilização dos formulários é difícil. É sempre uma boa
ideia ter um modelo que represente a página exatamente como você
deseja construir antes de você começar a programar, e isso é
particularmente verdade para formulário. Então, aqui está o
exemplo que criaremos neste capítulo:
Como você pode ver, essa é um formulário de submissão para
palestra para uma conferência de mentirinha. Ela contempla
muitos campos de elementos HTML para criar o formulário:
muitos tipos de campos de texto, um grupo de botões rádio,
um menu deslizante, uma caixa de seleção e um botão de submissão.
Crie um novo
projeto Atom
chamado forms e um novo arquivo HTML chamado
speaker-submission.html. Para começar, vamos
adicionar algumas marcações como o cabeçalho (<header>).
Veja! Temos alguma
semantica HTML!)
<!DOCTYPE html><htmllang='en'><head><metacharset='UTF-8'/><title>Speaker Submission</title><linkrel='stylesheet'href='styles.css'/></head><body><headerclass='speaker-form-header'><h1>Speaker Submission</h1><p><em>Want to speak at our fake conference? Fill out
this form.</em></p></header></body></html>
Próximo, crie um arquivo styles.css e adicione a
seguinte CSS. Usamos uma técnica simples de
flexbox para centralizar
o cabeçalho (e o formulário) não importa quanto largo a janela
do navegador seja:
Note que estamos aderindo a abordagem de
desenvolvimento primeiro para dispositivos móveis
que discutimos no capítulo de Design Responsivo. Essas
regras CSS básicas proveem um layout para dispositivos móveis uma
base para o layout para desktop, também. Criaremos uma
media query para um layout desktop com largura fixa
posteriormente nesse capítulo.
Formulários HTML
Vamos aos formulários! Todo formulário na HTML inicia com
o elemento, apropriado, <form>. Ele
aceita uma série de atributos, mas os mais importantes são action e
method. Vamos lá, adicione um formulário vazio
ao nosso documento HTML, bem abaixo do <header>:
O atributo action define a URL que processará o
formulário. É para onde a entrada de dados capturada pelo
formulário será enviada quando o usuário clicar no botão
Enviar. Isso é tipicamente uma URL especial definida
pelo nosso servidor web que sabe como processar o dado. Algumas
tecnologias (backend) para processar os formulários
são: Node.js,
PHP, and
Ruby on Rails, mas novamente,
vamos focar somente no frontend nesse capítulo.
O atributo method pode ser tanto um
post ou um
get (é interessante ressaltar que esses são métodos nativos do
protocolo HTTP, por isso eles não receberão tradução neste tutorial),
ambos definem como o formulário será enviado para o servidor no
backend. Isso é amplamente dependente de como seu servidor
quer tratar o formulário, mas a regra geral é utilizar o
post quando você está alterando dados no
servidor, e reservando o get para somente quando você
estiver realmente pegando dados.
AO deixar o atributo action em branco, estamos falando
ao formulário para enviar os dados para a mesma URL. Combinado
com o método get, isso vai permitir que nós
inspecionemos o conteúdo do formulário.
Estilizando Formulários
É claro, estamos olhando para um formulário em branco nesse
momento, mas isso não significa que não podemos adicionar
algum estilo a ele como se tivéssemos um contêiner
<div>. Isso irá transformá-lo em uma caixa
como o nosso elemento <header>:
Para coletar dados de entrada do usuário, vamos precisar de uma
nova ferramenta: o elemento <input/>. Insira
o seguinte código dentro do elemento <form>
para criar um campo de texto:
Primeiro, nós temos um contêiner <div> para
ajudar com a estilização. Isso é muito comum para separar
elementos de entrada de dados. Segundo, nós temos uma
<label>, que você pode pensar nela como outro
elemento semântico da HTML,
como <article> ou <figcaption>,
mas para rótulos. Um rótulo com o atributo for deve
casar com o atributo id associado ao elemento
<input/> definido.
E por fim, o elemento <input/> cria um campo
de texto. Isso é um pouco diferente de outros elementos que
aprendemos até o momento, porque ele pode mudar drasticamente a
aparência dependendo do atributo type, mas ele sempre
cria algum tipo de interatividade na entrada do usuário. Nós vamos
ver outros valores além do text no decorrer do
capítulo. Lembre-se que
seletores ID selectors são ruins—o atributo id aqui é utilizado somente
para referenciar o elemento <label>.
Conceitualmente, um elemento <input/> representa
uma “variável” que é enviada para o servidor. O atributo
name define o nome dessa variável, e o valor é qualquer
coisa que o usuário inseriu dentro do campo de texto. Note que você
pode pré-popular esse valor adicionando um atributo value
a um elemento <input/>.
Estilizando campos de entrada de texto
Um elemento <input/> pode ser estilizado como
qualquer outro elemento HTML. Vamos adicionar algumas regras CSS
ao nosso styles.css para embelezar um pouco o
formulário. Aqui vamos utilizar todos os conceitos aprendidos nos
capítulos Olá, CSS,
Modelo de Caixa, Seletores CSS, e
Flexbox:
A parte do código input[type='text'] é um novo tipo
de seletor CSS chamado “seletor de atributo”. Ele só combina com
elementos <input/> que tem um atributo
type igual ao text. Isso permite que
a gente selecione especificamente campos de texto, ao contrário
de botões radio, que são definidos pelo mesmo elemento HTML
(<input type='radio'/>). Você pode ler
mais sobre seletores de atributos na
Mozilla Developer Network.
Toda nossa estilização está com uma identificação
(“namespaced”) em um
seletor descendente chamado .form-row. Isolando o
<input/> e o <label> dessa
forma torna mais fácil criar diferentes tipos de formulários.
Veremos por que é conveniente evitar seletores globais como
input[type='text'] e label quando
formos utilizar botões radio.
E finalmente, vamos hackear esse estilo base agora para criar
nosso layout para desktop. Adicione a seguinte
media query ao final da nossa folha de estilo.
@media only screen and (min-width: 700px) {
.speaker-form-header,
.speaker-form {
width: 600px;
}
.form-row {
flex-direction: row;
align-items: flex-start; /* Para evitar que ele expanda verticalmente */margin-bottom: 20px;
}
.form-rowinput[type='text'] {
width: 250px;
height: initial;
}
.form-rowlabel {
text-align: right;
width: 120px;
margin-top: 7px;
padding-right: 20px;
}
}
Veja só esse uso incrível da propriedade
flex-direction e como utilizamos ela
para fazer o <label> aparecer em cima do
seu elemento <input/> no layout para
dispositivos móveis, mas ficar a esquerda dele no layout para
desktop.
Campo de entrada de E-mail
O atributo type do elemento <input/>
também permite que você faça alguma validação básica da entrada de
dados. Por exemplo, vamos tentar adicionar outro elemento de entrada
que só aceita endereços de e-mails ao invés de qualquer tipo
de texto inserido:
Isso funciona exatamente como a entrada type='text',
exceto que ele automaticamente verificar se o usuário inseriu um
endereço de e-mail. No Firefox, você pode tentar escrever alguma
coisa que não é um endereço de e-mail, depois clicar fora do campo
para ele perder o foco e ele vai validar seu campo. Ele deve ficar
vermelho para mostrar para o usuário que esse é um valor inválido.
O Chrome e o Safari não tentarão validar até que o usuário tente
enviar o formulário, então nós veremos essa ação mais tarde nesse
capítulo.
Isso é muito mais do que uma validação, na verdade. Ao dizer ao
navegador que estamos esperando um endereço de e-mail, eles podem
transformar a experiência do usuário em uma forma mais intuitiva.
Por exemplo, quando um navegador de smartphone vê esse
atributo type='email', ele mostra para o usuário um
teclado diferente, especial para inserir e-mails com o caractere
@ de forma mais acessível.
Também se atente ao novo atributo placeholder que
permite que você mostre um texto padrão quando o elemento
<input/>está vazio. Isso é uma técnica bem legal
de UX para mostrar ao usuário o valor do campo de entrada.
Existem um monte de outras validações embutidas nas opções além do
endereço de e-mail, que você pode consultar na documentação da MDN
<input/>. Alguns atributos em particular como o required,
minlength, maxlength, e pattern.
Estilizando Campos de Entrada de E-mail
Nós queremos que nosso campo de e-mail case com nosso campo de
texto da seção anterior, então vamos adicionar outro seletor de
atributo a regra input[type='text'], como segue:
/* Change this rule */.form-rowinput[type='text'] {
background-color: #FFFFFF;
/* ... */
}
/* To have another selector */.form-rowinput[type='text'],
.form-rowinput[type='email'] {
background-color: #FFFFFF;
/* ... */
}
Again, we don’t want to use a plain old input type
selector here because that would style all of our
<input/> elements, including our upcoming radio
buttons and checkbox. This is part of what makes styling forms
tricky. Understanding the CSS to pluck out exactly the elements you
want is a crucial skill.
Let’s not forget about our desktop styles. Update the corresponding
input[type='text'] rule in our media query to match the
following (note that we’re preparing for the next few sections with
the select, and textarea selectors):
@media only screen and (min-width: 700px) {
/* ... */.form-rowinput[type='text'],
.form-rowinput[type='email'], /* Add */.form-rowselect, /* These */.form-rowtextarea { /* Selectors */width: 250px;
height: initial;
}
/* ... */
}
Since we can now have a “right” and a “wrong” input value, we should
probably convey that to users. The :invalid and
:validpseudo-classes
let us style these states independently. For example, maybe we want
to render both the border and the text with a custom shade of red
when the user entered an unacceptable value. Add the following rule
to our stylesheet, outside of the media query:
.form-rowinput[type='text']:invalid,
.form-rowinput[type='email']:invalid {
border: 1px solid #D55C5F;
color: #D55C5F;
box-shadow: none; /* Remove default red glow in Firefox */
}
Until we include a submit button, you’ll only be able to see this in
Firefox, but you get the idea. There’s a similar pseudo-class called
:focus that selects the element the user is currently
filling out. This gives you a lot of control over the appearance of
your forms.
Radio Buttons
Changing the type property of the
<input/> element to radio transforms
it into a radio button. Radio buttons are a little more complex to
work with than text fields because they always operate in groups,
allowing the user to choose one out of many predefined options.
This means that we not only need a label for each
<input/> element, but also a way to group radio
buttons and label the entire group. This is what the
<fieldset> and
<legend> elements are for. Every radio button
group you create should:
Be wrapped in a <fieldset>, which is labeled
with a <legend>.
Associate a <label> element with each radio
button.
Use the same name attribute for each radio button in
the group.
Use different value attributes for each radio button.
Our radio button example has all of these components. Add the
following to our <form> element underneath the
email field:
<fieldsetclass='legacy-form-row'><legend>Type of Talk</legend><inputid='talk-type-1'name='talk-type'type='radio'value='main-stage' /><labelfor='talk-type-1'class='radio-label'>Main Stage</label><inputid='talk-type-2'name='talk-type'type='radio'value='workshop'checked /><labelfor='talk-type-2'class='radio-label'>Workshop</label></fieldset>
Unlike text fields, the user can’t enter custom values into a radio
button, which is why each one of them needs an explicit
value attribute. This is the value that will get sent
to the server when the user submits the form. It’s also very
important that each radio button has the same
name attribute, otherwise the form wouldn’t know they
were part of the same group.
We also introduced a new attribute called checked. This
is a “boolean attribute”, meaning that it never takes a
value—it either exists or doesn’t exist on an
<input/> element. If it does exist on either a
radio button or a checkbox element, that element will be
selected/checked by default.
Styling Radio Buttons
We have a few things working against us with when it comes to
styling radio buttons. First, there’s simply more elements to worry
about. Second, the <fieldset> and
<legend> elements have rather ugly default
styles, and there’s not a whole lot of consistency in these defaults
across browsers. Third, at the time of this writing,
<fieldset> doesn’t support flexbox.
But don’t fret! This is a good example of
floats being a useful fallback
for legacy/troublesome elements. You’ll notice that we didn’t wrap
the radio buttons in our existing .form-row class,
opting instead for a new .legacy-form-row class. This
is because it’s going to be completely separate from our other
elements, using floats instead of flexbox.
Start with the mobile and tablet styles by adding the following
rules outside of our media query. We want to get rid of the default
<fieldset> and
<legend> styles, then float the radio buttons and
labels so they appear in one line underneath the
<legend>:
For the desktop layout, we need to make the
<legend> line up with the
<label> elements in the previous section (hence
the width: 120px line), and we need to float
everything to the left so they appear on the same line.
Update our media query to include the following:
As far as layouts go, this is a pretty good cross-browser solution.
However, customizing the appearance of the actual button is another
story. It’s possible by taking advantage of the
checked attribute, but it’s a little bit complicated.
We’ll leave you to Google “custom radio button CSS” and explore that
rabbit hole on your own.
Select Elements
(Dropdown Menus)
Dropdown menus offer an alternative to radio buttons, as they let
the user select one out of many options. The
<select> element represents the dropdown menu,
and it contains a bunch of <option> elements that
represent each item.
Just like our radio button <input/> elements, we
have name and value attributes that get
passed to the backend server. But, instead of being defined on a
single element, they’re spread across the
<select> and
<option> elements.
Styling Select Elements
And, also just like our radio buttons,
<select> elements are notoriously hard to style.
However, there’s a reason for this. Dropdowns are a complex piece of
interactivity, and their behavior changes significantly across
devices. For instance, on an iPhone, clicking a
<select> element brings up a native scrolling UI
component that makes it much easier to navigate the menu.
It’s usually a good idea to let the browser/device determine the
best way to preset a <select> element, so we’ll
be keeping our CSS pretty simple. Unfortunately, even the simplest
things are surprisingly hard. For instance, try changing the font
size of our <select> element:
.form-rowselect {
width: 100%;
padding: 5px;
font-size: 14px; /* This won't work in Chrome or Safari */
}
This will work in Firefox, but not in Chrome or Safari! To sort of
fix this, we can use a vendor-specific prefix for the
appearance property:
.form-rowselect {
width: 100%;
padding: 5px;
font-size: 14px; /* This won't work in Chrome or Safari */-webkit-appearance: none; /* This will make it work */
}
The -webkit prefix will only apply to Chrome
and Safari (which are powered by the WebKit rendering engine), while
Firefox will remain unaffected. This is effectively a hack, and even
MDN says
not to use this CSS property.
Style difficulties like this are a serious consideration when
building a form. If you need custom styles, you may be better off
using radio buttons or JavaScript UI widgets.
Bootstrap Dropdowns
and
jQuery Selectmenu’s
are common JavaScript solutions for customizing select menus. In any
case, at least you now understand the problem. You can read more
about <select> issues
here.
Textareas
The <textarea> element creates a multi-line text
field designed to collect large amounts of text from the user.
They’re suitable for things like biographies, essays, and comments.
Go ahead and add a <textarea> to our form, along
with a little piece of instructional text:
<divclass='form-row'><labelfor='abstract'>Abstract</label><textareaid='abstract'name='abstract'></textarea><divclass='instructions'>Describe your talk in 500 words or less</div></div>
Note that this isn’t self-closing like the
<input/> element, so you always need a closing
</textarea> tag. If you want to add any default
text, it needs to go inside the tags opposed to a
value attribute.
Styling Textareas
Fortunately, styling textareas is pretty straightforward. Add the
following to our styles.css file (before the media
query):
By default, many browsers let the user resize
<textarea>
elements to whatever dimensions they want. We disabled this here
with the
resize property.
We also need a little tweak in our desktop layout. The
.instructions<div> needs to be
underneath the <textarea>, so let’s nudge it left
by the width of the <label> column. Add the
following rule to the end of our media query:
@media only screen and (min-width: 700px) {
/* ... */.form-row.instructions {
margin-left: 120px;
}
}
Checkboxes
Checkboxes are sort of like radio buttons, but instead of selecting
only one option, they let the user pick as many as they want. This
simplifies things, since the browser doesn’t need to know which
checkboxes are part of the same group. In other words, we don’t need
a <fieldset> wrapper or shared
name attributes. Add the following to the end of our
form:
<divclass='form-row'><labelclass='checkbox-label'for='available'><inputid='available'name='available'type='checkbox'value='is-available'/><span>I’m actually available the date of the talk</span></label></div>
The way we used <label> here was a little
different than previous sections. Instead of being a separate
element, the <label> wraps its corresponding
<input/> element. This is perfectly legal, and
it’ll make it easier to match our desired layout. It’s still a best
practice to use the for attribute.
Styling Checkboxes
For the mobile layout, all we need to do is override the
margin-bottom that we put on the rest the
<label> elements. Add the following to
styles.css, outside of the media query:
.form-row.checkbox-label {
margin-bottom: 0;
}
And inside the media query, we have to take that 120-pixel label
column into account:
@media only screen and (min-width: 700px) {
/* ... */.form-row.checkbox-label {
margin-left: 120px;
width: auto;
}
}
By wrapping both the checkbox and the label text, we’re able to use
a width: auto to make the entire form field be on a
single line (remember that the auto width makes the box
match the size of its contents).
Submit Buttons
Finally, let’s finish off our form with a submit button. The
<button> element represents a button that will
submit its containing <form>:
Clicking the button tells the browser to validate all of the
<input/> elements in the form and submit it to
the action URL if there aren’t any validation problems.
So, you should now be able to type in something that’s not an email
address into our email field, click the <button>,
and see an error message.
This also gives us a chance to see how the user’s input gets sent to
the server. First, enter some values into all the
<input/> fields, making sure the email address
validates correctly. Then, click the button and inspect the
resulting URL in your browser. You should see something like this:
Everything after the ? represents the variables in our
form. Each <input/>’s name attribute
is followed by an equal sign, then its value, and each variable is
separated by an & character. If we had a backend
server, it’d be pretty easy for it to pull out all this information,
query a database (or whatever), and let us know whether the form
submission was successful or not.
Styling Buttons
We had some experience styling buttons in the
pseudo-classes section
of the CSS Selectors chapter. Back then, we were applying
these styles to an <a> element, but we can use
the same techniques on a <button>.
Clean up that ugly default <button> styling by
adding the following to our stylesheet:
As with our checkbox, we need to take that 120px label
column into account, so include one more rule inside our media
query:
@media only screen and (min-width: 700px) {
/* ... */.form-rowbutton {
margin-left: 120px;
}
}
Summary
In this chapter, we introduced the most common HTML form elements.
We now have all these tools for collecting input from our website
visitors:
<input type='text'/>
<input type='email'/>
<input type='radio'/>
<select> and <option>
<textarea>
<input type='checkbox'/>
<button>
You should be pretty comfortable with the HTML and CSS required to
build beautiful forms, but actually making these forms functional
requires some skills we don’t have yet. That stuff is out of scope
for this tutorial, but it might help to have some context. Generally
speaking, there are two ways to process forms:
Use the action attribute to send the form data to a
backend URL, which then redirects to a success or error page. We
got a little glimpse of this in the previous section, and it
doesn’t require any JavaScript.
Use AJAX queries to submit the form without leaving the page.
Success or error messages are displayed on the same page by
manipulating the HTML with JavaScript.
Depending on how your organization is structured, form processing
may not be part of your job role as a frontend web developer. If
that’s the case, you’ll need to coordinate closely with a backend
developer on your team to make sure the
<form> submits the correct name-value pairs.
Otherwise, it’ll be up to you to make sure the frontend and backend
of your forms fit neatly together.
Next, we have our final chapter in
HTML & CSS Is Hard. We’ll round out our frontend skills with a thorough discussion of
web fonts and practical typographic principles that every web
developer should know about.