Formularz kontaktowy

Poniższy tekst będzie w zasadzie podsumowaniem tego, co sobie powiedzieliśmy w poprzednich rozdziałach. Naszym celem będzie stworzenie w pełni działającego dynamicznego formularza kontaktowego.

HTML i CSS

Zaczynamy od prostego kodu HTML i CSS, który przewijał nam się już w poprzednich dyskusjach:

Pokaż HTML

<form class="form" id="contactForm" method="post" action="./send-script.php">
    <div class="form-row">
        <label for="name">Imię (min. 3 znaki)*</label>
        <input type="text" name="name" required pattern=.{3,} id="name" data-error-text="Wypełnij to pole">
    </div>
    <div class="form-row">
        <label for="email">Email*</label>
        <input type="email" name="email" required id="email" data-error-text="Wpisz poprawny email">
    </div>
    <div class="form-row">
        <label for="message">Wiadomość*</label>
        <textarea name="message" required data-error-text="Musisz wypełnić pole" id="message"></textarea>
    </div>
    <div class="form-row">
        <button type="submit" class="submit-btn">
            Wyślij
        </button>
    </div>
</form>

Pokaż CSS

* {
    box-sizing: border-box;
}

.form {
    margin: 3rem auto;
    font-family: sans-serif;
    max-width: 40rem;
}

.form .form-row {
    margin-bottom: 1rem;
}

.form .form-row:last-child {
    margin-bottom: 0;
}

.form input,
.form textarea {
    font-family: sans-serif;
    padding: 0.8rem;
    border: 1px solid #bbb;
    display: block;
    width: 100%;
    color: #666;
}

.form textarea {
    height: 10rem;
}

.form label {
    display: block;
    font-size: 0.9rem;
    margin-bottom: 0.5rem;
}

.form .submit-btn {
    font-family: sans-serif;
    padding: 1rem 2rem;
    cursor: pointer;
    background: tomato;
    border: 0;
    border-radius: 0.2rem;
    color: #FFF;
    font-size: 1.1rem;
    font-weight: bold;
    transition: 0.3s background-color;
}

@media screen and (max-width: 500px) {
    .form .submit-btn {
        display: block;
        width: 100%;
    }
}

/* ---------------------------------------------------- */
/* walidacja formularza JS */
/* ---------------------------------------------------- */
.form input.field-error,
.form textarea.field-error {
    border-color: tomato;
    outline: none;
    box-shadow: 0 0 0 2px rgba(255,99,71, 0.3);
}
.form-error-text {
    color: tomato;
    font-size: 0.8rem;
    margin-top: 5px;
}
.form-error-inline .form-error-text {
    display: none;
}


/* ---------------------------------------------------- */
/* sukces wysylania */
/* ---------------------------------------------------- */
.form-send-success,
.form-send-error {
    font-family: sans-serif;
    padding: 3em;
    border: 1px solid #ddd;
}

.form-send-success strong,
.form-send-error strong {
    display: block;
    font-size: 1.5em;
    color: tomato;
    margin-bottom: 0.3em;
}

.form-send-success span,
.form-send-error span {

}

Wygląd formularza możesz zobaczyć tutaj.

Do naszego formularza wybierzemy sposób z umieszczeniem komunikatów błędów w dataset pól. Tak jak to robiliśmy w tamtym rozdziale - każde wymagane pole w naszym formularzu będzie miało dodatkowy atrybut data-error-text, który zawiera tekst wyświetlany w razie błędu. W razie jeżeli nie wiesz o czym mówimy, zapraszam do lektury.

Początkowy skrypt

Żeby na siłę nie powtarzać czynności, zacznijmy od punktu, na którym skończyliśmy w poprzednim rozdziale, to jest na podpięciu sprawdzania pól w czasie pisania, ale i przy próbie wysyłki formularza:


function removeFieldError(field) {
    const errorText = field.nextElementSibling;
    if (errorText !== null) {
        if (errorText.classList.contains("form-error-text")) {
            errorText.remove();
        }
    }
};

