Autouzupełnianie w polach formularza. Odc.1.

Słowo wstępu o Autocomplete

Jak już pisałam w artykule Nadszedł czas na jQuery przyszło mi się zmagać z problemem podpowiedzi przy uzupełnianiu formularza. Oczywiście przy małej ilości danych można się pokusić o zastosowanie elementów typu <select> ale gdy w grę wchodzą podpowiedzi pochodzące z bazy danych zawierającej tysiące rekordów a w dodatku chcemy pozostawić użytkownikowi możliwość wpisania czegoś spoza tej puli danych, pozostaje nam już tylko autouzupełnianie.

Z takim właśnie przypadkiem się zetknęłam. W formularzu który przyszło mi obsłużyć były nazwy miejscowości, gmin i ulic z całej Polski. Żaden <select> by tego nie wytrzymał. Dlatego sięgnęłam po plugin Autocomplete dla JavaScriptowego frameworka jQuery

Oczywiście, żeby móc skorzystać z dobrodziejstw tego frameworka i pluginu trzeba je do swojego projektu dołączyć. To akurat najprostsze w naszym zadaniu.
Trzeba ściągnąć paczkę z pluginem Autocomplete i rozpakować w jakimś wygodnym katalogu (niech to będzie na przykład katalog ./jq-ac). W tej paczce jest już samo jQuery. Następnie w nagłówku dokumentu należy dopisać kilka linii:

<script src="./jq-ac/lib/jquery.js" type="text/javascript"></script><script src="./jq-ac/lib/jquery.dimensions.js" type="text/javascript"></script>
<script src="./jq-ac/jquery.autocomplete.js" type="text/javascript"></script>

Wpierw dołączamy arkusz stylów zdefiniowanych dla tego pluginu. Oczywiście można go modyfikować, albo stworzyć na jego wzór inny. Następnie dołączamy sam framework jQuery i dwa pliki w których zdefiniowany jest nasz plugin. I już po krzyku.

Uruchamiamy autouzupełnianie

W najprostszym przypadku sposób użycia tego pluginu jest banalny i nie powinien nastręczać problemów nikomu kto choćby odrobinę popróbował JavaScriptu. Aby wyjaśnić w czym rzecz zacznę od skonstruowania formularza:

<form action="" method="post" name="ankieta"><input id="urzadzenia" name="urzadzenia" type="text" /> 
 <input type="submit" value="Akceptuj" />
<table>
<tbody>
<tr>
<th colspan="2">Formularz z podpowiedziami</th>
</tr>
<tr>
<td>Urządzenia</td>
</tr>
</tbody>
</table>
</form>

Nic szczególnego. Jedno pole typu <input/>, przycisk <submit/> i wszystko w tabli (nie jest to szczyt elegancji), żeby było równo. Aby podłączyć podpowiedzi do pola <input/> skorzystamy z funkcji autocomplete() W tym celu w nagłówku dokumentu dopisujemy następujący skrypt:

<script type="text/javascript">// <![CDATA[
$(document).ready(
   function (){
     $("input#urzadzenia").autocomplete("jq_urzadzenia.php",{width: 200,max: 10,selectFirst: false,	cacheLength: 1});
});
// ]]></script>

W lini nr 4 tego skryptu widzimy wywołanie funkcji autocomplete() dla elementu <input/> o id=”urzadzenia”, czyli dla jedynego elementu tego typu w naszym formularzu. Oczywiście jeśli jest tylko jedne nie musimy go szukać po id, ale lepiej wyrobić sobie taki nawyk. Przecież formularz rzadko składa się z jednego okienka <input/>.

