Un formulario de contacto elegante de AJAX

Introducción

Proporcionar un medio simple y confiable de retroalimentación de los visitantes del sitio es una parte crucial de cualquier presencia en la web. El canal de comentarios más simple y común son los formularios de contacto.

En este tutorial vamos a crear un formulario de contacto AJAX que aprovecha las técnicas modernas de desarrollo web.
Estamos usando PHP, CSS y jQuery con la ayuda del complemento formValidator para la validación de formularios y el complemento JQTransform, que diseñará todos los campos de entrada y botones del formulario. Además, estamos utilizando la clase PHPMailer para enviar los correos electrónicos del formulario de contacto.

Para el fondo de la página estamos utilizando una fantástica textura de madera oscura de Matt Hamm.

El formulario se degrada correctamente, lo que significa que se puede utilizar perfectamente incluso con JavaScript desactivado. .

*Editar: También asegúrese de estar ejecutando PHP 5 . Si este no es el caso, puede cambiar la versión de PHP desde el panel de control de su alojamiento.

Entonces, comencemos con el tutorial.

Paso 1 - XHTML

Primero, vamos a echar un vistazo al marcado XHTML detrás del formulario.

demo.php

<div id="main-container"> <!-- The main container element -->

<div id="form-container"> <!-- The form container -->

<h1>Fancy Contact Form</h1> <!-- Headings -->
<h2>Drop us a line and we will get back to you</h2>

<form id="contact-form" name="contact-form" method="post" action="submit.php">    <!-- The form, sent to submit.php -->

<table width="100%" border="0" cellspacing="0" cellpadding="5">

<tr>
<td width="18%"><label for="name">Name</label></td> <!-- Text label for the input field -->
<td width="45%"><input type="text" class="validate[required,custom[onlyLetter]]" name="name" id="name" value="<?=$_SESSION['post']['name']?>" /></td>
<!-- We are using session to prevent losing data between page redirects -->

<td width="37%" id="errOffset">&nbsp;</td>
</tr>

<tr>
<td><label for="email">Email</label></td>
<td><input type="text" class="validate[required,custom[email]]" name="email" id="email" value="<?=$_SESSION['post']['email']?>" /></td>
<td>&nbsp;</td>
</tr>

<tr>
<td><label for="subject">Subject</label></td>

<!-- This select is being replaced entirely by the jqtransorm plugin -->

<td><select name="subject" id="subject">
<option value="" selected="selected"> - Choose -</option>
<option value="Question">Question</option>
<option value="Business proposal">Business proposal</option>
<option value="Advertisement">Advertising</option>
<option value="Complaint">Complaint</option>
</select>          </td>
<td>&nbsp;</td>
</tr>

<tr>
<td valign="top"><label for="message">Message</label></td>
<td><textarea name="message" id="message" class="validate[required]" cols="35" rows="5"><?=$_SESSION['post']['message']?></textarea></td>
<td valign="top">&nbsp;</td>
</tr>

<tr>
<td><label for="captcha"><?=$_SESSION['n1']?> + <?=$_SESSION['n2']?> =</label></td>

<!-- A simple captcha math problem -->

<td><input type="text" class="validate[required,custom[onlyNumber]]" name="captcha" id="captcha" /></td>
<td valign="top">&nbsp;</td>
</tr>

<tr>
<td valign="top">&nbsp;</td>
<!-- These input buttons are being replaced with button elements -->
<td colspan="2"><input type="submit" name="button" id="button" value="Submit" />
<input type="reset" name="button2" id="button2" value="Reset" />
<?=$str?>

<!-- $str contains the error string if the form is used with JS disabled -->

<img id="loading" src="img/ajax-load.gif" width="16" height="16" alt="loading" />
<!-- the rotating gif animation, hidden by default -->
</td></tr>

</table>
</form>

<?=$success?>
<!-- The $success variable contains the message that is shown if JS is disabled and the form is submitted successfully -->

</div>
</div>    <!-- closing the containers -->

Como puede ver en la línea 8, estamos enviando nuestro formulario a submit.php . Estamos utilizando este archivo para manejar tanto el envío de formulario regular (para visitantes con JS deshabilitado) como el envío de formulario AJAX. Esto permite que el código se actualice fácilmente sin necesidad de fusionar cambios entre archivos.

Más adelante puedes ver que usamos la $_SESSION array para rellenar los valores de los campos de entrada. Esto se hace para garantizar que los datos no se pierdan durante los redireccionamientos de página, que ocurren cuando se envía el formulario a submit.php. durante el envío regular del formulario.

Otro aspecto importante son las clases asignadas a los campos de entrada:classs="validate[required,custom[onlyLetter]]" . El complemento de validación utiliza estas clases para definir las reglas de validación para cada campo de entrada o área de texto. Básicamente estamos diciendo que el campo es obligatorio y que solo se permiten letras.

Hay una serie de reglas de validación disponibles. Puede verlos en la página de inicio del complemento.

Ahora veamos cómo se modifica la forma simple con el uso del complemento JQtransform .

Paso 2:jQuery

Estamos usando dos complementos de jQuery:JQtransform para diseñar todos los elementos del formulario y formValidator , que nos ayudará a validar todos los campos de entrada en el lado del cliente.

Es importante recordar siempre validar los datos de entrada en el lado del servidor además de la validación del lado del cliente.

Primero necesitamos incluir todas las bibliotecas requeridas.

demo.php

<link rel="stylesheet" type="text/css" href="jqtransformplugin/jqtransform.css" />
<link rel="stylesheet" type="text/css" href="formValidator/validationEngine.jquery.css" />
<link rel="stylesheet" type="text/css" href="demo.css" />

<?=$css?> <!-- Special CSS rules, created by PHP -->

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript" src="jqtransformplugin/jquery.jqtransform.js"></script>
<script type="text/javascript" src="formValidator/jquery.validationEngine.js"></script>
<script type="text/javascript" src="script.js"></script>

El código anterior es de la sección principal de demo.php . Primero incluimos las hojas de estilo que usan los dos complementos, y luego la biblioteca jQuery y los complementos. Puede que encuentre interesante la línea 5:se trata de un conjunto especial de reglas CSS que creamos con PHP para mostrar un mensaje de confirmación, como verá más adelante.

Ahora echemos un vistazo a nuestro script.js .

secuencia de comandos.js

$(document).ready(function(){
    /* after the page has finished loading */

    $('#contact-form').jqTransform();
    /* transform the form using the jqtransform plugin */

    $("button").click(function(){

        $(".formError").hide();
        /* hide all the error tooltips */
    });

    var use_ajax=true;
    $.validationEngine.settings={};
    /* initialize the settings object for the formValidation plugin */

    $("#contact-form").validationEngine({   /* create the form validation */
        inlineValidation: false,
        promptPosition: "centerRight",
        success :  function(){use_ajax=true},   /* if everything is OK enable AJAX */
        failure : function(){use_ajax=false}    /* in case of validation failure disable AJAX */
     })

    $("#contact-form").submit(function(e){

            if(!$('#subject').val().length)
            {
                $.validationEngine.buildPrompt(".jqTransformSelectWrapper","* This field is required","error")
                /* a custom validation tooltip, using the buildPrompt method */

                return false;
            }

            if(use_ajax)
            {
                $('#loading').css('visibility','visible');
                /* show the rotating gif */

                $.post('submit.php',$(this).serialize()+'&ajax=1',
                /* using jQuery's post method to send data */

                function(data){
                    if(parseInt(data)==-1)
                        $.validationEngine.buildPrompt("#captcha","* Wrong verification number!","error");
                        /* if there is an error, build a custom error tooltip for the captcha */
                    else
                    {
                        $("#contact-form").hide('slow').after('<h1>Thank you!</h1>');
                        /* show the confirmation message */
                    }

                    $('#loading').css('visibility','hidden');
                    /* hide the rotating gif */
                });
            }

e.preventDefault(); /* stop the default form submit */
})

});

Todo este bloque de script se ejecuta dentro de $(document).ready método, que garantiza que se ejecuta después de que la página haya terminado de cargarse.

A continuación usamos jqTransform() método definido por el complemento jqtransform . Convierte y aplica estilo a todos los elementos del formulario (campos de entrada, áreas de texto, botones).

El elemento de selección en realidad se reemplaza por un conjunto de divs y anclas. Seguramente se ve muy bien, pero abre algunos problemas con el complemento de validación que nos hace manejar nuestra propia información sobre herramientas para el menú desplegable de selección.

Después de esto, en la línea 7 vinculamos cada clic en los botones en la parte inferior de la página con una línea de código que oculta todas las sugerencias de herramientas de error mostradas actualmente por el complemento de validación. Esto asegura que se actualicen correctamente y no permanezcan en la pantalla si el usuario ingresa datos válidos.

Posteriormente inicializamos el formValidation complemento con validationEngine() y en la línea 24 defina el onsubmit del formulario evento. Algunas cosas que vale la pena mencionar aquí:la información sobre herramientas personalizada en la línea 28 y el ajax=1 adicional parámetro en la línea 39. Este parámetro es utilizado por submit.php para distinguir si la solicitud se ha realizado a través de ajax o directamente con el envío del formulario.

También tenga en cuenta que usamos una variable especial use_ajax , para evitar interacciones ajax si el formulario no se valida.

Paso 3 - CSS

Todas nuestras reglas CSS están definidas en demo.css

demostración.css

body,h1,h2,h3,p,quote,small,form,input,ul,li,ol,label{
    /* reset some of the page elements */
    margin:0px;
    padding:0px;
}

body{
    color:#555555;
    font-size:13px;
    background: url(img/dark_wood_texture.jpg) #282828;
    font-family:Arial, Helvetica, sans-serif;
}

.clear{
    clear:both;
}

#main-container{
    width:400px;
    margin:30px auto;
}

#form-container{
    background-color:#f5f5f5;
    padding:15px;

    /* rounded corners */
    -moz-border-radius:12px;
    -khtml-border-radius: 12px;
    -webkit-border-radius: 12px;
    border-radius:12px;
}

td{
    /* prevent multiline text */
    white-space:nowrap;
}

a, a:visited {
    color:#00BBFF;
    text-decoration:none;
    outline:none;
}

a:hover{
    text-decoration:underline;
}

h1{
    color:#777777;
    font-size:22px;
    font-weight:normal;
    text-transform:uppercase;
    margin-bottom:5px;
}

h2{
    font-weight:normal;
    font-size:10px;

    text-transform:uppercase;

    color:#aaaaaa;
    margin-bottom:15px;

    border-bottom:1px solid #eeeeee;
    margin-bottom:15px;
    padding-bottom:10px;
}

label{
    text-transform:uppercase;
    font-size:10px;
    font-family:Tahoma,Arial,Sans-serif;
}

textarea{
    color:#404040;
    font-family:Arial,Helvetica,sans-serif;
    font-size:12px;
}

td > button{
    /* A special CSS selector that targets non-IE6 browsers */
    text-indent:8px;
}

.error{
    /* this class is used if JS is disabled */
    background-color:#AB0000;
    color:white;
    font-size:10px;
    font-weight:bold;
    margin-top:10px;
    padding:10px;
    text-transform:uppercase;
    width:300px;
}

#loading{
    /* the loading gif is hidden on page load */
    position:relative;
    bottom:9px;
    visibility:hidden;
}

.tutorial-info{
    color:white;
    text-align:center;
    padding:10px;
    margin-top:10px;
}

Nada fuera de este mundo aquí. Observe la línea 85. Esto hace que los botones en la parte inferior del formulario sean más anchos, pero desafortunadamente se ven defectuosos en IE6. Es por eso que utilicé un selector de CSS especial que se ignora en IE6, para apuntar al resto de los navegadores.

Ahora todo lo que queda es el código PHP.

Paso 4 - PHP

Primero echemos un vistazo al código al principio de demo.php.

demo.php

session_name("fancyform");
session_start();

$_SESSION['n1'] = rand(1,20);   /* generate the first number */
$_SESSION['n2'] = rand(1,20);   /* then the second */
$_SESSION['expect'] = $_SESSION['n1']+$_SESSION['n2'];  /* the expected result */

/* the code below is used if JS has been disabled by the user */
$str='';
if($_SESSION['errStr']) /* if submit.php returns an error string in the session array */
{
    $str='<div class="error">'.$_SESSION['errStr'].'</div>';
    unset($_SESSION['errStr']); /* will be shown only once */
}

$success='';
if($_SESSION['sent'])
{
    $success='<h1>Thank you!</h1>'; /* the success message */

    $css='<style type="text/css">#contact-form{display:none;}</style>';
    /* a special CSS rule that hides our form */

    unset($_SESSION['sent']);
}

Como puede ver, usamos el $_SESSION matriz para almacenar los dos números aleatorios y el resultado esperado. Esto se usa luego en submit.php para confirmar que el captcha ha sido resuelto.

Otro momento interesante es la línea 21 donde definimos una clase CSS personalizada. Esto oculta el formulario, de modo que lo único que se muestra es el mensaje de éxito en caso de que el visitante haya deshabilitado JS.

enviar.php

require "phpmailer/class.phpmailer.php";

session_name("fancyform");  /* starting the session */
session_start();

foreach($_POST as $k=>$v)
{
    /* if magic_quotes is enabled, strip the post array */
    if(ini_get('magic_quotes_gpc'))
    $_POST[$k]=stripslashes($_POST[$k]);

    $_POST[$k]=htmlspecialchars(strip_tags($_POST[$k]));
    /* escape the special chars */
}

$err = array();

/* some error checks */
if(!checkLen('name'))
    $err[]='The name field is too short or empty!';

if(!checkLen('email'))
    $err[]='The email field is too short or empty!';
else if(!checkEmail($_POST['email']))
    $err[]='Your email is not valid!';

if(!checkLen('subject'))
    $err[]='You have not selected a subject!';

if(!checkLen('message'))
    $err[]='The message field is too short or empty!';

/* compare the received captcha code to the one in the session array */
if((int)$_POST['captcha'] != $_SESSION['expect'])
    $err[]='The captcha code is wrong!';

/* if there are errors */
if(count($err))
{
    /* if the form was submitted via AJAX */
    if($_POST['ajax'])
    {
        echo '-1';
    }

    /* else fill the SESSION array and redirect back to the form */
    else if($_SERVER['HTTP_REFERER'])
    {
        $_SESSION['errStr'] = implode('<br />',$err);
        $_SESSION['post']=$_POST;

        header('Location: '.$_SERVER['HTTP_REFERER']);
    }

    exit;
}

/* the email body */
$msg=
'Name:  '.$_POST['name'].'<br />
Email:  '.$_POST['email'].'<br />
IP: '.$_SERVER['REMOTE_ADDR'].'<br /><br />

Message:<br /><br />

'.nl2br($_POST['message']).'

';

$mail = new PHPMailer();    /* using PHPMailer */
$mail->IsMail();

$mail->AddReplyTo($_POST['email'], $_POST['name']);
$mail->AddAddress($emailAddress);
$mail->SetFrom($_POST['email'], $_POST['name']);
$mail->Subject = "A new ".mb_strtolower($_POST['subject'])." from ".$_POST['name']." | contact form feedback";

$mail->MsgHTML($msg);

$mail->Send();

unset($_SESSION['post']);   /* unsetting */

/* the form was successfully sent */
if($_POST['ajax'])
{
    echo '1';
}
else
{
    $_SESSION['sent']=1;

    if($_SERVER['HTTP_REFERER'])
        header('Location: '.$_SERVER['HTTP_REFERER']);

    exit;
}

/* some helpful functions */
function checkLen($str,$len=2)
{
    return isset($_POST[$str]) && mb_strlen(strip_tags($_POST[$str]),"utf-8") > $len;
}

function checkEmail($str)
{
    return preg_match("/^[\.A-z0-9_\-\+]+[@][A-z0-9_\-]+([.][A-z0-9_\-]+)+[A-z]{1,4}$/", $str);
}

Observe cómo comprobamos si $_POST['ajax'] se ha fijado la variable y actúe en consecuencia. Como recordará, lo volvimos a configurar en script.js para indicar que estábamos usando AJAX para obtener los datos.

Los dos $_SESSION variables errStr y publicar se utilizan para compartir datos entre el formulario y submit.php en caso de que JS esté deshabilitado. Aquí publicar contiene el $_POST matriz que enviamos y se usa para completar los campos del formulario, una vez que se redirige al usuario.

¡Con esto nuestro elegante formulario de contacto está terminado!

Conclusión

Hoy usamos dos excelentes complementos de jQuery en conjunto para crear un formulario de contacto de aspecto elegante. Lo mejor es que funciona en cualquier navegador y gracias a la elegante degradación ni siquiera necesitas tener javascript habilitado.

Eres libre de descargar y modificar el código. Lo único que necesita para ejecutar esta demostración por su cuenta es ingresar la dirección de correo electrónico en la que desea recibir los correos electrónicos en submit.php.

*Editar: Si modifica el código, intente abrir submit.php directamente en su navegador; esto mostrará los posibles errores que, de lo contrario, permanecerían ocultos. Si tiene problemas, puede echar un vistazo a la sección de comentarios; allí podría encontrar la respuesta a su problema.