function createFieldError(field, text) {
    removeFieldError(field); //przed stworzeniem usuwam by zawsze był najnowszy komunikat

    const div = document.createElement("div");
    div.classList.add("form-error-text");
    div.innerText = text;
    if (field.nextElementSibling === null) {
        field.parentElement.appendChild(div);
    } else {
        if (!field.nextElementSibling.classList.contains("form-error-text")) {
            field.parentElement.insertBefore(div, field.nextElementSibling);
        }
    }
};

function toggleErrorField(field, show) {
    const errorText = field.nextElementSibling;
    if (errorText !== null) {
        if (errorText.classList.contains("form-error-text")) {
            errorText.style.display = show ? "block" : "none";
            errorText.setAttribute('aria-hidden', show);
        }
    }
};

function markFieldAsError(field, show) {
    if (show) {
        field.classList.add("field-error");
    } else {
        field.classList.remove("field-error");
        toggleErrorField(field, false);
    }
};

//pobieram elementy
const form = document.querySelector("#contactForm");
const inputs = form.querySelectorAll("[required]");

form.setAttribute("novalidate", true);

//etap 1 : podpinam eventy
for (const el of inputs) {
    el.addEventListener("input", e => markFieldAsError(e.target, !e.target.checkValidity()));
}

form.addEventListener("submit", e => {
    e.preventDefault();

    let formErrors = false;

    //2 etap - sprawdzamy poszczególne pola gdy ktoś chce wysłać formularz
    for (const el of inputs) {
        removeFieldError(el);
        el.classList.remove("field-error");

        if (!el.checkValidity()) {
            createFieldError(el, el.dataset.errorText);
            el.classList.add("field-error");
            formHasErrors = true;
        }
    }

    if (!formErrors) {
        //form.submit();
        //dane będziemy wysyłać dynamicznie!
    }
});

Sprawdź formularz w akcji

Wysyłka formularza

W tym momencie możemy już wysłać formularz, a i nasza walidacja po stronie przeglądarki działa.

Żeby wysłać dane, musimy pobrać wszystkie dane z formularza. Wystarczy zrobić pętlę po elementach formularza i zebrać je pod postacią formData:


...

form.addEventListener("submit", e => {
    e.preventDefault();

    let formErrors = false;

    //2 etap - sprawdzamy poszczególne pola gdy ktoś chce wysłać formularz
    for (const el of inputs) {
        markFieldAsError(el, false);
        toggleErrorField(el, false);

        if (!el.checkValidity()) {
            markFieldAsError(el, true);
            toggleErrorField(el, true);
            formErrors = true;
        }
    }

    if (!formErrors) {
        //tutaj wysyłka
        const formData = new FormData(form);
    }
});

Mamy zebrane dane. Pozostaje je wysłać.

Przed samym wysłaniem danych wyłączmy przycisk submit poprzez dodanie do niego atrybutu disabled. Dzięki temu zniecierpliwiony użytkownik nie będzie mógł klikać w przycisk wysyłania aż do czasu zakończenia poprzedniej wysyłki.

Dodatkowo powinniśmy pokazać jakiś wskaźnik wczytywania. Możemy to zrobić jakąś graficzką loadingu, ale lepiej zastosować jakąś prostą animację:


.loading {
    position: relative;
    pointer-events: none;
    opacity:0.5;
}

.loading::after {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    border: 2px solid rgba(0, 0, 0, 0.2);
    border-right-color: rgba(0,0,0,0.7);
    transform: translate(-50%, -50%) rotate(0deg);
    content:"";
    animation: rotateSingleLoading 0.3s infinite linear;
    z-index: 10;
}

@keyframes rotateSingleLoading {
    from {
        transform: translate(-50%, -50%) rotate(0deg);
    }
    to {
        transform: translate(-50%, -50%) rotate(360deg);
    }
}

const loadingTest = document.querySelector("#loadingTest")
loadingTest.addEventListener("click", e => {
    loadingTest.classList.add("loading");
    loadingTest.disabled = true;
});

Zastosujmy ją w naszym skrypcie:


form.addEventListener("submit", e => {
    e.preventDefault();

    let formErrors = false;

    //2 etap - sprawdzamy poszczególne pola gdy ktoś chce wysłać formularz
    for (const el of inputs) {
        markFieldAsError(el, false);
        toggleErrorField(el, false);

        if (!el.checkValidity()) {
            markFieldAsError(el, true);
            toggleErrorField(el, true);
            formErrors = true;
        }
    }

    if (!formErrors) {
        const submit = form.querySelector("[type=submit]");
        submit.disabled = true;
        submit.classList.add("loading");

        ...
    }
});

Do samej wysyłki zastosujemy fetch. Dla adresu i metody jaką wyślemy musimy pobrać atrybuty action i method z formularza. Wcześniej pobrane dane z formularza wysyłamy jako POST, dlatego zamieniamy je za pomocą formData na właściwy zapis:


form.addEventListener("submit", e => {
    e.preventDefault();

    let formErrors = false;

    //2 etap - sprawdzamy poszczególne pola gdy ktoś chce wysłać formularz
    for (const el of inputs) {
        markFieldAsError(el, false);
        toggleErrorField(el, false);

        if (!el.checkValidity()) {
            markFieldAsError(el, true);
            toggleErrorField(el, true);
            formErrors = true;
        }
    }

    if (!formErrors) {
        const submit = form.querySelector("[type=submit]");
        submit.disabled = true;
        submit.classList.add("loading");

        //generuję dane do wysyłki
        const formData = new FormData(form);

        const url = form.getAttribute("action"); //pobieramy adres wysyłki z action formularza
        const method = form.getAttribute("method"); //tak samo metodę

        fetch(url, {
            method: method,
            body: formData
        })
        .then(res => res.json())
        .then(res => {
            //tutaj odpowiedź
        }).finally(() => { //gdy zakończy się połączenie chcemy włączyć przycisk submit
            submit.disabled = false;
            submit.classList.remove("loading");
        });
    }
});

Po stronie serwera

Formularz wysłaliśmy na serwer. Czas zająć się napisaniem skryptu serwerowego. Do naszych celów użyjemy PHP, ale podobne działanie można utworzyć w każdej innej technologii serwerowej:


<?php

$mailToSend = "twoj-mail@lorem.pl";

if ($_SERVER["REQUEST_METHOD"] === "POST") {
    $name = $_POST["name"];
    $email = $_POST["email"];
    $message = $_POST["message"];

    $errors = [];
	$return = [];

    if (empty($name)) { //jeżeli pusta wartość
        array_push($errors, "name");
    }
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { //sprawdzamy czy email ma zły wzór
        array_push($errors, "email");
    }
    if (empty($message)) {
        array_push($errors, "message");
    }

    if (count($errors) > 0) {
        $return["errors"] = $errors;
    } else {
        //każde wysłanie wiadomości musi być poprzedzone ustawieniem nagłówków
        $headers  = "MIME-Version: 1.0" . "\r\n";
        $headers .= "Content-type: text/html; charset=UTF-8". "\r\n";
        $headers .= "From: ".$email."\r\n";
        $headers .= "Reply-to: ".$email;
        $message  = "
            <html>
                <head>
                    <meta charset=\"utf-8\">
                </head>
                <body>
                    <div> Imię: $name</div>
                    <div> Email: <a href=\"mailto:$email\">$email</a> </div>
                    <div> Wiadomość: </div>
                    <div> $message </div>
                </body>
            </html>";

        if (mail($mailToSend, "Wiadomość ze strony - " . date("d-m-Y"), $message, $headers)) {
            $return["status"] = "ok";
        } else {
            $return["status"] = "error";
        }
    }

    header("Content-Type: application/json");
    echo json_encode($return);
}

Jest to prosty skrypt, który kolejno sprawdza przesłane pola. Jeżeli dane są poprawne, skrypt spróbuje wysłać maila.

Zwrócone do Javascript dane mogą być w 3 postaciach:

  • {status: "ok"} - gdy wszystko poszło dobrze i wiadomość została wysłana
  • {status: "error"} - dane z formularza były poprawne, ale z jakiegoś powodu nie udało się wysłać wiadomości
  • {"errors" : ["name", "email", "message"] } - tablica zawierająca nazwy pól, które zostały wysłane z formularza z błędnymi danymi

Czasami mogą się pojawić problemy z wysyłką maila z naszego serwera. Może to być spowodowane wieloma czynnikami. Niektóre serwery wymagają by nagłówek From wskazywał na maila, który mamy podpięty pod dany serwer. Inne wymagają dodatkowych parametrów.
A i nie zawsze będziemy korzystać przecież z PHP. Może w naszym przypadku lepiej sprawdzi się nodemailer? A nawet czasami nie potrzeba w ogóle skryptów na serwerze. Zawsze możemy skorzystać z usług darmowych dostawców statycznych formularzy, które to serwisy wyślą takie maile za nas. Dobre zestawienie znajdziesz pod adresem https://css-tricks.com/a-comparison-of-static-form-providers/

Tutaj znajdziesz też wersję dla Node.js. Korzysta ona z express, oraz z NodeMailera.

Poniżej znajdziesz wersję dla Node.js, która korzysta z Express i NodeMailera. Do sprawdzenia maila użyłem paczki email-validator. Dane wysyłamy w przy użyciu FormData, dlatego musimy też użyć dodatkowej paczki express-form-data.

Aby użyć poniższego skryptu powinieneś zainstalować odpowiednie paczki. Oczywiście przydał by się też plik package.json. Mówiliśmy o tym tutaj.


    npm i express body-parser nodemailer cors email-validator express-form-data
    

Poniższy skrypt ustawiony jest dla konta Gmail. Nodemailer pozwala też skorzystać z innych serwerów. Można o tym poczytać na ich stronie.

Dane będziemy wysyłać na adres http://localhost:3002/sendMail.


    //ogólny opis nodemailera:
    //https://nodemailer.com/about/

    //dla konta które będzie wysyłać maile za pomocą google trzeba dodatkowo włączyć:
    //https://myaccount.google.com/lesssecureapps
    const account = {
        user : '...twoj-login-na-gmail',
        pass : '...twoj-password-na-gmail'
    }

    const express = require('express');
    const path = require('path');
    const bodyParser = require('body-parser');
    const formData = require('express-form-data');
    const cors = require('cors');
    const app = express();

    const nodemailer = require('nodemailer');
    const emailValidator = require('email-validator');


    // odpalamy parsowanie requestow dla express
    app.use( bodyParser.json() );
    app.use(cors())
    app.use(formData.parse());
    app.use(bodyParser.urlencoded({ extended: true }));


    //zeby nie miec problemow z corsami strona index.html i serwer musza byc na tej samej domenie
    //wiec ja serwujemy z tego serwera
    app.get('/', function (req, res) {
        res.sendFile(path.resolve(__dirname + '/index.html'))
    });

    //nasluchujemy rzadania danego typu (get, post, put, path, delete)
    app.post('/sendMail', (req, res) => {
        //obiekt ktory bede zwracal
        let returnObj = {}
        console.log(req.body)
        //ustawiam nagłówek dla zwrotki
        res.setHeader('Content-Type', 'application/json');

        //sprawdzam prymitywnie dane ktore przyszly z formularza
        if (!emailValidator.validate(req.body.email)) {
            if (!returnObj.errors) returnObj.errors = [];
            returnObj.errors.push("email");
        }

        if (req.body.name === undefined || req.body.name === "") {
            if (!returnObj.errors) returnObj.errors = [];
            returnObj.errors.push("name");
        }

        if (req.body.message === undefined || req.body.message === "") {
            if (!returnObj.errors) returnObj.errors = [];
            returnObj.errors.push("message");
        }

        //jezeli sa bledy
        if (returnObj.errors) {
            res.setHeader('Content-Type', 'application/json');
            res.send(JSON.stringify(returnObj));
        } else {
            let transporter = nodemailer.createTransport({
                service : 'gmail',
                host: 'smtp.gmail.com',
                port: 587,
                secure: false,
                requireTLS: true,
                auth: {
                    user: account.user,
                    pass: account.pass
                }
            });

            let mailOptions = {
                from: account.user,
                to: account.user,
                subject: 'Wiadomość ze strony',
                text: `
                    Imię: ${req.body.name}
                    Email: ${req.body.email}
                    Wiadomość:
                    ${req.body.message}
                `,
                html: `
                    <html>
                        <head>
                            <meta charset="utf-8">
                        </head>
                        <body>
                            <div> Imię: ${req.body.name} </div>
                            <div> Email: <a href="mailto:${req.body.email}">${req.body.email}</a> </div>
                            <div> Wiadomość: </div>
                            <div> ${req.body.message} </div>
                        </body>
                    </html>`
            };

            transporter.sendMail(mailOptions, (error, info) => {
                if (error) {
                    console.log('Email sent ERROR: ' + error);
                    returnObj.status = "error";
                } else {
                    console.log('Email sent SUCCESS: ' + info.response);
                    returnObj.status = "ok";
                }
                res.send(JSON.stringify(returnObj));
            });
        }

    });


    /* statyczne pliki jak css, js, obrazki */
    app.get(/^(.+)$/, (req, res) => {
        res.sendFile(path.resolve(__dirname + '/' + req.params[0]));
    });


    //odpalamy...
    const port = 3002;
    app.listen(port, () => {
        console.log(`Listening on http://localhost:${port}`);
    });

    

