
Often students ask me: “Which element of the site is the most important?”, To which I answer them - forms. After all, with the help of forms, users perform almost all conversion actions. It is with this element that most problems are associated. In this article I will try to tell you what can be improved when interacting with forms. And at the same time to describe the new features of working with them in browsers.
However, first I would like to indicate my position on the development of such forms. In my opinion, the right approach when developing interfaces is a progressive improvement approach.
Alexander Pershin spoke in detail about this approach in his article
Progressive Enhancement . If you still have not encountered it, then I highly recommend reading it.
Another caveat: I do not think that the interface should work and look the same in all browsers. I am firmly convinced that you should not torment users with old browsers with your multi-kilo byte libraries - they are already without it.
')
Browser statistics of the RuNet
Since I will be showing approaches using new features of browsers, take a look at the current runet statistics. Everyone can determine for themselves the degree of user readiness. I divided it into two categories: full and partial support.
All receptions in this article will be
estimated by 56.8% of users. In this statistics there are browsers: IE 10, Firefox 11-17, Chrome 4-24, Safari 6, Opera 12.
Some receptions will be appreciated by
80.1% of users. This additionally included support: IE 8-9, Safari 4-5, Opera 10-11.
Mobile browsers did not include statistics, although they would give an even greater percentage of support. Information was taken from
LiveInternet with a slice in Russia . The average traffic for 3 months (October-December) of 2012 got into the calculation.
New HTML5 Attribute Forms
For a start, let me remind you of the new attributes of the form fields that will be used:
required
,
autofocus
,
placeholder
.
required
- required field;autofocus
- setting the focus on the field when loading the page;placeholder
- field description.
Along with this, many new field types have appeared:
date
,
email
,
number
,
range
and others. However, the most innocuous of them (
email
) is still used with caution. But in order for it to earn special action is not necessary. Browsers that do not know this type of field will consider it text.
There are also additional selectors in CSS:
E:valid
,
E:invalid
,
E:required
- with which you can describe the style design of fields in different situations.
Demonstration of the work of new attributes of fields and their design.Such an approach, of course, will not work in older browsers. However, since we are progressively improving the shape, this should not bother us too much.
The form remains to work even in older browsers. In any case, validation of the entered data should occur on the server side. Take it as a rule not to trust user data and always completely check them on the server.
Write form data as you type
One of the common problems of filling out forms is that the data entered is lost. This can happen for various reasons: site error, following the link, in the end, the Internet can be disconnected. You can solve this problem in different ways, for example, write data to
localStorage
as you type.
- if ( window. localStorage ) {
- var elements = document. querySelectorAll ( '[name]' ) ;
- for ( var i = 0 , length = elements. length ; i < length ; i ++ ) {
- ( function ( element ) {
- var name = element. getAttribute ( 'name' ) ;
- element. value = localStorage. getItem ( name ) || '' ;
- element. onkeyup = function ( ) {
- localStorage. setItem ( name , element. value ) ;
- } ;
- } ) ( elements [ i ] ) ;
- }
- }
Validation of the form and sending data by Ajax
Since we use the
required
attributes, we can also do the validation in a new way. In the
HTML5 specification for the form element, the
checkValidity()
method has been added. It returns
true
or
false
. The form's working strategy will be very simple: if the validation check gives a negative result, we block sending the form, otherwise, we allow sending.
- submit. disabled = ! form. checkValidity ( ) ;
Now we add the ability to send the form without reloading, using Ajax. With the second version of the
XMLHttpRequest specification, we got a lot of interesting things. For example, we can no longer engage in collecting data to submit a form, for this there is a
FormData
object.
- form. onsubmit = function ( event ) {
- if ( window. FormData ) {
- event. preventDefault ( ) ;
- var data = new FormData ( form ) ;
- var xhr = new XMLHttpRequest ( ) ;
- var url = form. getAttribute ( 'action' ) + '? time =' + ( new Date ( ) ) . getTime ( ) ;
- xhr. open ( 'post' , url ) ;
- xhr. onreadystatechange = function ( ) {
- if ( xhr. readyState == 4 && xhr. status == 200 ) {
- // server response: xhr.responseText
- }
- } ;
- xhr. send ( data ) ;
- }
- } ;
When working with asynchronous requests, be aware that some browsers cache the result. For example, this makes Internet Explorer, Mobile Safari (iOS 6) and others. To avoid this problem, you can add the current time to the request address.
Now we get the answer from the server in text form (
xhr.responseText
), but this will change over time. For example, if we know for sure that the answer is JSON, we can get a JavaScript object right away.
- var xhr = new XMLHttpRequest ( ) ;
- xhr. open ( method , url ) ;
- xhr. responseType = 'json' ;
- xhr. onreadystatechange = function ( ) {
- if ( xhr. readyState == 4 && xhr. status == 200 ) {
- // server response: xhr.response
- }
- } ;
- xhr. send ( ) ;
Please note that the server response will be in the
xhr.response
property. And the
xhr.responseType
property can take other values, for example:
arraybuffer
,
blob
,
document
.
Demonstration of saving form data and sending it using XMLHttpRequest.After successfully submitting the form, I advise you to leave the contact information in
localStorage
and clear the rest. Thus, if the user wants to submit the form again, some of the information will already be filled out.
Preview of uploaded photos
Smoothly go to work with files in the forms. Until recently, there was almost no means for working with files. But everything changes. Let's start with the simple - new attributes for file upload fields.
multiple
- allows you to select multiple files;accept
- allows you to specify which files to select.
Suppose we want to add the ability to upload several photos to our form. This field will look something like this.
- < input type = "file" name = "image" accept = "image / * multiple " >
I want to remind: a field with such attributes will work in older browsers. The constraint will be:
- Only one file;
- File validation is performed on the server side.
Let's try to improve the experience of interacting with files. Once we expect users to add photos, it is logical to make a preview possible. For this we will use the
FileReader
object from the
File API specification.
- document. querySelector ( '[type = file]' ) . addEventListener ( 'change' , function ( ) {
- [ ] . forEach . call ( this . files , function ( file ) {
- if ( file. type . match ( /image.*/ ) ) {
- var reader = new FileReader ( ) ;
- reader. onload = function ( event ) {
- var img = document. createElement ( 'img' ) ;
- img. src = event. target . result ;
- div. appendChild ( img ) ;
- queue. push ( { file : file , element : img } ) ;
- } ;
- reader. readAsDataURL ( file ) ;
- }
- } ) ;
- } , false ) ;
Thus, we immediately display all selected photos on the site.

And in order to send them using Ajax, we collect them in an array
queue
. Earlier in the article we used the
FormData
object, now we just add a list of files to it.
- var data = new FormData ( form ) ;
- queue. forEach ( function ( element ) {
- data. append ( 'image' , element. file ) ;
- } ) ;
That's all, the rest remains the same. The form will be sent with files without rebooting.
Demonstration of previewing photos and sending them using Ajax.Drag and drop files
Let's try to pay more attention to the files. Add the ability to drag files from your computer immediately to the form. In this case, the logic of previewing and sending without restarting should remain. To get started, let's select the work with the preview in a separate function.
- function previewImages ( files ) {
- [ ] . forEach . call ( files , function ( file ) {
- if ( file. type . match ( /image.*/ ) ) {
- var reader = new FileReader ( ) ;
- reader. onload = function ( event ) {
- var img = document. createElement ( 'img' ) ;
- img. src = event. target . result ;
- div. appendChild ( img ) ;
- queue. push ( { file : file , element : img } ) ;
- } ;
- reader. readAsDataURL ( file ) ;
- }
- } ) ;
- }
For example, a zone with a
wrapper
class will be a zone for moving files. Add events for it.
- var file = document. querySelector ( '[type = file]' ) ;
- var dropzone = document. querySelector ( '.wrapper' ) ;
- file. addEventListener ( 'change' , function ( ) {
- previewImages ( this . files ) ;
- this . value = '' ;
- } , false ) ;
- dropzone. addEventListener ( 'dragover' , function ( event ) {
- event. preventDefault ( ) ;
- dropzone. classList . add ( 'active' ) ;
- event. dataTransfer . dropEffect = 'copy' ;
- } , false ) ;
- dropzone. addEventListener ( 'drop' , function ( event ) {
- event. preventDefault ( ) ;
- dropzone. classList . remove ( 'active' ) ;
- previewImages ( event. dataTransfer . files ) ;
- } , false ) ;
As you can see, we have added the start (
dragover
) and end (
drop
) events of moving files. We transfer all transferred files to
previewImages
. Thus, our form works in the same way with files selected through the site and moved from the computer.
The process of downloading files (progress bar)
Photos are very large, so try to display the download process. To visualize this process, I will take the
progress
element, and you can take a
div
with a moving background. The process itself will occur in the
progress
event of the XMLHttpRequest specification.
- var xhr = new XMLHttpRequest ( ) ;
- xhr. upload addEventListener ( 'progress' , function ( event ) {
- if ( event. lengthComputable ) {
- progress. value = Math. round ( ( event. loaded * 100 ) / event. total ) ;
- }
- } , false ) ;
Demonstration of drag & drop and file download progress.Eventually
Our simple form has a number of significant improvements in the UX area.
- Validation occurs at the time of entry;
- The entered data is remembered until it is sent;
- Contact details are saved for re-work;
- Photo preview;
- The process of downloading files.
In this case, since we acted in accordance with the progressive improvement, our form works everywhere:
- in IE 6-7 and other older browsers,
- with javascript disabled,
- on mobile devices.

Work
FileReader
on iOS 6.
I am sure, by improving the UX form, you can find more interesting solutions. Please add your solutions, tips, criticism in the comments below. Thank!
Original article on the Web Energy blog.