Dodajemy formularz kontaktowy 2#

Walidacja Javascript + AJAX


Spis treści:

W tym poście opisuję walidację formularza po stronie użytkownika (Javascript). Ponadto pokazuję jak za pomocą AJAX, przekazać dane do pliku PHP i odebrać odpowiedź bez przeładowania strony.

Jeśli trafiłeś tu bezpośrednio, zajrzyj do pierwszego postu, gdzie opisuję założenia i tworzę podstawową strukturę formularza.

Wpisy w tej serii:

  1. HTML + otwieranie / zamykanie JQuery
  2. Walidacja HTML5/JS + AJAX
  3. Walidacja PHP + Swiftmailer
  4. Dostępność formularza
  5. Zrefaktoryzowana wersja + Github

W kolejnym poście zajmiemy się walidacją po stronie serwera (PHP) oraz wreszcie wysyłaniem wiadomości za pomocą Swiftmailer-a

Walidacja

Dlaczego zarówno po stronie serwera jak i użytkownika?

Samo zweryfikowanie danych po stronie użytkownika nie jest wystarczającym zabezpieczeniem. Możemy łatwo wyłączyć Javascript w przeglądarce i nasza walidacja znika. Dlaczego więc w ogóle ją robić po stronie użytkownika? Ponieważ jest dla niego bardziej przyjazna oraz przeprowadzana jest natychmiast. Nie ma potrzeby czekać na odpowiedź serwera za każdym razem, kiedy pole jest źle wypełnione. Walidacji po stronie serwera nie da się wyłączyć, więc jest to nasza ostatnia linia obrony przed nieprawidłowymi danymi przekazanymi przez użytkownika.

Walidacja w HTML5 i Javascript

HTML5 oferuję nam wbudowaną funkcję walidacji formularza, którą możemy także obsłużyć i dowolnie modyfikować za pomocą Javascript-u, i z tego skorzystamy.

Tak więc do naszych pól dodajemy takie właściwości:

  • type="email" - pole zostanie sprawdzone czy znajduję się w nim poprawny adres email. Dodatkowo, gdy ten atrybut jest ustawiony, na klawiaturach w urządzeniach mobilnych pojawi się symbol @, co jest dodatkowym ułatwieniem.
  • required - określa, że pole nie może pozostać puste.

Gdy dla <input> nie zdefiniujemy atrybutu type, domyślnie jest to “text”.

<label>
Twój email
<input name="from" type="email" placeholder="Wpisz tu twój email" required>
</label>
<label>
Temat
<input name="subject" placeholder="Wpisz tu temat wiadomości" required>
</label><br>
<label for="inp-message">
Treść
</label>
<textarea name="message" id="inp-message" placeholder="Wpisz tu twoją wiadomość" required></textarea>

Od strony HTML to by było na tyle. Przejdźmy do JS/JQuery.

Przypominam, że we wcześniejszym poście pobraliśmy sobie wszystkie elementy formularza, na których teraz będziemy pracować:

  const userEmail = document.getElementsByName('from')[0];
  const subject = document.getElementsByName('subject')[0];
  const message = document.getElementsByName('message')[0];
  const recaptcha = document.querySelector(".g-recaptcha");
  const formAlert = document.querySelector(".emailFormAlert");
  const contactForm = $('#contact');

Dodajemy obsługę zdarzenia - kliknięcie przycisku “Submit”:

  $('.emailFormSubmit').click(function (event) {
    event.preventDefault();
    formAlert.innerHTML='<i class="fa fa-spinner fa-pulse fa-2x fa-fw"></i>';
    //reszta

  });

W funkcji anonimowej odbieramy to zdarzenie event i wywołujemy na nim metodę event.preventDefault();, która zapobiega domyślnemu zachowaniu przycisku “submit”. Gdybyśmy tego nie zrobili to wykonałaby się wbudowana walidacja w HTML5, a po jej pomyślnym przejściu przeniosłoby nas do pliku emailform.php, tak jak to określiliśmy w atrybucie action w formularzu. Jest to coś dokładnie odwrotnego od tego, co byśmy chcieli, więc powstrzymujemy to i wykorzystamy AJAX, aby dostać odpowiedź z pliku emailform.php bez przeładowania strony, zaraz pod naszym formularzem.

AJAX umożliwia wysyłanie i odbieranie danych z serwera w sposób asynchroniczny(w tle), bez potrzeby przeładowywania całej strony od nowa. Jest to podstawowa metoda tworzenia dynamicznych stron/aplikacji.

Dodatkowo w następnej linii dodaję ikonkę, która będzie się pojawiać podczas przetwarzania formularza:. Jednak nie zobaczysz jej teraz, bo walidacja po stronie JS jest wręcz natychmiastowa. Ujrzysz ją dopiero podczas wysyłania wiadomości, bo to trwa odrobinę dłużej.