Odpowiedź z serwera - błędne dane

Serwer dostał paczkę danych, obsłużył ją i zwraca nam odpowiedź. W pierwszej kolejności obsłużmy odpowiedź z wypisanymi błędnie wypełnionymi polami:


form.addEventListener("submit", e => {
    e.preventDefault();

    let formErrors = false;

    //2 etap - sprawdzamy poszczególne pola gdy ktoś chce wysłać formularz
    for (const el of inputs) {
        markFieldAsError(el, false);
        toggleErrorField(el, false);

        if (!el.checkValidity()) {
            markFieldAsError(el, true);
            toggleErrorField(el, true);
            formErrors = true;
        }
    }

    if (!formErrors) {
        const submit = form.querySelector("[type=submit]");
        submit.disabled = true;
        submit.classList.add("loading");

        const formData = new FormData(form);
        const url = form.getAttribute("action");
        const method = form.getAttribute("method");

        fetch(url, {
            method: method.toUpperCase(),
            body: formData
        })
        .then(res => res.json())
        .then(res => {
            if (res.errors) { //błędne pola
                const selectors = res.errors.map(el => `[name="${el}"]`);
                const fieldsWithErrors = form.querySelectorAll(selectors.join(","));
                for (const el of fieldsWithErrors) {
                    markFieldAsError(el, true);
                    toggleErrorField(el, true);
                }
            } else { //pola są ok - sprawdzamy status wysyłki
                if (res.status === "ok") {
                    //wyświetlamy komunikat powodzenia, cieszymy sie
                }
                if (res.status === "error") {
                    //komunikat błędu, niepowodzenia
                }
            }
        }).catch(() => {
            submit.disabled = false;
            submit.classList.remove("loading");
        });
    }
});

Gdy dostaniemy w odpowiedzi błędy w postaci zmiennej res.errors, każdą wartość tej tablicy (która zawiera nazwy naszych pól) zmieniamy za pomocą map na teksty np. [name="email"] czy [name="message"]. Następnie taką tablicę łączymy za pomocą join(","), dzięki czemu uzyskujemy zapis:


const selectors = res.errors.map(el => `[name="${el}"]`);
const fieldsWithErrors = form.querySelectorAll(ret.errors.join(","));

//da nam w wyniku
const fieldsWithErrors = form.querySelectorAll(`[name="name"],[name="email"],[name="message"]...`)

Gdy już dostaniemy odpowiednie pola, podobnie jak na początku robimy po nich pętlę i pokazujemy im błędy (linie 38-40).

Odpowiedź z serwera - błąd serwera

Pozostały nam rzeczy małe, czyli obsługa odpowiedzi, gdy status równy jest "ok" i "error".

Zacznijmy od błędów. Dla error możemy dodać do formularza tekst z komunikatem błędu wysyłki. Gdzie? A to zależy od naszych upodobać - może być np. po prawej stronie przycisku wyślij (a może jakiś toast):


form.addEventListener("submit", e => {
    e.preventDefault();

    let formErrors = false;

    //2 etap - sprawdzamy poszczególne pola gdy ktoś chce wysłać formularz
    for (const el of inputs) {
        markFieldAsError(el, false);
        toggleErrorField(el, false);

        if (!el.checkValidity()) {
            markFieldAsError(el, true);
            toggleErrorField(el, true);
            formErrors = true;
        }
    }

    if (!formErrors) {
        const submit = form.querySelector("[type=submit]");
        submit.disabled = true;
        submit.classList.add("loading");

        const formData = new FormData(form);
        const url = form.getAttribute("action");
        const method = form.getAttribute("method");

        fetch(url, {
            method: method.toUpperCase(),
            body: formData
        })
        .then(res => res.json())
        .then(res => {
            if (res.errors) {
                const selectors = res.errors.map(el => `[name="${el}"]`);
                const fieldsWithErrors = form.querySelectorAll(selectors.join(","));
                for (const el of fieldsWithErrors) {
                    markFieldAsError(el, true);
                    toggleErrorField(el, true);
                }
            } else {
                if (res.status === "ok") {
                    //wyświetlamy komunikat powodzenia, cieszymy sie
                }
                if (res.status === "error") {
                    //jeżeli istnieje komunikat o błędzie wysyłki
                    //np. generowany przy poprzednim wysyłaniu formularza
                    //usuwamy go, by nie duplikować tych komunikatów
                    const statusError = document.querySelector(".send-error");
                    if (statusError) {
                        statusError.remove();
                    }

                    const div = document.createElement("div");
                    div.classList.add("send-error");
                    div.innerText = "Wysłanie wiadomości się nie powiodło";
                    submit.parentElement.appendChild(div);
                }
            }
        }).finally(() => {
            submit.disabled = false;
            submit.classList.remove("loading");
        });
    }
});

Dodajmy też proste stylowanie dla tego komunikatu:


.form .send-error {
    display:inline-block;
    font-family: sans-serif;
    padding:1rem 2rem;
    color:red;
}

@media screen and (max-width:500px) {
    .form .send-error {
        text-align:center;
        display: block;
    }
}

Odpowiedź z serwera - wreszcie pozytywnie

Uff. Zajmijmy się teraz pozytywną odpowiedzią. Możemy tutaj oczywiście wstawić tekst tak samo jak w przypadku błędu serwera. Polecam jednak zastąpienie całego formularza html z informacją o pozytywnej wysyłce. Dzięki temu formularz będzie trochę bardziej odporny na różne ataki robotów lub ludzi, którzy lubią wysyłać formularze za pomocą setInterval:


form.addEventListener("submit", e => {
    e.preventDefault();

    let formErrors = false;

    //2 etap - sprawdzamy poszczególne pola gdy ktoś chce wysłać formularz
    for (const el of inputs) {
        markFieldAsError(el, false);
        toggleErrorField(el, false);

        if (!el.checkValidity()) {
            markFieldAsError(el, true);
            toggleErrorField(el, true);
            formErrors = true;
        }
    }

    if (!formErrors) {
        const submit = form.querySelector("[type=submit]");
        submit.disabled = true;
        submit.classList.add("loading");

        const formData = new FormData(form);
        const url = form.getAttribute("action");
        const method = form.getAttribute("method");

        fetch(url, {
            method: method.toUpperCase(),
            body: formData
        })
        .then(res => res.json())
        .then(res => {
            if (res.errors) {
                const selectors = res.errors.map(el => `[name="${el}"]`);
                const fieldsWithErrors = form.querySelectorAll(selectors.join(","));
                for (const el of fieldsWithErrors) {
                    markFieldAsError(el, true);
                    toggleErrorField(el, true);
                }
            } else {
                if (res.status === "ok") {
                    const div = document.createElement("div");
                    div.classList.add("form-send-success");
                    div.innerText = "Wysłanie wiadomości się nie powiodło";

                    form.parentElement.insertBefore(div, form);
                    div.innerHTML = `
                        <strong>Wiadomość została wysłana</strong>
                        <span>Dziękujemy za kontakt. Postaramy się odpowiedzieć jak najszybciej</span>
                    `;
                    form.remove();
                }
                if (res.status === "error") {
                    //jeżeli istnieje komunikat o błędzie wysyłki
                    //np. generowany przy poprzednim wysyłaniu formularza
                    //usuwamy go, by nie duplikować tych komunikatów
                    const statusError = document.querySelector(".form-send-error");
                    if (statusError) {
                        statusError.remove();
                    }

                    const div = document.createElement("div");
                    div.classList.add("form-send-error");
                    div.innerText = "Wysłanie wiadomości się nie powiodło";
                    submit.parentElement.appendChild(div);
                }
            }
        }).finally(() => {
            submit.disabled = false;
            submit.classList.remove("loading");
        });
    }
});

Oraz dodajmy dla takiego komunikatu stylowanie:


.form-send-success {
    font-family: sans-serif;
    text-align:center;
    font-size:2rem;
    font-weight:bold;
    border:1px solid #eee;
    color:#333;
    padding:10rem 0;
    margin:3rem auto;
    max-width:40rem;
}

.form-send-success strong {
    display:block;
    margin-bottom:0.5rem;
}

.form-send-success span {
    font-size:1rem;
    color:#888;
    font-weight:normal;
    display: block;
}

Mini bonus - słodki antyspam

Powyżej stworzyliśmy prosty w pełni dynamiczny formularz kontaktowy. Problem z takimi formularzami jest taki, że bardzo lubią je roboty rozsyłające spam. Aby im przeszkodzić musimy jakoś zabezpieczyć nasze dzieło. Jedną ze sprytniejszych metod jest tak zwana honeypot. Polega ona na dodaniu do formularza dodatkowego pola, które ukrywamy za pomocą styli:


<span class="form-row honey-row">
    <label for="honey">Jeżeli jesteś człowiekiem, nie wypełniaj tego pola</label>
    <input type="text" name="honey">
</span>

.form .honey-row {
    display:none;
}

Większość robotów ignoruje style, ani nie rozumie języka pisanego, więc wypełnią to pole. Wystarczy teraz po stronie serwera sprawdzić czy pole to ma wartość. Jak ma, olewamy całą sprawę, ewentualnie pokrzepiając robota miłymi słowami wiadomości w formie sukcesu:


<?php
$mailToSend = "twoj-email@lorem.pl";

if ($_SERVER["REQUEST_METHOD"] === "POST") {
    $name = $_POST["name"];
    $email = $_POST["email"];
    $message = $_POST["email"];
    $antiSpam = $_POST["honey"];

    $errors = Array();
	$return = Array();

    if (empty($antiSpam)) {

        //tutaj cała reszta skryptu który mieliśmy wcześniej (od 23 do 49)
        if (count($errors) > 0) {
            $return["errors"] = $errors;
        } else {
            ...
        }

    } else {
        $return["status"] = "ok";
    }

    header("Content-Type: application/json");
    echo json_encode($return);
}

Demo

Gotowy formularz znajdziesz poniżej:

demo

Wszelkie prawa zastrzeżone. Jeżeli chcesz używać jakiejś części tego kursu, skontaktuj się z autorem. Aha - i ta strona korzysta z ciasteczek.

Menu