W naszym przykładzie funkcja autocomplete() przyjmuje dwa parametry. Pierwszy z nich definiuje źródło danych, drugi to opcje. O opcjach nie będę się szczegółowo rozpisywać. Nie widzę sensu przepisywania dokumentacji , coś nieco tylko wspomnę o niektórych. Bardziej istotny jest pierwszy parametr. Może to być tablica z danymi, lub jak to jest w naszym przypadku plik, który dane będzie generował dynamicznie. Do takiego skryptu metogą get przekazywany jest niejawny parametr ‘q’, którym jest string wpisany do okienka <input/>. Cała sztuka więc polega na tym, żeby odebrać tę zmienną i sprawdzić które pozycje z listy pasują do wpisanego stringu. Poniżej najprostszy skrypt z pliku jq_urzadzenia.php

<?php
$q = $_GET['q'];
if(!$q)
   return;

$dane = array('aparat','motor','rower','samochód','telefon','telewizor'); 

$i = 0;
foreach ($dane as $id => $wartosc) {
   if(preg_match('/^'.$q.'/', $wartosc)){
      echo $wartosc.PHP_EOL;
      $i++;
   }
}

if(!$i) echo "urz±dzenie spoza listy\n";

?>

Skrypt pobiera wspomniany już parametr ‘q’, jeśli z jakiegoś powodu nie ma on wartości to kończy swoje działanie. W przeciwnym razie definiuje listę pozycji podpowiedzi. W tym przykładzie oczywiście statycznie, ale nic nie stoi na przeszkodzie, żeby w tym miejscu dokonać zapytania do bazy i stworzyć dynamicznie tę tablicę. Dlaczego to ma być tablica? Bo łatwo takie dane obsłużyć.

Po zdefiniowaniu tablicy zaczyna się pętla (linie 7-12), która szuka w niej wartości zgodnych wartością przekazaną w do skryptu. Pobiera kolejne łańcuchy znaków z tablicy i sprawdza, czy zawierają one łańcuch wpisany do okienka <input/>. Ja w tym celu wykorzystuję funkcję preg_match(), która korzysta z Perlowych wyrażeń regularnych a w dodatku wymuszam (poprzez zastosowanie metaznaku ^) aby wybrana pozycja zaczynała się od zadanego ciągu. Oczywiście tak skonstruowany skrypt jest czuły na wielkość liter. Można wykorzystać funkcję strtolower() na przykład modyfikując warunek w ten sposób:

