chevron-left chevron-right

[CSS] Jak dodać interaktywną etykietę pola do formularza?

Formularze jakie są, każdy widzi. Najczęściej jest to zestaw kilku pól do wypełnienia i przycisk wyślij, przykładem takiego formularza jest na przykład formularz kontaktowy na blogu. Zazwyczaj jedynymi usprawnieniami formularza są komunikaty o błędnie wypełnionych polach oraz informacje wspomagające wprowadzanie danych.

W tym wpisie przedstawię sposób na wykonanie efektu ruchomej etykiety. Efekt jest nieinwazyjny, to znaczy nie odprasza zbytnio uwagi użytkownika od pól do wypełnienia a dodaje ciekawy akcent do nudnych formularzy.

Kod HTML formularza

Animated floating labels

Aby móc przystąpić do zabawy z efektami pól w formularzu musimy przygotować odpowiednią strukturę kodu, która będzie podstawą do osiągnięcia wyglądu przedstawionego na powyższym obrazku:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<form class="form-login">
    <div class="form__content">
        <ul class="form__content__inputs">
            <li class="form__content__input-container">
                <span class="form__content__input-addon fa fa-user"></span>
                <input class="form__content__input" type="text" name="user-login" id="user-login">
                <label class="form__content__label" for="user-login">Nazwa użytkownika</label>
            </li>
            <li class="form__content__input-container">
                <span class="form__content__input-addon fa fa-lock"></span>
                <input class="form__content__input" type="password" name="user-pass" id="user-pass">
                <label class="form__content__label" for="user-pass">Hasło</label>
            </li>
            <li>
                <button type="submit" class="form__content__submit">Zaloguj się</button>
            </li>
        </ul>
    </div>
</form>

Jak możesz zauważyć, wiersz zawierający pole input posiada również 2 inne elementy. Jeden pełniący rolę czysto wizualną, odpowiedzialną za wyświetlenie grafiki ilustrującej typ pola. Natomiast drugi element jest etykietą pola tekstowego, którą będziemy animować za pomocą kodu CSS.

Kod CSS odpowiedzialny za wygląd formularza

Pora na trochę magii CSS. Jak wielokrotnie wspominałem na tym blogu wraz z postępem implementacji funkcjonalności CSS3 w przeglądarkach internetowych webmasterzy mogą sobie pozwalać na zaimplementowaniu coraz ciekawszych projektów wyglądu strony przy wsparciu animacji, cieni, itp., itd.
W poniższym kodzie CSS możesz się przyjrzeć sposobowi implementacji animacji etykiet. Efekt jest bardzo prosty do osiągnięcia:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
* {
    box-sizing: border-box;
}
 
body {
    font: 100%/1.4 'Roboto', Arial, sans-serif;
    margin: 0;
    height: 100%;
}
 
form {
    border-radius: .125rem;
}
 
form button {
    border: 0;
    background: none;
    cursor: pointer;
}
 
.form-login {
    background: #4fc1e9;
    width: 20rem;
    margin: 1rem auto;
    padding-top: .5rem;
    display: block;
    opacity: 1;
}
 
.form__content {
    background: #fff;
    padding: 0 1rem;
}
 
.form__content__inputs {
    padding: 0;
    margin: 0;
    list-style: none;
    text-align: center;
}
 
.form__content__input-container {
    position: relative;
    border-radius: .125rem;
    border: 1px solid #4fc1e9;
    margin-bottom: 1.5rem;
}
 
.form__content__input-container:after {
    content: '';
    display: table;
    clear: both;
}
 
.form__content__input-addon {
    float: left;
}
 
.form__content__input {
    float: right;
    padding: 0 .5rem;
}
 
.form__content__label {
    position: absolute;
    top: 0;
    left: 3.75rem;
    color: #a4aab2;
    transition: all .2s ease-in-out;
    line-height: 2.75;
    z-index: 2;
}
 
.form__content__input-addon {
    width: 2.75rem;
    height: 2.75rem;
    line-height: 2.75;
    background: #f0f2f5;
    color: #b7babf;
}
 
.form__content__input {
    line-height: 2.75;
    color: #a4aab2;
    width: calc(100% - 2.75rem);
    height: 2.75rem;
    border: 0;
    outline: 0;
}
 
.form__content__input:focus + .form__content__label ,
.form__content__input.is-filled + .form__content__label {
    font-size: .75rem;
    line-height: .75rem;
    -webkit-transform: translateY(-150%);
            transform: translateY(-150%);
    color: #777;
}
 
.form__content__submit {
    width: 10.25rem;
    line-height: 2.5;
    color: #fff;
    background: #4fc1e9;
    border-radius: .125rem;
    box-shadow: 0 .25rem 0 0 #3bafda;
    cursor: pointer;
    text-transform: uppercase;
    transition: all .2s ease-in-out;
}
 
.form__content__submit:hover,
.form__content__submit:focus {
    background: #1cacde;
    transition: all .2s ease-in-out;
}

Analizując powyższy kod CSS możesz się natknąć na klasę .is-filled. Jest ona wykorzystywana za pomocą kodu JavaScript, który sprawdza czy pole nie jest puste. Dzięki temu, unikniemy problemu powracającej na miejsce etykiety, co byłoby niepożądanym zdarzeniem w przypadku wypełnionego pola.

Sprawdzanie, za pomocą Javascript, czy pole jest wypełnione

Aby sprawdzić czy pola tekstowe w formularzu są puste czy nie, nie potrzebujemy jQuery (zakładam, że wspierasz najnowsze przeglądarki, tj. od IE10 wzwyż i najnowsze wersje Firefoksa, Google Chrome, Opery i Safari). Wykorzystamy funkcjonalności udostępnione wraz z rozwojem specyfikacji HTML5, tj. classlist API oraz usprawnione selektory JS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
(function () {
    // pobieramy wszystkie formularze na stronie
    var forms = document.querySelectorAll('form');
    // pobieramy wszystkie pola formularzy na stronie
    var inputs = document.querySelectorAll('.form__content__input');
    var isFilledClass = 'is-filled';
 
    // konwertujemy obiekt typu HTML NodeList do obiektu typu tablicowego
    forms = Array.prototype.slice.call(forms);
    inputs = Array.prototype.slice.call(inputs);
 
    // dla każdego formularza
    forms.forEach(function (form) {
        // przypisujemy obsługę zdarzenia `change`
        form.addEventListener('change', function (event) {
            var target = event.target;
 
            // jeśli element na którym miało miejsce zdarzenie `change`
            // nie jest polem formularza, to zakończ przetwarzanie
            if (!target.classList.contains('form__content__input')) {
                return;
            }
 
            // jeśli pole nie jest puste
            if (target.value !== '') {
                // dodaj klasę
                target.classList.add(isFilledClass);
            } else { // jeśli jest puste
                // usuń klasę
                target.classList.remove(isFilledClass);
            }
        });
    });
})();

Co robi zaprezentowany przeze mnie kawałek kodu JS omówiłem za pomocą komentarzy w kodzie. Warto zauważyć, że w kodzie konwertuję obiekt typu HTML Nodelist do obiektu typu Array, dzięki czemu mogę bezproblemowo skorzystać z dobrodziejstw funkcji foreach(). Bez tej konwersji, ten sam cel moglibyśmy osiągnąć stosując zwykłą pętlę for(;;){}.

Ponadto, zastosowałem delegację obsługi zdarzenia na cały formularz. Dzięki temu zamiast podpinać obsługę zdarzenia do każdego z pól formularza osobno podpiąłem obsługę zdarzenia do formularza. Dzięki temu jakość kodu znacząco wzrasta, bo zamiast kilku elementów obsługujących dane zdarzenie jest tylko jedno.

Podsumowanie

Mając tak przygotowany kod możemy być pewni, że wypełnianie pól formularza będzie odrobinę ciekawszym i przyjemniejszym zajęciem niż wcześniej. Mam nadzieję, że przedstawiony przeze mnie sposób na osiągnięcie animowanej etykiety okaże się dla Ciebie użyteczny oraz przedstawiony przeze mnie sposób na uniknięcie wykorzystania jQuery w kodzie JS swojej strony internetowej sprawi, że zaczniesz unikać jQuery jak tylko to jest możliwe.

Demo możesz zobaczyć poniżej:

  • Z tą techniką trzeba uważać: http://www.webaxe.org/floated-labels-still-suck/
    Tutaj zwłaszcza jest problem, gdy JS jest wyłączony – wówczas etykietka nie znika. Ale… myślę, że przy pomocy małego tricku to samo można osiągnąć w samym CSS 😉 Łatwo zauważyć, że obydwa pola są wymagane. Czemu nie dowalić im [required] i ostylować odpowiednio label dla :invalid i :valid? Tym samym JS przestanie być potrzebny. Oczywiście taki trick nie zadziała wszędzie i zawsze, ale w tym wypadku – jak najbardziej by się sprawdził.

    >Wykorzystamy funkcjonalności udostępnione wraz z rozwojem specyfikacji HTML5, tj. classlist API oraz usprawnione selektory JS
    A one nie są częścią specyfikacji DOM Living Standard?

    >Warto zauważyć, że w kodzie konwertuję obiekt typu HTML Nodelist do obiektu typu Array, dzięki czemu mogę bezproblemowo skorzystać z dobrodziejstw funkcji foreach()
    Ahh, zatem polecam spojrzeć na piękno nowego DOM 4/Living Standard 😉 Gdzie zamiast NodeList dostaniemy Elements, które nie są już array-like object, lecz pełnoprawną subklasą Array (ES6 – FTW!).

    A co blokuje adopcję nowego DOM?
    „The getElementById() method is not on elements for compatibility with older versions of jQuery. If a time comes where that version of jQuery has disappeared, we might be able to support it.”

    Cóż, to dość ironiczne – zwłaszcza, że nowy DOM to de facto kopia 1:1 wielu features z tej biblioteki 😉