en HTML5

Input type email REGEX pattern

En este artículo vamos a ver como trabajar con campos email en nuestros formularios, ya sean de contacto o de registro, para evitar malas experiencias a nuestros usuarios con nuestro producto o marca.

¿Por qué usar type email y/o patrones de validación?

El campo de correo electrónico es probablemente el campo de formulario más recurrente en web y cualquier aplicación. Lo usamos para registrarnos en tiendas online y servicios multimedia, para acceder a nuestras cuentas, para suscribirnos, para dejar comentarios en blogs ajenos, para pedir información sobre productos o presupuestos de servicios y, en general, en cualquier formulario de contacto; y en la gran mayoría de los casos posibles, equivocarnos al introducir este dato tan valioso, supone una gran molestia. Supongamos por ejemplo que un usuario se da de alta en un servicio de pago y después de haber abonado la suscripción no le llega el correo de confirmación. OMG!! ¿Ahora qué hace? Pues yo te lo voy a decir: acordarse de los ancestros de quién inventó internet.

Por esa razón, quienes trabajamos creando aplicaciones tenemos una responsabilidad para con los usuarios de nuestros productos y sus experiencias al usarlos y HTML5 nos ayuda en la tarea de lograr que interactuar con ellos sea más placentero. En concreto, en este caso, nos aporta el input type email, que por defecto incluye la validación del formato de e-mail requiriendo al usuario que introduzca, por ejemplo, la arroba si no lo ha hecho.

Sin embargo, la validación nativa de input type mail es algo escasa en la práctica por ser demasiado permisiva. Por ejemplo, validará dominios de red sin TLD, pero también algunos caracteres prohibidos en la parte local de la cuenta de correo como veremos un poco más adelante.

Debido a estas carencias prácticas es por lo que type email, aún resultando muy interesante tanto por su semántica como para identificarlo y trabajar con él con JS o CSS, en cuanto a validación de correo sigue dependiendo del uso de REGEX usando el parámetro pattern. Y eso es lo que hoy vamos a ver aquí.

¿Cómo validar un e-mail?

Para validar un campo e-mail deberemos ir por partes. Por un lado tendremos el dominio y por el otro la parte local que identificará al usuario dentro de ese domino. Cada una de ellas sigue reglas diferentes que habrá que tener en cuenta a la hora de validarlo. Y por supuesto entre ambos deberá estar la arroba «@».

parte.local@dominio.tld

TLD = Top Level Domain (Dominio de Alto Nivel). Es la última parte del dominio

Dominio de un e-mail

El dominio de un correo electrónico sigue las reglas generales de los dominios. La pincelada aquí la pone el hecho de que, por lo general, en web los correos de nuestros usuarios estarán asociados a un proveedor de correo como pueda ser Gmail, Hotmail, Yahoo, etc. Por lo tanto, siempre tendrán TLD ya que, puesto que el ICANN prohibió/desaconsejó el uso de dominios gTLD (genéricos) sin punto (dotless domains) por razones de accesibilidad, seguridad y estabilidad de la red, éstos sólo se suelen encontrar en redes internas.

Aunque las reglas para los dominios son complejas y cambiantes, a efectos prácticos, para la validación de un campo tipo e-mail, estas son las reglas a seguir:

  • Puede contener caracteres Latin en mayúsculas y minúsculas (A-Za-z);
  • Dígitos 0-9 siempre que los TLD no sean todos numéricos pues no podrían distinguirse de las IP*;
  • Los nombres de dominio, también puede incluir guiones (hyphen -), siempre y cuando no sea el primer ni el último carácter. No así los TLD;
  • Los TLD deberán tener al menos 2 caracteres y comenzar por un carácter alfabético;
  • También existe una restricción de 63 caracteres.

*IMPORTANTE:  Técnicamente es posible que los TLD sean completamente numéricos y por tanto también empezar por un dígito, pero son casos muy particulares y a efectos prácticos, en especial en cuanto a correo electrónico se refiere, al menos a día de hoy, no nos los vamos a encontrar.

En cuanto a la estructura, los dominios se componen de varios niveles de etiquetas separadas por puntos (.).

subdominio.dominio.TLD_opcional.TLD_obligatorio /*para dominios de la red pública*/

Tendremos que tener en cuenta esta estructura a la hora crear nuestra REGEX. Pero antes veamos la parte local de un e-mail.

El nombre de usuario

Todos hemos visto nombres de e-mail separados por puntos y con caracteres raros, pero ¿hasta qué punto hay libertad a la hora de crear una cuenta de correo? Lo vamos a ver a continuación, pues de ello dependerá que evitemos errores de los usuarios al introducir sus correos electrónicos.

Sabiendo qué no puede incluir la parte local de un e-mail, podremos acotar nuestras reglas de validación. Lo primero que debemos tener en cuenta es que la parte local de un correo electrónico es mucho menos restrictiva que el dominio.

En ese sentido, tenemos cadenas de texto entrecomilladas que permiten cualquier carácter ASCII así como espacios y tabulaciones, pero son muy inusuales y, de hecho RFC 5321 desaconseja configurar bandejas de entrada que soporten este formato; y por otro lado, cadenas separadas por puntos, que son en las que nos vamos a centrar. Estos dos tipos de cadena no pueden coexistir.

Además de los tipos de cadena también se soporta comentarios encapsulados entre paréntesis en ambos extremos de la cadena, pero se trata de un formato obsoleto, por lo que no lo contemplaremos. El formato actual es Juan Pérez <jp@example.com>.

Entonces qué soporta la parte local:

  • En primer lugar soporta hasta 64 caracteres
  • Estos pueden ser caracteres Latin en mayúsculas y minúsculas (A-Za-z);
  • También dígitos 0-9;
  • Símbolos de escritura: !#$%&’*+-/=?^_`{|}~;
  • Punto (.) siempre que no sea el primer ni último carácter ni aparezca de forma consecutiva.
  • No admite espacios.

Sin embargo hemos de dejar clara la diferencia entre el estándar definido originalmente por el IETF y lo que los proveedores de correo permiten a sus usuarios / clientes. Algunos ejemplos:

  • Windows Live Hotmail sólo permite caracteres alfanuméricos, punto (.), guión (-) y guión bajo (_) , aunque parece ser que para Office 365 permite este set de símbolos: ‘ . – _ ! # ^ ~ según su documentación.
  • Gmail además de los tres básicos (.,-,_) permite más (+), coma (,), apóstrofe (‘), igual (=), ampersand (&) e incluso brackets (<,>) según su página de soporte aunque en la práctica sólo permite el punto (.) en la actualidad. Ello no quiere decir que en el pasado no fuese menos restrictiva.
    Por otro lado sus nombres de usuarios deben ser de entre 6 y 30 caracteres.
  • Yahoo permite entre 8 y 32 caracteres alfanuméricos, puntos (.) y guiones bajos (_).

Parece que además de las diferencias entre unos y otros, la tendencia es a ser cada vez más restrictivos a la hora de crear nuevos usuarios, pero podemos asumir que no lo fueron tanto en el pasado y por ello mejor trabajar con el estándar para crear nuestras REGEX.

e-mail REGEX

Ahora que entendemos cómo validar correo electrónico vamos a ver cómo construir la expresión regular, o REGEX, para el atributo pattern de nuestro input.

Validar e-mail de contacto o registro

Validar campos e-mail de contacto y/o registro suelen ser los casos más comunes pues en ambos un error puede hacernos perder al usuario/cliente.

REGEX para dominio y subdominio

El primer lugar atenderemos las etiquetas del dominio y subdominio cuya expresión regular será tal que así:

([a-zA-Z0-9]([^ @&%$\\\/()=?¿!.,:;]?|\d?)+[a-zA-Z0-9][\.]){1,2}
  1. Creamos un bloque que podrá aparecer 1 ó 2 veces. Según si contiene un subdominio o no.
    ( ){1,2}
  2. Definimos que cada etiqueta se separará de la siguiente mediante un punto
    [\.]
  3. Y que cada una debe comenzar y terminar con un carácter alfanumérico
    [a-zA-Z0-9]
  4. En medio podrá contener uno o más caracteres que no sean uno de los símbolos definidos o en su lugar cualquier dígito:
    ([^ @&%$\\\/()=?¿!.,:;]?|\d?)+
    

    Si os fijáis después de la cejilla (^) el primer carácter es un espacio en blanco. De esa forma excluimos también los espacios que no están permitidos.
    Si bien es cierto que podríamos usar simplemente \s o [a-zA-Z], de esa forma estaríamos excluyendo también el guión (-) que sí está permitido.

    IMPORTANTE: Esta parte de la REGEX espera encontrar uno o más elementos de los contenidos en el paréntesis (…)+; sin embargo, las dos opciones dentro de éste, son opcionales ?, lo que nos permite validar incluso dominios de dos caracteres. Esta posibilidad la he planteado de cara a futuro, pues inicialmente todos los nombres de dominio de segundo nivel de dos caracteres fueron reservados por la ICANN, como sucede también con todos aquellos dominios de un carácter que no fueron adquiridos al inicio como a.co (Amazon.com), g.co (Google), t.co (Twitter) o t.me (Telegram), aunque en Diciembre de 2005 la ICANN consideró subastarlos. El valor de estos dominios se cuenta en millones de dólares (Facebook fb.com = $8.5M).
    En este contexto, se entiende innecesario añadir complejidad al pattern de correo electrónico para validar nombres de dominio de un carácter.

REGEX para TLD

Aquí tendremos que tener en cuenta que nuestro dominio puede tener distintos tipos de TLD que además pueden combinarse.

La última etiqueta será la del TLD Regional (.es, .uk…). Si sólo usa un TLD irá a continuación del nombre del dominio, pero podemos encontrarnos con que tiene dos y en ese caso, primero pondremos la genérica y finalmente la regional.

El TLD Regional constará de 2 caracteres, mientras que el resto pueden llegar hasta 4. Puesto que el TLD que irá a continuación del nombre de dominio puede ser, o no, regional, deberemos definir un bloque de entre 2 y 4 caracteres alfabéticos.

[a-zA-Z]{2,4}

Para el caso de que el dominio cuente con dos TLD, por último incluiremos otro bloque opcional (?) de 2 caracteres que, al ser el último, siempre será el regional. En caso de existir deberá ir precedido de un punto [\.].

([\.][a-zA-Z]{2})?

Así, al unir las distintas partes obtenemos la REGEX para la parte del dominio de nuestro e-mail:

([a-zA-Z0-9]([^ @&%$\\\/()=?¿!.,:;]|\d)+[a-zA-Z0-9][\.]){1,2}[a-zA-Z]{2,4}([\.][a-zA-Z]{2})?

REGEX para la parte local

Aplicando las reglas antes mencionadas tendríamos lo siguiente:

[a-zA-Z0-9!#$%&'*\/=?^_`{|}~+-]([\.]?[a-zA-Z0-9!#$%&'*\/=?^_`{|}~+-])+
  1. Definimos un expresión regular de inicio que no incluirá el punto (.).
    [a-zA-Z0-9!#$%&'*\/=?^_`{|}~+-]
  2. Seguidamente duplicamos ese bloque que ahora, opcionalmente, podrá ir precedido de un punto [\.]?, pero no ir seguido de él.
  3. Este último bloque deberá aparecer al menos una vez, y podrá repetirse tantas como sea necesario (…)+

Notaréis que la barra (/) va precedida de otra barra invertida (\), ésta segunda escapa la primera para que sea correctamente interpretada.
También el guion (-) está colocado al final pues de otra forma se entendería como un delimitador de rangos. Así no es necesario escaparlo para ser leído literalmente.

El resultado final una vez añadida la arroba (@) entre la parte local y el dominio:

[a-zA-Z0-9!#$%&'*\/=?^_`{|}~+-]([\.]?[a-zA-Z0-9!#$%&'*\/=?^_`{|}~+-])+@[a-zA-Z0-9]([^@&%$/()=?¿!.,:;]|\d)+[a-zA-Z0-9][\.][a-zA-Z]{2,4}([\.][a-zA-Z]{2})?

Actualización 11/07/2023: debido a cambios de los navegadores, será necesario escapar algunos caracteres que antes no escapábamos. Abajo podéis encontrar la REGEX actualizada. Sólo añadir que si trabajáis con compiladores JS seguramente tendréis que escaparlas doblemente. Por ejemplo: \\/ para escapar el slash (/).

[a-zA-Z0-9!#$%&'*\/=?^_`\{\|\}~\+\-]([\.]?[a-zA-Z0-9!#$%&'*\/=?^_`\{\|\}~\+\-])+@[a-zA-Z0-9]([^@&%$\/\(\)=?¿!\.,:;]|\d)+[a-zA-Z0-9][\.][a-zA-Z]{2,4}([\.][a-zA-Z]{2})?

Os dejo un ejemplo en vivo.

See the Pen
email pattern
by Daniel Abril (@elcssar)
on CodePen.

Comentarios de cierre

Si bien es cierto que la validación no evitará que el usuario se equivoque al introducir su dirección pues no podemos controlar los errores ortográficos o que el usuario confunda dos cuentas de proveedores distintos, al menos reduciremos la casuística de errores posibles al eliminar todos los relacionados con el formato.

Si queréis podéis ser más restrictivos que el estándar y eliminar algunos de los caracteres permitidos. Por ejemplo las llaves ({,}), la cejilla (^), el igual (=) o el interrogante (?) son símbolos extremadamente raros en el contexto del correo electrónico.

Espero os haya sido útil.

Escribe un comentario

Comentario

  1. Saludos y muy buen post. Yo necesito que al escribir en un INPUT de tipo EMAIL, ya se encuentre predeterminado mi dominio, pej: @dominio.com, o sea, que solamente escriba el username. Gracias.

    • Hola Ed,
      gracias, hacemos lo que podemos 😉
      Eso lo puedes lograr con máscaras de formateo. Si no eres programador, la opción más fácil sería encontrar una librería que te permita meter sufijos, aunque normalmente, librerías como clave.js o formatjs te permiten sólo una serie limitada de opciones tales como formateo de fechas, teléfonos, etc.
      Si te animas a crear tu propia máscara, encontrarás algunas soluciones de la comunidad. Sólo busca «input masking javascript» o «máscaras input jasvascript».
      Sin embargo, teniendo en cuenta que sólo quieres añadir @tudominio.tld detrás del nombre de usuario lo que puedes hacer es que al recibir el foco, rellenes el input con el dominio y seguidamente posiciones el cursor al inicio de la cadena de texto con este código:
      $("#id_de_tu_campo").get(0).setSelectionRange(0,0);
      Eso debería ser suficiente en la mayoría de los casos de uso.
      Espero haberte ayudado.

    • De nada Gonzalo. Celebro haberte ayudado con esta REGEX

    • A ti, Víctor, por tu amabilidad. Si tienes cualquier pregunta no dudes en formularla.

  2. Me gustaría saber si por ejemplo se puede usar este método para restringir algunos LTD como por ejemplo los .ru que causan muchísimo spam , o en caso de por ejemplo se quieran limitar los dominios genericos , en el caso que no se admitan los gmail, hotmal, yahoo, se podría hacer usando esta propiedad?

    • Por supuesto @Miguel, con REGEX puede restringir cualquiera de las partes, incluido el TLD, creo que lo que necesitaríamos es un código tal que así: \b(?!ru|ca|fr\b)\w{2}, además deberías cambiarlo en ambos niveles de TLD, pues en función de si tiene uno o dos, el TLD territorial estará en una posición u otra. Cuando tengas que trabajar con REGEX te recomiendo regex101.com.

      Si te funciona, por favor, compártelo por aquí para que otros usuarios se puedan beneficiar 😉