if(preg_match('/^'.strtolower($q).'/', strtolower($wartosc))){

ale należy pamiętać, że ta funkcja sprawia poważne problemy w przypadku obecności polskich znaków w stringach i bezpieczniej by było samemu napisać odpowiednią funkcję. Wartości z tablicy, które spełniają zadany warunek (oczywiście może on być skonstruowany zupełnie inaczej, a ograniczeniem są tylko nasze potrzeby i wyobraźnia) są wyświetlane za pomocą funkcji echo. Ważne jest, aby zawsze na końcu znalazł się znak nowej linii, gdyż kolejne linie będą pojawiać się w okienku podpowiedzi jako niezależne pozycje.

W zaproponowanym tu skrypcie znajduje się dodatkowo licznik pozycji pasujących do wzorca. Dodałam go po to, żeby ułatwić zadanie użytkownikowi. Ponieważ jeśli w tablicy nie znajdzie się żadna pozycja pasująca do wzorca zostanie wyświetlony komunikat odpowiedni komunikat w tym wypadku: “urządzenie spoza listy”;

I to już tyle. Jeśli chcesz możesz zobaczyć jak to działa: demo

Lada moment opublikuję kolejny odcinek w którym pokażę co jeszcze można wydusić z tego pluginu.

EDIT (lipiec 2015):
Od dnia publikacji tego tutoriala minęło kilka lat a mimo to są ludzie, którzy chcą z niego skorzystać. Niestety obawiam się, że opisany tu plugin może nie działać z nowszymi wersjami jQuery stąd problemy. Postanowiłam ułątwić ściągnięcie dokładnie tych wersji skryptów jakie zostały użyte w powyższym demo, jak również w dalszych częściach tej serii tutoriali.

jquery.js
jquery.bgiframe.min.js
jquery.autocomplete.css
jquery.autocomplete.js

65 komentarzy do wpisu „Autouzupełnianie w polach formularza. Odc.1.”

  1. Witam
    Niestety nie mogę sobie poradzić z pobraniem danych z bazy mysql

    robie to w następujący sposób:

    while($dane = mysql_fetch_array($wynik))

  2. Witam Panią bardzo fajnie to Pani pokazała na przykładzie a ja szukałem autouzupełnienia i proszę tu tak przyjaźnie napisane. Mam też i pytanie do Pani, w przykładzie pokazuje Pani autouzupełnianie na przykładzie danych wprowadzonych ręcznie do skryptu: “jq_urzadzenia.php” moje pytanie brzmi czy mogła by Pani pokazać jak np pobierać dane do autouzupełnienia z bazy danych i to np określone dane z określonych tabel to by było ciekawe bo np jakbyśmy chcieli zrobić taki trik dla kodów pocztowych albo miejscowości to wpisywanie ręcznie wszystkich tych danych do skryptu php było by koszmarem. Pozdrawiam serdecznie

  3. To nic skomplikowanego. Zamiast:
    $dane = array(‘aparat’,’motor’,’rower’,’samochód’,’telefon’,’telewizor’);

    trzeba napisać:

    $dane = moja_funkcja();

    No i gdzieś trzeba tę funkcję zdefiniować tak, żeby zwracała tablicę.

    Oczywiście tablica kodów pocztowych byłaby koszmarnie długa. Są sposoby, aby pobierać tylko potrzebną część danych. Może faktycznie pokuszę się o stosowny tutorial.

  4. Witam,

    szkoda, że tak późno tutaj trafiłem :) W sumie przerobiłem skrypt aby pobierał informacje z bazy (MSSQL). Walczę teraz z kodowaniem :( Niestety mam krzaczki.. problem rozwiązuje częściowo:
    header(‘Content-Type: text/html; charset=windows-1250’);
    Częściowo ponieważ nie wyszukuje słów rozpoczynających się polskimi literami.. natomiast jak polska litera jest druga i kolejna to nie ma problemu. Przykład:
    maść -> i jest ok, prawidłowo sugeruje, prawidłowo rozpoznaje ść, wpisując maść widzi pl znaki
    natomiast słowo:
    ściana -> nie znajduje, nie daje żadnej sugestii do słów rozpoczynających się od polskich znaków śćźłż itd..
    Ma Pani jakiś pomysł na to?

    Dziękuję i pozdrawiam !

  5. Aha, czytałem :) Bazę MSSQL mam w Polish_CI_AS a strona kodowa dla tej kolacji to 1250. Wracając to mojego pytania -> problem nareszcie rozwiązałem. POST a nie GET :) Oto całe rozwiązanie :)

  6. Przepisałem dosłownie cały kod zmieniając tylko nazwę inputa, ścieżki do js i danych (gotowa tabelka w js generowana phpem)
    wpisuję:

    $(document).ready(
    function (){
    $(“input#lista_osob”).autocomplete(“osoby”,
    {width: 200,max: 10,selectFirst: false, cacheLength: 1});
    }
    );

    i za każdym razem Firebug wypisuje mi: ‘autocomplete is not a function’

  7. nazwa tabeli
    wycinek:
    var osoby = [
    {id:”00136″, imie:”Anna”},
    {id:”00145″, imie:”Adam”},
    {id:”00118″, imie:”Zygmunt”},
    ]

  8. w przykładach http://jquery.bassistance.de/autocomplete/demo/ był taki kod, ale nie za bardzo go rozumiem zwłaszcz formatItem :
    $(“#thickboxEmail”).autocomplete(emails, {
    minChars: 0,
    width: 310,
    matchContains: true,
    highlightItem: false,
    formatItem: function(row, i, max, term) {
    return row.name.replace(new RegExp(“(” + term + “)”, “gi”), “$1“) + “Email: <” + row.to + “>”;
    },
    formatResult: function(row) {
    return row.to;
    }
    });
    polami w tabeli emails były ‘to’ i ‘name’

  9. Na temat formatItem naskrobię w wolnej chwili, ale tymczasem zwróć uwagę, że nazwę tablicy wstawia się bez cudzysłowu.

  10. Te pierwsze trzy kody od góry mają sie znależć w jednym pliku index.php?
    Bo nie bardzo wiem gdzie mam te poszczególen bloki kodu wpisać. prosze o pomoc

  11. Formularz ma się znaleźć na tej stronie na której chcesz mieć formularz :P a skrypty powinny być na tej samej stronie tylko w sekcji <head>

  12. Witam,

    Odnośnie Pani wpisu : To nic skomplikowanego. Zamiast:
    $dane = array(‘aparat’,’motor’,’rower’,’samochód’,’telefon’,’telewizor’);
    trzeba napisać:
    $dane = moja_funkcja();
    No i gdzieś trzeba tę funkcję zdefiniować tak, żeby zwracała tablicę

    Czy mogę prosić o wskazówki

    Pozdrawiam

  13. Witam, Niestety nie znalazłem w następnych artykułach. Chodzi mi o dopisanie funkcji pobierającej dane z bazy i nie wiem jak się do tego zabrać

  14. No rzeczywiście. Dawno już pisałam te artykuły i nie pamiętałam co tam dalej było.

    To naprawdę proste. Zamiast linijki
    $dane = array(‘aparat’,’motor’,’rower’,’samochód’,’telefon’,’telewizor’);

    Trzeba wstawić wywołanie skryptu, który pobierze dane z bazy i zwróci stosowną tablicę. Definiujesz sobie taki skrypt, w którym będzie funkcja potrafiąca połączyć się z bazą (mysql_connect), wykonać na niej zapytanie SQL (mysql_query) i przetworzyć je do postaci potrzebnej tablicy (mysql_fetch_row). Skrypt dołączasz za pomocą include i już. Prawdę mówiąc nie bardzo rozumiem z czym masz problem. Może więcej napiszesz?

  15. Zamiast – $dane = array(‘aparat’,’motor’,’rower’,’samochód’,’telefon’,’telewizor’);

    wstawiłem :include ‘connect.php’;
    include ‘query.php’;
    a moje query wygląda tak :

    Niby ok , ale tylko pokazuje pierwszą wartość z bazy ;)

  16. A u mnie skrypt php wcale nie otrzymuje parametru. Gdy dodam jakąś zmienną $q w środku skryptu to pokazuje tylko pasujące do niej podpowiedzi, a parametru nie widzi.

  17. Mam pytanko, może laickie ale i tak je zadam. Skoro tak się udało rozpisać o tablicach to może gotowy przykładzik na bazę? Ten tu podany zawiera tylko fragment rozsiany, a tak jak to tu było zaznaczone, bardziej się przyda przykład z bazą zawierającą tysiące rekordów niż z tablicą która ma ich kilka. Czy byłaby taka możliwość by załączyć kompletny skrypt na bazę? Za cierpliwość w nadmiarze z góry dziękuję

  18. No nie żartuj. Przecież nawiązanie połączenia z bazą, wysłanie zapytania, odebranie rekordów i wrzucenie ich w pętli do tablicy to szkolne zadanie. Znajdziesz to w każdym podręczniku do PHP. A podpowiedzi sa w starszych komentarzach. Gotowca nie będę pisać. To by było mało wychowawcze, a ja mam belferską naturę ;)

  19. Kłopot mam w tym, że zakładając, iż tabela ma dwa rekordy: id, nazwisko – chciałbym z inputa odebrać id (pomimo, że uzupełniane nazwisko), i tu mam zonka. Jak z tego odebrać zmienną? Starałem się dodać tą zmienną w zapytaniu do bazy ale poległem. Pomijając fakt, że przy Polskich ogonkach najczęściej zaprezentowany skrypt omija te rekordy. Czy na prawdę nie da się tego jakoś obejść. Heh skoro belferska natura – to pytam :)

  20. I bardzo dobrze, że pytasz. Lubie dociekliwych studentów, nawet jeśli są niecierpliwi ;)

    Sądzę, że podpowiedź w związku z pierwszym pytaniem znajdziesz w drugim odcinku tej serii:
    http://blog.joanna-siwiec.pl/autouzupelnianie/62/

    Natomiast jeśli chodzi o polskie znaki, to tez się trochę z tym zmagałam i to co odkryłam opisałam tu:

    http://blog.joanna-siwiec.pl/polskie-znaki-w-php-dramat-w-czterech-aktach/65/

    Życzę przyjemnej lektury.

  21. To wtedy trzeba pokombinować. Nie sprawdziłam, ale spróbowałabym najpierw wykryć który element został kliknięty, odczytać jego ID a następnie zastosować do niego funkcję autocomplete.

  22. W moim skrypcie zaciągane są informacje z bazy danych, jednakże kiedy zapytanie nic nie znajdzie, wyświetla się komunikat:
    Warning: Invalid argument supplied for foreach() in
    on line 15

    Poniżej kod. Bardzo proszę o pomoc
    $wartosc)
    {

    if(preg_match(‘/^’.strtoupper($q).’/’, strtoupper($wartosc)))
    {
    echo $wartosc.PHP_EOL;
    $i++;
    }
    }

    if(!$i) echo “Brak danych w bazie…\n”;
    include (“../../disconnect.php4”);
    ?>

    • No to może sprawdziłbyś w manualu jaki Typ zwraca Ci funkcja kiedy nie znajdzie nic w bazie a jaki typ powinna mieć zmienna którą chcesz przeanalizować za pomocą foreach() ]:-> Polecam tez podstawowy kurs angielskiego, albo przynajmniej korzystanie z jakiegoś translatora, bo komunikat w warningu mówi ci wszystko.

  23. A jak przekazać np. dwie zmienne przez autocomplete?
    Chodzi mi o kolejny parametr wykorzystywany w zapytaniu do bazy. Niestety kiedy podaję drugi wykorzystując zmienne, skrypt przestaje działać.

  24. jednak malutka drobnostka, której poświęciłem już więcej czasu, niż zajęło mi całe zamontowanie tego skryptu na swoją stronę, a dalej nie mogę znaleźć rozwiązania: jak wyrównać propozycję do lewej (zamiast wycentrowania na liście propozycji). Tak jak jest to zrobione tutaj: http://jquery.bassistance.de/autocomplete/demo/
    Będę bardzo wdzięczny za podpowiedź :).

  25. świetny tutorial ale mam pytanie jak tą zmienną wartość przekazać dalej. chodzi mi o coś takiego że z bazy danych pobieram nazwy drużyn przez podpowiedzi i po kliknięciu pokaż wyświetlam wybrany rekord

  26. Hej, fajnie to ci to wyszlo,
    podpowiesz co mam zle ?
    cały czas: Notice: Undefined index: q

    $q = $_GET[‘q’];
    if(!$q) return;

    $q = array(‘select count(*) from akt where nr_akt like (‘%”.$q.”%’)’);
    $i = 0;
    foreach ($q as $id => $wartosc) {
    if(preg_match(‘/^’.$q.’/’, $wartosc)){
    echo $wartosc.PHP_EOL;
    $i++;
    }
    }

    if (mysql_num_rows($q)>0) {
    echo ‘jest’;
    }
    else
    {
    echo ‘niema’;
    }

  27. Hej. Mam problem, bo mi skrypt nie działa.. wrzuciłem wszystko tak jak u Ciebie, ale nadal wpisując w pole formularza, nie otrzymuje żadnej podpowiedzi.

  28. Witaj

    z tego co widzę jeśli w pliku jq_urzadzenia.php są nagłówki to wczytuje je do listy z inputa …. dziwne.
    Nie wyświetlami się pierwsza litera tak jak w przykładzie u Ciebie .
    Pozdrawiam

    Wszystko mam spisane identycznie.

Leave a Reply

%d bloggers like this: