Jak pokonać ograniczenie zagnieżdżania formularzy HTML?

199

Wiem, że XHTML nie obsługuje zagnieżdżonych znaczników formularzy i przeczytałem już tutaj inne odpowiedzi na temat przepełnienia stosu dotyczące tego tematu, ale wciąż nie znalazłem eleganckiego rozwiązania tego problemu.

Niektórzy twierdzą, że nie potrzebujesz go i że nie mogą wymyślić scenariusza, gdyby był on potrzebny. Cóż, osobiście nie mogę wymyślić scenariusza, w którym go nie potrzebowałem.

Zobaczmy bardzo prosty przykład:

Tworzysz aplikację blogową i masz formularz z kilkoma polami do tworzenia nowego postu oraz pasek narzędzi z „czynnościami”, takimi jak „Zapisz”, „Usuń”, „Anuluj”.

<form
 action="/post/dispatch/too_bad_the_action_url_is_in_the_form_tag_even_though_conceptually_every_submit_button_inside_it_may_need_to_post_to_a_diffent_distinct_url"
method="post">

    <input type="text" name="foo" /> <!-- several of those here -->
    <div id="toolbar">
        <input type="submit" name="save" value="Save" />
        <input type="submit" name="delete" value="Delete" />
        <a href="/home/index">Cancel</a>
    </div>
</form>

Naszym celem jest napisanie formularza w sposób niewymagający JavaScript , tylko zwykły stary formularz HTML i przyciski przesyłania.

Ponieważ adres URL akcji jest zdefiniowany w znaczniku Form, a nie w każdym przycisku wysyłania, naszą jedyną opcją jest opublikowanie pod ogólnym adresem URL, a następnie rozpoczęcie „jeśli ... to ... inaczej”, aby określić nazwę przycisku, który został złożony. Niezbyt elegancki, ale nasz jedyny wybór, ponieważ nie chcemy polegać na JavaScript.

Jedynym problemem jest to, że naciśnięcie „Usuń” spowoduje przesłanie WSZYSTKICH pól formularza na serwerze, mimo że jedyną rzeczą potrzebną do wykonania tej akcji jest Ukryte wejście z identyfikatorem post-id. Niezbyt duża sprawa w tym małym przykładzie, ale mam formularze z setkami (że tak powiem) pól i zakładek w moich aplikacjach LOB , które (z powodu wymagań) muszą przesyłać wszystko za jednym razem, a w każdym razie wydaje się to bardzo nieefektywne i marnotrawstwo. Gdyby obsługiwane było zagnieżdżanie formularzy, mógłbym przynajmniej owinąć przycisk wysyłania „Usuń” wewnątrz własnego formularza tylko polem post-id.

Możesz powiedzieć „Po prostu zaimplementuj„ Usuń ”jako link zamiast przesyłać”. Byłoby to niewłaściwe na tak wielu poziomach, ale co najważniejsze, ponieważ działania niepożądane, takie jak „Usuń” tutaj, nigdy nie powinny być żądaniem GET.

Więc moje pytanie (szczególnie do tych, które mówią, że nie potrzebują zagnieżdżania formularzy) brzmi: co robisz? Czy jest jakieś eleganckie rozwiązanie, którego mi brakuje, a sedno brzmi: „Wymagaj JavaScript lub prześlij wszystko”?

gCoder
źródło
17
To zabawne, ale XHTML pozwala na pośrednie zagnieżdżanie formularzy zgodnie z interesującą uwagą anderwald.info/internet/nesting-form-tags-in-xhtml - (X) HTML nie zezwala na zagnieżdżanie formularzy takich jak „formularz> formularz”, ale zezwala na „formularz> zestaw pól> form ”, walidator W3 mówi, że jest poprawny, ale przeglądarki mają takie błędy.
Nishi
linkrot fix dla komentarza @ Nishi link: web.archive.org/web/20170420110433/http://anderwald.info/…
alberta

Odpowiedzi:

53

Zaimplementowałbym to dokładnie tak, jak opisałeś: prześlij wszystko na serwer i zrób prosty jeśli / jeszcze, aby sprawdzić, który przycisk został kliknięty.

A następnie zaimplementowałbym wywołanie JavaScript wiążące w zdarzeniu onsubmit formularza, które sprawdzałoby przed przesłaniem formularza i przesyłało tylko odpowiednie dane do serwera (być może poprzez drugi formularz na stronie z identyfikatorem potrzebnym do przetworzenia rzeczy jako ukryte dane wejściowe lub odśwież lokalizację strony danymi, które musisz przekazać jako żądanie GET, lub opublikuj post Ajax na serwerze lub ...).

W ten sposób osoby bez Javascript mogą dobrze korzystać z formularza, ale obciążenie serwera jest równoważone, ponieważ ludzie, którzy mają Javascript, przesyłają tylko jeden potrzebny element danych. Skoncentrowanie się na wspieraniu tylko jednego z nich naprawdę niepotrzebnie ogranicza twoje opcje.

Alternatywnie, jeśli pracujesz za korporacyjną zaporą sieciową lub coś takiego i wszyscy mają wyłączoną obsługę Javascript, możesz chcieć zrobić dwie formy i popracować nad magią CSS, aby wyglądała, jakby przycisk usuwania był częścią pierwszej formy.

Jedna kredka
źródło
15
oryginał OP nie zawierał javascript
Jason
26
Problem z tą metodą polega na tym, że nie jest ona w stanie RESTful. Zapisz i usuń odpowiadają różnym działaniom na zasobie: „Zapisz” -> POST / my_resource (tworzenie nowego zasobu) „Save” -> PUT / my_resource (modyfikacja istniejącego zasobu) „Delete” -> DELETE / my_resource (zniszcz zasób) OCZYWIŚCIE, problem polega na tym, że test POST powinien utworzyć nowy zasób. Z pewnością nigdy nie powinien usuwać istniejącego zasobu.
user132447,
177

Wiem, że to stare pytanie, ale HTML5 oferuje kilka nowych opcji.

Pierwszym z nich jest oddzielenie formularza od paska narzędzi w znacznikach, dodanie innego formularza dla akcji usuwania i powiązanie przycisków na pasku narzędzi z odpowiednimi formularzami za pomocą formatrybutu.

<form id="saveForm" action="/post/dispatch/save" method="post">
    <input type="text" name="foo" /> <!-- several of those here -->  
</form>
<form id="deleteForm" action="/post/dispatch/delete" method="post">
    <input type="hidden" value="some_id" />
</form>
<div id="toolbar">
    <input type="submit" name="save" value="Save" form="saveForm" />
    <input type="submit" name="delete" value="Delete" form="deleteForm" />
    <a href="/home/index">Cancel</a>
</div>

Ta opcja jest dość elastyczna, ale w oryginalnym poście wspomniano również, że może być konieczne wykonywanie różnych działań za pomocą jednego formularza. HTML5 znów na ratunek. Możesz użyć formactionatrybutu w przyciskach przesyłania, aby różne przyciski w tej samej formie mogły przesyłać różne adresy URL. Ten przykład po prostu dodaje metodę klonowania do paska narzędzi poza formularzem, ale działałby tak samo zagnieżdżony w formularzu.

<div id="toolbar">
    <input type="submit" name="clone" value="Clone" form="saveForm"
           formaction="/post/dispatch/clone" />
</div>

http://www.whatwg.org/specs/web-apps/current-work/#attributes-for-form-submission

Zaletą tych nowych funkcji jest to, że robią to wszystko deklaratywnie bez JavaScript. Wadą jest to, że nie są one obsługiwane w starszych przeglądarkach, dlatego w starszych przeglądarkach trzeba by było wykonać polifilling.

shovavnik
źródło
4
+1 - ale byłoby form=miło wiedzieć, do czego służy przeglądarka - CanIUse tak naprawdę nie mówi. caniuse.com/#feat=forms
AKX
1
To prawda, jest to doskonałe, ale na dzień dzisiejszy funkcja nieobsługiwana przez IE nadal dyskwalifikuje ją do użytku „produkcyjnego”
Jako
3
<input>Deklaracja przycisków jest złą praktyką . <button>element został wprowadzony 15 lat temu do tego celu. Naprawdę ludzie.
Nicholas Shanks,
27
Twój punkt widzenia jest niepotrzebnie kontrowersyjny i nie ma znaczenia dla pytania PO. OP zapytał o ograniczenie zagnieżdżania formularzy bez użycia JavaScript, a odpowiedź oferuje rozwiązanie. To, czy używasz, <input>czy <button>elementów, nie ma znaczenia, chociaż przyciski są zwykle bardziej odpowiednie dla pasków narzędzi, jak mówisz. Nawiasem mówiąc, elementy przycisków nie są obsługiwane przez wszystkie przeglądarki od 15 lat.
shovavnik
6
@AKX Chociaż jest to stare oświadczenie, można wyświetlić obsługę tej funkcji tutaj: html5test.com/compare/feature/form-association-form.html . IE to odpis, większość innych przeglądarek komputerowych wydaje się tolerowana.
tłusty
16

czy możesz mieć formularze obok siebie na stronie, ale nie zagnieżdżone. następnie użyć CSS, aby wszystkie przyciski były w porządku?

<form method="post" action="delete_processing_page">
   <input type="hidden" name="id" value="foo" />
   <input type="submit" value="delete" class="css_makes_me_pretty" />
</form>

<form method="post" action="add_edit_processing_page">
   <input type="text" name="foo1"  />
   <input type="text" name="foo2"  />
   <input type="text" name="foo3"  />
   ...
   <input type="submit" value="post/edit" class="css_makes_me_pretty" />
</form>
Jason
źródło
5
Tak i wydaje się to zbyt hackerskie. Dodatkowo na bardzo dużej stronie (wyobraź sobie karty i podkarty oraz zagnieżdżanie przycisków przesyłania na kilku poziomach zakresu) prawie niemożliwe jest całkowite oddzielenie pozycji ekranu elementów od ich pozycji w dokumencie.
gCoder
3
cóż, zawsze jest równowaga między tym, co chcesz robić, a tym, co możesz zrobić. wygląda na to, że są to opcje - albo jedna forma obsługiwana na serwerze, albo jedna wiele formularzy, które stają się trudne do utrzymania - wybierz mądrze :)
Jason
3
Tak, niestety, z tymi wszystkimi ograniczeniami w html, po prostu będę trzymać się tego, co jest obsługiwane i wyślę cały formularz na serwer, a następnie dyskretnie użyję javascript dla klientów, którzy go obsługują, aby przechwycić przesłanie formularza i wysłać tylko to, co jest naprawdę potrzebne. To najlepszy kompromis.
gCoder
13

HTML5 ma pojęcie „właściciel formularza” - atrybut „forma” dla elementów wejściowych. Pozwala emulować zagnieżdżone formularze i rozwiązuje problem.

Nishi
źródło
1
Wszelkie odniesienia do tego warte linkowania?
dakab
Zobacz w3.org/TR/html5/forms.html#form-owner „Element powiązany z formularzem jest domyślnie powiązany z najbliższym elementem formularza przodka, ale jeśli można go ponownie powiązać, może mieć określony atrybut formularza do zastąpienia to."
Nishi
11

Niby stary temat, ale ten może być przydatny dla kogoś:

Jak ktoś wspomniano powyżej - możesz użyć fikcyjnego formularza. Jakiś czas temu musiałem rozwiązać ten problem. Na początku całkowicie zapomniałem o tym ograniczeniu HTML i po prostu dodałem zagnieżdżone formularze. Wynik był interesujący - straciłem pierwszą formę z zagnieżdżonego. Potem okazało się, że to „sztuczka” polegająca na dodaniu fikcyjnego formularza (który zostanie usunięty z przeglądarki) przed faktycznymi zagnieżdżonymi formularzami.

W moim przypadku wygląda to tak:

<form id="Main">
  <form></form> <!--this is the dummy one-->
  <input...><form id="Nested 1> ... </form>
  <input...><form id="Nested 1> ... </form>
  <input...><form id="Nested 1> ... </form>
  <input...><form id="Nested 1> ... </form>
  ......
</form>

Działa poprawnie z Chrome, FireFox i Safari. IE do 9 (nie jestem pewien co do 10) i Opera nie wykrywa parametrów w głównej formie. Globalny $ _REQUEST jest pusty, niezależnie od danych wejściowych. Formy wewnętrzne wydają się wszędzie dobrze działać.

Nie przetestowałem innej opisanej tutaj sugestii - zestaw pól wokół zagnieżdżonych form.

EDYCJA : Zestaw ramek nie działał! Po prostu dodałem formularz Główny po pozostałych (nie zagnieżdżonych) i użyłem „klonu” jQuery'ego, aby powielić dane wejściowe w formularzu po kliknięciu przycisku. Dodano .hide () do każdego sklonowanego wejścia, aby zachować niezmieniony układ, a teraz działa jak urok.

BDJoe
źródło
To działało idealnie dla mnie. Pracowałem nad stroną asp.net, która miała całkowicie zamknięty formularz. Miałem wewnętrzną zagnieżdżoną formę do użycia dla wtyczki jQuery Validation ( github.com/jzaefferer/jquery-validation ) i chociaż działała idealnie w FireFox i IE, nie działała w Chrome z elementem „TypeError: Cannot call method” elementu nieokreślony'. Okazało się, że wywołanie sprawdzania poprawności () nie powiodło się, ponieważ nie można było znaleźć wewnętrznej formy, ponieważ Chrome usunął ją z bloku html dodanego za pomocą jQuery.html (). Dodanie małego manekina sprawiło, że Chrome opuścił prawdziwy.
John - Not A Number
To wydaje się już nie działać. Firefox usunął drugi znacznik <form>, a następnie zakończył mój formularz pierwszym znacznikiem </form>. Spowodowało to pozostawienie niepoprawnego tagu </form> u dołu strony i formularza, który nie zostanie poprawnie przesłany. :(
Tarquin
Bardzo pożytecznie dla mnie. Dodaję widżet sieciowy zawierający formularz do strony asp.net. Zaskoczony przez WebForm, że zawsze generuje formularz obejmujący wszystko w treści HTML. Otaczający formularz staje się problemem dla widgetu internetowego. Atrapa wydaje się być szybkim i skutecznym rozwiązaniem tej sytuacji. Działa doskonale w Chrome i Safari, jak dotąd nie przetestowałem Firefoksa. Dziękuję za dzielenie się, oszczędziłeś mi dużo czasu!
JM Yang,
4

Myślę, że Jason ma rację. Jeśli czynność „Usuń” jest minimalna, ułóż ją w formie samodzielnie i ustaw w linii z innymi przyciskami, aby interfejs wyglądał jak jedna zunifikowana forma, nawet jeśli nie jest.

Lub, oczywiście, przeprojektuj swój interfejs i pozwól ludziom usunąć gdzie indziej całkowicie, co nie wymaga od nich wcale zobaczenia formularza.

AmbroseChapel
źródło
2

Jednym ze sposobów, w jaki zrobiłbym to bez javascript, byłoby dodanie zestawu przycisków opcji, które określają akcję, którą należy podjąć:

  • Aktualizacja
  • Usunąć
  • Cokolwiek

Następnie skrypt akcji podejmowałby różne działania w zależności od wartości zestawu przycisków opcji.

Innym sposobem byłoby umieszczenie dwóch formularzy na stronie, tak jak sugerowałeś, ale nie zagnieżdżonych. Układ może być trudny do kontrolowania:

<form name = "editform" action = "the disease_url" method = "post">
   <input type = "hidden" name = "task" value = "update" />
   <input type = "text" name = "foo" />
   <input type = "upload" name = "save" value = "Save" />
</form>

<form name = "delform" action = "the disease_url" method = "post">
   <input type = "hidden" name = "task" value = "delete" />
   <input type = „hidden” name = „post_id” wartość = „5” />
   <input type = "upload" name = "delete" value = "Delete" />
</form>

Użycie ukrytego pola „zadania” w skrypcie obsługi do odpowiedniego rozgałęzienia.

Ron Savage
źródło
1

Ta dyskusja wciąż mnie interesuje. Za oryginalnym postem znajdują się „wymagania”, które wydaje się, że PO dzieli - tj. Formularz z kompatybilnością wsteczną. Jako ktoś, kogo praca w momencie pisania musi czasem obsługiwać z powrotem do IE6 (i przez wiele lat), kopie to.

Bez popychania frameworka (wszystkie organizacje będą chciały się upewnić co do kompatybilności / solidności, a ja nie używam tej dyskusji jako uzasadnienia frameworka), rozwiązania Drupala w tej kwestii są interesujące. Drupal ma również bezpośrednie znaczenie, ponieważ framework ma długą politykę: „powinien działać bez Javascript (tylko jeśli chcesz)”, tj. Problem PO.

Drupal używa dość obszernych funkcji form.inc, aby znaleźć element wyzwalający (tak, taka jest nazwa w kodzie). Zobacz dolną część kodu wymienionego na stronie API dla form_builder (jeśli chcesz zagłębić się w szczegóły, zalecane jest źródło - drupal-x.xx / include / form.inc ). Konstruktor korzysta z automatycznego generowania atrybutów HTML, dzięki czemu może po powrocie wykryć, który przycisk został naciśnięty, i działać odpowiednio (można je skonfigurować tak, aby uruchamiały również osobne procesy).

Oprócz konstruktora formularzy Drupal dzieli akcje „usuń” dane na osobne adresy URL / formularze, prawdopodobnie z powodów wymienionych w oryginalnym poście. Wymaga to pewnego rodzaju kroku wyszukiwania / wpisu ( jęczenie w innej formie !, ale jest przyjazny dla użytkownika) jako wstępne. Ma to jednak tę zaletę, że eliminuje problem „prześlij wszystko”. Duża forma z danymi jest używana zgodnie z przeznaczeniem, tworzeniem / aktualizowaniem danych (lub nawet działaniem „scalania”).

Innymi słowy, jednym ze sposobów obejścia problemu jest przekształcenie formularza na dwa, a następnie problem znika (a metody HTML można również poprawić za pomocą testu POST).

Rob Crowther
źródło
0

Użyj iframedla zagnieżdżonego formularza. Jeśli muszą udostępniać pola, to ... to nie jest tak naprawdę zagnieżdżone.

Dan Rosenstark
źródło
1
Korzystanie z iframe to świetny pomysł, szkoda tylko, że w większości przypadków potrzebujesz javascript, aby zmienić jego rozmiar (ponieważ nie możesz wiedzieć, jaki będzie rozmiar tekstu użytkownika, a więc jak duży będzie przycisk).
Nicholas Shanks,
@Nicholas, w jaki sposób możesz użyć Javascript, aby zmienić jego rozmiar? Iframed HTML nie może rozmawiać z ramką iframe i odwrotnie, chyba że znajdują się w tej samej domenie.
Dan Rosenstark,
@Nicholas, dzięki, chciałem tylko upewnić się, że muszą być w tej samej domenie
Dan Rosenstark
0

Moim rozwiązaniem jest, aby przyciski wywoływały funkcje JS, które zapisują, a następnie przesyłają formularze poza formularzem głównym

<head>
<script>
function removeMe(A, B){
        document.write('<form name="removeForm" method="POST" action="Delete.php">');
        document.write('<input type="hidden" name="customerID" value="' + A + '">');
        document.write('<input type="hidden" name="productID" value="' + B + '">');
        document.write('</form>');
        document.removeForm.submit();
}
function insertMe(A, B){
        document.write('<form name="insertForm" method="POST" action="Insert.php">');
        document.write('<input type="hidden" name="customerID" value="' + A + '">');
        document.write('<input type="hidden" name="productID" value="' + B + '">');
        document.write('</form>');
        document.insertForm.submit();
}
</script>
</head>

<body>
<form method="POST" action="main_form_purpose_page.php">

<input type="button" name="remove" Value="Remove" onclick="removeMe('$customerID','$productID')">
<input type="button" name="insert" Value="Insert" onclick="insertMe('$customerID','$productID')">

<input type="submit" name="submit" value="Submit">
</form>
</body>
scotweb
źródło
-1

Jeśli naprawdę nie chcesz używać wielu formularzy (jak sugeruje Jason), użyj przycisków i programów obsługi onclick.

<form id='form' name='form' action='path/to/add/edit/blog' method='post'>
    <textarea name='message' id='message'>Blog message here</textarea>
    <input type='submit' id='save' value='Save'>
</form>
<button id='delete'>Delete</button>
<button id='cancel'>Cancel</button>

A potem w javascript (używam jQuery tutaj dla łatwości) (nawet jeśli jest to dość przesada w dodawaniu niektórych programów obsługi onclick)

$('#delete').click( function() {
   document.location = 'path/to/delete/post/id'; 
});
$('#cancel').click( function() {
   document.location = '/home/index';
});

Wiem też, że spowoduje to, że połowa strony nie będzie działać bez javascript.

Pim Jager
źródło
1
Chyba że coś przeoczyłem, nie jest to lepsze niż linkowanie do akcji usuwania: końcowym rezultatem będzie żądanie GET do akcji usuwania (złe). W rzeczywistości jest nieco grubszy, ponieważ wymaga JavaScript, aby osiągnąć coś, co mógłby zrobić zwykły stary tag link.
Kyle Fox,
-1

W odpowiedzi na pytanie zadane przez Yara w komentarzu do jego własnej odpowiedzi przedstawiam JavaScript, który zmieni rozmiar iframe. W przypadku przycisku formularza można bezpiecznie założyć, że element iframe będzie w tej samej domenie. To jest kod, którego używam. Będziesz musiał zmienić matematykę / stałe swojej witryny:

function resizeIFrame(frame)
{
    try {
        innerDoc = ('contentDocument' in frame) ? frame.contentDocument : frame.contentWindow.document;
        if('style' in frame) {
            frame.style.width = Math.min(755, Math.ceil(innerDoc.body.scrollWidth)) + 'px';
            frame.style.height = Math.ceil(innerDoc.body.scrollHeight) + 'px';
        } else {
            frame.width = Math.ceil(innerDoc.body.scrollWidth);
            frame.height = Math.ceil(innerDoc.body.scrollHeight);
        }
    } catch(err) {
        window.status = err.message;
    }
}

Nazwij to tak:

<iframe ... frameborder="0" onload="if(window.parent && window.parent.resizeIFrame){window.parent.resizeIFrame(this);}"></iframe>
Nicholas Shanks
źródło
-1

Właśnie wymyśliłem fajny sposób na zrobienie tego z jquery.

<form name="mainform">    
    <div id="placeholder">
    <div>
</form>

<form id="nested_form" style="position:absolute">
</form>

<script>
   $(document).ready(function(){
      pos = $('#placeholder').position();
      $('#nested_form')
         .css('left', pos.left.toFixed(0)+'px')
         .css('top', pos.top.toFixed(0)+'px');
   });
</script>
mordy
źródło
-1

Cóż, jeśli prześlesz formularz, przeglądarka wyśle ​​również wejściową nazwę i wartość przesłania. Więc co możesz zrobić, to

<form   
 action="/post/dispatch/too_bad_the_action_url_is_in_the_form_tag_even_though_conceptually_every_submit_button_inside_it_may_need_to_post_to_a_diffent_distinct_url"  
method="post">  

    <input type="text" name="foo" /> <!-- several of those here -->  
    <div id="toolbar">
        <input type="submit" name="action:save" value="Save" />
        <input type="submit" name="action:delete" value="Delete" />
        <input type="submit" name="action:cancel" value="Cancel" />
    </div>
</form>

więc po stronie serwera po prostu szukasz parametru, który rozpoczyna szerokość ciągu „akcja:”, a reszta mówi ci, jaką akcję podjąć

więc po kliknięciu przycisku Zapisz przeglądarka wysyła ci coś takiego jak foo = asd & action: save = Save

Lauri Lüüs
źródło
-1

Obejrzałem ten problem, dodając pole wyboru w zależności od formy, jaką osoba chciała zrobić. Następnie użyłem 1 przycisku, aby przesłać cały formularz.

Gregws
źródło
2
Witamy w Stack Overflow . Lepiej jest opisać, jak to rozwiązuje problem, niż po prostu powiedzieć, co zrobiłeś. Zobacz, jak odpowiedzieć, aby uzyskać porady dotyczące tworzenia świetnych odpowiedzi na temat przepełnienia stosu. Dziękuję Ci!
Qantas 94 Heavy
-2

Alternatywnie możesz przypisać formularz actiob w locie ... może nie być najlepszym rozwiązaniem, ale na pewno zwalnia logikę po stronie serwera ...

<form name="frm" method="post">  
    <input type="submit" value="One" onclick="javascript:this.form.action='1.htm'" />  
    <input type="submit" value="Two" onclick="javascript:this.form.action='2.htm'" />  
</form>
Qiniso
źródło
Czemu? Pytam poważnie, ponieważ nie ma łatwego sposobu, aby dowiedzieć się, który przycisk został kliknięty. Czy mówisz, że do ustawienia programu obsługi przycisku onClick należy użyć czegoś takiego jak metoda click () jQuery? A może problemem jest obsługa zdarzenia kliknięcia przed przesłaniem formularza?
Zack Morris,