Formulário de contacto AJAX

Os mesmos campos de contacto, enviados com fetch em JSON. O visitante fica na página e vê um estado curto.

O que está a copiar

Este modelo envia os mesmos campos de contacto com fetch, para o visitante ficar na sua página e ler um estado curto.

O formulário continua a ter uma ação e o método POST. Se o JavaScript não correr, o navegador faz um envio normal.

Essa reserva importa para navegadores mais antigos e para quem bloqueia scripts. Não mantém dois formulários.

O excerto principal envia JSON. Define Content-Type como application/json e Accept como application/json.

O sucesso é o booleano success no JSON. Quando é true, o excerto mostra a mensagem da resposta, ou um agradecimento curto, e limpa o formulário.

Quando é false, o excerto mostra a mensagem da resposta. O Formgong escreve essa mensagem no idioma de _lang.

JSON e FormData

Um corpo JSON tem de listar os campos que quer enviar. O excerto inclui access_key, _lang, name, email, phone, message e botcheck.

Se mais tarde retirar o comentário do Turnstile, o caminho JSON também copia cf-turnstile-response quando esse campo tem um valor.

O excerto extra usa FormData em vez de JSON. O navegador define o tipo de conteúdo, incluindo o boundary. Não defina Content-Type.

O FormData apanha cada campo do formulário, incluindo um token Turnstile se o widget estiver dentro do formulário.

Os dois caminhos enviam Accept: application/json. Sem esse cabeçalho, um envio normal do navegador recebe a página de agradecimento, não JSON.

Não envie ao mesmo tempo um corpo JSON e um corpo multipart. Escolha um. Os dois excertos são alternativas, não camadas.

Spam, Telegram e limites

Mantenha o honeypot com o nome botcheck, vazio, fora do ecrã, com tabindex -1. Um honeypot preenchido fica guardado como spam e não é entregue.

Os nomes dos campos ficam curtos para o Telegram os poder imprimir a negrito. Não mude email para uma frase longa.

A chave de acesso fica na página. Passá-la para um segredo de servidor não torna o formulário mais seguro, e este excerto não tem servidor.

No plano gratuito, o Telegram é imediato e o email é um resumo diário. A caixa de entrada conserva os envios durante 30 dias.

O plano gratuito inclui 300 envios por mês. Um envio de spam não gasta um desses.

Esta pré-visualização não envia. Ligue o excerto no seu site e depois envie um teste a partir dessa página.

O Formgong opera o serviço. Para ajuda com o produto, escreva para support@formgong.com.

Pré-visualização

Código do formulário

HTML
<form id="fg-ajax" action="https://formgong.com/submit" method="POST" style="position:relative">
  <input type="hidden" name="access_key" value="fk_your_access_key">
  <input type="hidden" name="_lang" value="pt">
  <label>Nome <input type="text" name="name" autocomplete="name" required></label>
  <label>E-mail <input type="email" name="email" autocomplete="email" required></label>
  <label>Telefone <input type="tel" name="phone" autocomplete="tel"></label>
  <label>Mensagem <textarea name="message" required></textarea></label>
  <div aria-hidden="true" style="position:absolute;inset-inline-start:0;top:0;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)">
  <label>Deixe este campo vazio <input type="text" name="botcheck" tabindex="-1" autocomplete="off"></label>
</div>
  <!--
Opcional. Ative o Turnstile neste formulário, retire estas marcas de comentário e cole a chave do sítio.
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
<div class="cf-turnstile" data-sitekey="YOUR_TURNSTILE_SITE_KEY" data-language="pt"></div>
-->
  <button type="submit">Enviar</button>
  <p data-status role="status"></p>
</form>
<script>
(function () {
  var form = document.getElementById("fg-ajax");
  if (!form) return;
  var status = form.querySelector("[data-status]");
  form.addEventListener("submit", function (event) {
    event.preventDefault();
    var data = {
      access_key: form.access_key.value,
      _lang: form._lang.value,
      name: form.name.value,
      email: form.email.value,
      phone: form.phone.value,
      message: form.message.value,
      botcheck: form.botcheck.value
    };
    var token = form.querySelector('[name="cf-turnstile-response"]');
    if (token && token.value) data["cf-turnstile-response"] = token.value;
    var body = JSON.stringify(data);
    fetch("https://formgong.com/submit", {
      method: "POST",
      headers: { "Content-Type": "application/json", Accept: "application/json" },
      body: body
    }).then(function (res) { return res.json(); }).then(function (result) {
      if (status) status.textContent = result && result.success ? (result.message || "Enviado. Obrigado!") : ((result && result.message) || "Não foi possível enviar. Tente novamente.");
      if (result && result.success) form.reset();
    }).catch(function () {
      if (status) status.textContent = "Não foi possível enviar. Tente novamente.";
    });
  });
})();
</script>
O mesmo formulário com FormData em vez de JSON
O mesmo formulário com FormData em vez de JSON
<form id="fg-ajax" action="https://formgong.com/submit" method="POST" style="position:relative">
  <input type="hidden" name="access_key" value="fk_your_access_key">
  <input type="hidden" name="_lang" value="pt">
  <label>Nome <input type="text" name="name" autocomplete="name" required></label>
  <label>E-mail <input type="email" name="email" autocomplete="email" required></label>
  <label>Telefone <input type="tel" name="phone" autocomplete="tel"></label>
  <label>Mensagem <textarea name="message" required></textarea></label>
  <div aria-hidden="true" style="position:absolute;inset-inline-start:0;top:0;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)">
  <label>Deixe este campo vazio <input type="text" name="botcheck" tabindex="-1" autocomplete="off"></label>
</div>
  <!--
Opcional. Ative o Turnstile neste formulário, retire estas marcas de comentário e cole a chave do sítio.
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
<div class="cf-turnstile" data-sitekey="YOUR_TURNSTILE_SITE_KEY" data-language="pt"></div>
-->
  <button type="submit">Enviar</button>
  <p data-status role="status"></p>
</form>
<script>
(function () {
  var form = document.getElementById("fg-ajax");
  if (!form) return;
  var status = form.querySelector("[data-status]");
  form.addEventListener("submit", function (event) {
    event.preventDefault();
    var body = new FormData(form);
    fetch("https://formgong.com/submit", {
      method: "POST",
      headers: { Accept: "application/json" },
      body: body
    }).then(function (res) { return res.json(); }).then(function (result) {
      if (status) status.textContent = result && result.success ? (result.message || "Enviado. Obrigado!") : ((result && result.message) || "Não foi possível enviar. Tente novamente.");
      if (result && result.success) form.reset();
    }).catch(function () {
      if (status) status.textContent = "Não foi possível enviar. Tente novamente.";
    });
  });
})();
</script>

Perguntas e respostas

Porque é que o formulário ainda tem um URL de ação?

Para o envio continuar a funcionar quando o JavaScript está desligado. O script só chama preventDefault quando corre.

Devo enviar JSON ou FormData?

Qualquer um é válido. O JSON é explícito sobre cada campo. O FormData é mais curto quando acrescenta inputs mais tarde, porque lê o formulário.

A linha de estado fica vazia. O que falhou?

Verifique se Accept é application/json, se a chave de acesso corresponde a um formulário seu e se a página é servida por http ou https, não como ficheiro local.

Este formulário pode carregar um ficheiro?

Não neste modelo. Os campos de ficheiro precisam de anexos ligados e de um token Turnstile válido. Use a documentação do painel para essa configuração.