Zanim zaczniemy pisać walidację pobierzemy jeszcze element z odpowiedzią reCAPTCHA:

    const recaptchaResponse = document.getElementById("g-recaptcha-response");

Do walidacji przygotujemy sobie dwie funkcje:

  function validateEmailForm()
  {
    if(userEmail.validity.valid===false){
      markWrongInput(userEmail,"Podaj poprawny email!");
    }
    else if (subject.validity.valueMissing){
      markWrongInput(subject,"Wpisz jakiś temat!");
    }
    else if (message.validity.valueMissing){
      markWrongInput(message,"Pusta wiadomość? Napisz coś!");
    }
    else if (grecaptcha.getResponse().length === 0){
      recaptcha.classList.remove('shake');
      recaptcha.classList.add('shake');
      recaptcha.addEventListener("click", function (){this.classList.remove('shake')});
      formAlert.innerHTML="Potwierdź, że nie jesteś robotem!";
    }
    else return true;
  }
  • element.validity.valid - Sprawdza, na podstawie walidacji HTML5, czy element jest prawidłowy. W naszym przypadku sprawdza poprawność maila, bo zadeklarowaliśmy type="email".
  • element.validity.valueMissing - Tu podobnie, sprawdza czy element zawierający atrybut required nie jest pusty.
  • grecaptcha.getResponse().length === 0 - Ten warunek sprawdza czy zaznaczone jest pole reCAPTCHA. Jeśli długość odpowiedzi = 0 to nie jest zaznaczony.

W każdym przypadku, jak pewnie zauwżyłeś, gdy któryś test zwraca “false” za pomocą drugiej funkcji pomocniczej markWrongInput(element,"Treść komunikatu") generujemy komunikat oraz zaznaczamy niepoprawne pole. Wygląda ona tak:

  function markWrongInput(wrongElement,alert){
    formAlert.innerHTML=alert;
    wrongElement.classList.add('wrongInput');
    wrongElement.addEventListener("focus", function (){this.classList.remove('wrongInput')});
  }

Jest relatywnie prosta - zmienia treść komunikatu w elemencie “formAlert” i nadaje (i usuwa ją gdy element uzyska focus) klasę ze stylem “wrongInput”, który zawiera animację i wygląda tak:

.wrongInput {
  background: #fd4854;
  border: 1px solid red;
  animation: 1s shake;
  color: white;
}

.shake {
  animation: 1s shake;
}

@keyframes shake {
   0%, 100% {transform: translateX(0);} 
   10%, 30%, 50%, 70%, 90% {transform: translateX(-5px);} 
   20%, 40%, 60%, 80% {transform: translateX(5px);} 
}

Gdy wszystkie testy przejdą pomyślnie, to zwracana jest wartość “true”. Przypiszmy sobie odpowiedź tej funkcji do zmiennej (a w sumie stałej :O ES6) tam gdzię obsługujemy przycisk “submit”:

    const isValid = validateEmailForm();

Na jej podstawie zdecydujemy czy wysłać dane:

    if(isValid===true){
    	//tu pojawi się AJAX

    }

Ostatnim krokiem jest wykonanie żądania AJAX. JQuery strasznie ułatwia sprawę:

    const sendEmail = $.ajax({
      type: "POST",
      url: "emailform.php",
      dataType : 'json',
      data: {
        'userEmail' : userEmail.value,
        'subject' : subject.value,
        'message' : message.value,
        'g-recaptcha-response' : recaptchaResponse.value
      }
    });
  • Pierwsze dwa pola są oczywiste, już je omawiałem w pierwszym poście.
  • dataType określa format przekazywanych danych. W naszym przypadku będzie to json.
  • data - i tu właśnie w formacie json (w naszym przypadku obiekt {‘nazwa’ : wartość}) przekazujemy dane z formularza.

Po tych samych zdefiniowanych nazwach ('userEmail', 'subject' itd.) odbierzemy odpowiadające im wartości w PHP.

Pozostało nam tylko odebrać odpowiedź, która jest przechowywana w zmiennej “sendEmail”. I tak: gdy coś się nie powiedzie w naszym pliku PHP (czyli wystąpi jakiś error) wyświetlimy komunikat + treść błędu:

      sendEmail.fail(function(error) {
        formAlert.innerHTML='Coś poszło nie tak :( '+error.responseText;
      });

gdy wszystko wykona się poprawnie (walidacja nie musi się powieść, chodzi o to, że kod nie zawiera żadnego błędu) wyświetlimy wygenerowaną na podstawie naszego kodu PHP odpowiedź.

      sendEmail.done(function(response){
        formAlert.innerHTML=response.text;
      })

Oto efekt końcowy: