Odeslání formuláře přes AJAX
Školení, která pořádám
Na některých stránkách mohou být formuláře, jejichž odeslání nás vrátí na stejnou stránku. Může to být třeba přidání příspěvku u diskuse nebo poptávkový formulář u detailu firmy. Tyto formuláře se pomocí AJAXu dají odeslat i bez obnovení stránky.
Odeslání konkrétního formuláře je poměrně jednoduché – stačí nastavit vyplněná políčka, nezapomenout na funkci encodeURIComponent, spojit vše pomocí &
a přiložit hlavičku Content-Type: application/x-www-form-urlencoded
.
Obecný formulář
Odeslání obecného formuláře je ale pracnější – musíme projít všechna formulářová políčka (input, select, textarea) a hodnotu nastavit u každého z nich. Speciální zacházení vyžaduje typ radio a checkbox (odesíláme jen když jsou vybrané) a také submit, reset, button, file a image (neodesíláme vůbec).
Kapitolou samou pro sebe je select – u něj musíme počítat s atributem multiple a také s tím, že option nemusí mít nastavený atribut value.
<script type="text/javascript">
/** Odeslání formuláře přes XMLHttp požadavek
* @param function funkce zajišťující obsluhu při změně stavu požadavku, dostane parametr s XMLHttp objektem
* @param string URL požadavku
* @param object formulář k odeslání
* @param object předané hlavičky ve tvaru { 'hlavička': 'obsah' }
* @return boolean true v případě úspěchu, false jinak
* @copyright Jakub Vrána, https://php.vrana.cz/
*/
function post_xmlhttprequest(state_change, url, form, headers) {
var elements = form.elements;
var content = [];
for (var i=0; i < elements.length; i++) {
if (elements[i].attributes.name && elements[i].attributes.name.specified && !elements[i].disabled) {
if (/^(select)$/i.test(elements[i].tagName)) {
var options = elements[i].options;
for (var j=0; j < options.length; j++) {
if (options[j].selected) {
var value = (options[j].attributes.value && options[j].attributes.value.specified ? options[j].value : options[j].text);
content.push(encodeURIComponent(elements[i].name) + '=' + encodeURIComponent(value));
}
}
} else if (!/^(submit|reset|button|file|image)$/i.test(elements[i].type) && (!/^(radio|checkbox)$/i.test(elements[i].type) || elements[i].checked)) {
content.push(encodeURIComponent(elements[i].name) + '=' + encodeURIComponent(elements[i].value));
}
}
}
if (!headers) {
headers = {};
}
headers['Content-Type'] = 'application/x-www-form-urlencoded';
return send_xmlhttprequest(state_change, 'POST', url, content.join('&'), headers);
}
</script>
Pokud by formulář obsahoval více odesílacích tlačítek a my bychom chtěli ve skriptu rozlišit, které chceme použít pro odeslání, bylo by možné funkci předávat místo formuláře objekt tohoto tlačítka.
Použití
Použití funkce je jednoduché – stačí ji navázat na událost onsubmit formuláře. Pak je vhodné ještě uživatele informovat o tom, že se něco děje.
<script type="text/javascript">
function odeslat(form) {
document.getElementById('stav').innerHTML = 'Odesílá se.';
return post_xmlhttprequest(function (xmlhttp) {
if (xmlhttp.readyState == 4) {
document.getElementById('stav').innerHTML = 'Odesláno.';
}
}, 'odeslat.php', form);
}
</script>
<span id="stav"></span>
<form action="" method="post" onsubmit="return !odeslat(this);">
Skript odeslat.php
je možné využít i při zpracování s vypnutým JavaScriptem a po odeslání dat přesměrovat uživatele zpátky na stránku s formulářem. Při odesílání dat AJAXem je možné odesílací formulář také zcela skrýt, aby ho uživatel neměl možnost odeslat znovu.
Přijďte si o tomto tématu popovídat na školení JavaScript a AJAX.
Diskuse
Psaní JS bez frameworku (jQuery) si dnes nedokáži vůbec představit. Urychluje mi práci zhruba o 108000% :)
pl4nt:
heh přesně, pomocí jQuery by se ten kód dál zkrátit (a "zhezčit") minimálně o polovinu
jenže vždycky je dobré vědět jak se to dělá "obtížněji" bez frameworku a pak si to teprve užívat :)
Malavon:
Když zkrátit na polovinu, počítáte do toho taky délku jQuery?
Jinak mě ta závislost na frameworcích trochu děsí. Ano, lze s nimi ušetřit spoustu práce i času, ale musí mít smysl jej použít. Často vidím, jak je někdo schopný kvůli jedné javascriptové funkci loadovat celý framework.
pl4nt:
"Když zkrátit na polovinu, počítáte do toho taky délku jQuery?"
Tak vzhledem k tomu, že v dnešní době přenesení 15kb (a navíc pouze 1x) je opravdu méně než zanedbatelné, tak ne, nepočítám.
"Často vidím, jak je někdo schopný kvůli jedné javascriptové funkci loadovat celý framework."
Tak s tímto samozřejmě souhlasím, je nesmysl na všechno použít takové obrovské frameworky, ale pokud je na webu opravdu hodně js "vymožeností", tak je to maximálně výhodné.
piler:
vies nasmerovat aj na nejaky konkretny priklad pomocou jQuery?
Juraj:
Učiteľka chémie nám raz povedala asi toto:
"Chlapci, nie je problém, aby som vám povedala, že z chemikálie č. 23 zoberte 20g, z chemikálie č. 68 zoberte 18g a zmiešajte, potom ešte pridajte 5g z chemikálie č. 154 a máte hotovú zlúčeninu XYZ. Avšak ja chcem, aby ste vedeli, čo je chemikália č. 23 a čo sa stane, keď ju zmiešame s chemikáliou č. 68 a prečo musíme ešte pridať chemikálie č. 154, aby sme dostali výslednú zlúčeninu XYZ. Nechcem, aby ste sa nadrvili recept ako namiešať zlúčeninu XYZ, ale chcem, aby ste vedeli ako spolu reagujú jednotlivé chemikálie a potom nebude problém namiešať akúkoľvek zlúčeninu."
A to isté, myslím, platí aj pre programovanie. Aspoň ja sa toho držím. Najprv chcem pochopiť základ a keď ten pochopím, tak potom si môžem život spríjemňovať frameworkami, ale kým nebudem vedieť, čo ten framework robí, tak so mňa programátor nebude, bude so mňa len lepič kódu. (príspevok berte s rezervou, nechcem tým nikoho uraziť, len konštatujem môj prístup k problematike)
Mám jiné přirovnání: Pokud budeš skládat třeba počítač, koupíš si jednotlivé komponenty (procesor, základní deska, ...), nebo ho budeš stavět z atomů?
Samozřejmě, může být přínosné porozumět pochodům na nižší úrovni, ale proč se tím zabývat, pokud to není potřeba?
Třeba proto, že ne vždy je daný framework v pořádku, ne vždy s ním lze udělat to, co člověk zamýšlí, a nebo lze, ale je potřeba "mírně pozměnit" jeho chování, či dokonce se může stát (a stává se to často), že framework nepoužívá "správně".
V zásadě jsem taky pro využití frameworku, konkrétně jQuery považuji za velmi kvalitní. Baví mě ale věcem porozumět do hloubky, takže popisuji i to, jak si ty kouzla frameworků můžeme vyčarovat sami.
Někdo se navíc bez frameworku chce obejít (třeba já na tomto blogu, protože článek využívající jQuery by byl nezajímavý pro uživatele jiného frameworku) a tomu se nějaká zjednodušující funkce může hodit.
"Baví mě ale věcem porozumět do hloubky, takže popisuji i to, jak si ty kouzla frameworků můžeme vyčarovat sami."
To mi mluvite z duse. Taky chci vedet jak veci funguji ve sve podstate, ne jen rychle dosahnout cile. Bez toho to neni proste zabava. Leo
Jirka:
v jQuery to neni ani na polovinu
$('form').submit(function() {
var form = $(this);
$('#stav').html('odesila se');
$.ajax({type: form.attr('method'), url: form.attr('action'), data: form.serializeArray(), success: function(response) {
$('#stav').html('odeslano');
}});
return false;
});
Mě to tedy s délkou funkce odeslat() přijde zcela srovnatelné. A post_xmlhttprequest() využiji všude stejné, takže zahrnovat ji do porovnání by bylo stejně absurdní jako přičíst k vašemu kódu celou délku jQuery.
Ke kódu ještě jedna poznámka - obvykle je výhodnější posílat data AJAXem na jinou adresu, protože běžné odeslání formuláře musí vrátit celou HTML stránku, čemuž se u AJAXu určitě chceme vyhnout.
Kevujin:
A co posílat na stejnou adresu a rozlišit, zda se jedná o xmlhttrequest a podle toho vracet data?
Nemělo by to vynechávat disabled prvky a odesílat submit, který byl aktivovaný?
mělo, ale už by to bylo nepřehledné ;) btw, zatím jsem nedělal formulář o němž bych nevěděl jaký má prvky a tím pádem nevěděl jak ho odeslat postem jinak, než ho takhle úchylně projít
Problém je, že pak to člověk musí psát vždycky znovu (i když je kód jednodušší, tak je hrozná otrava vyjmenovávat všechny prvky formuláře).
Možná by tam ještě mohlo být zmíněno, že funkce send_xmlhttprequest je tvá vlastní, aby se s tím copy&pasteři netrápili ;)
Chápu, že 'file' vynecháváš kvůli jednoduchosti, ale 'submit' a 'image' by se posílat měl, ne? Stačilo by upravit funkci post_xmlhttprequest tak, že by třetí parametr mohl být i odesílací tlačítko a místo události onsubmit by se obsluhovalo onclick na tlačítku.
Techi:
ne :)
XmlHttpRequest neumí posílat multipart/form-data
Jakub Vrána :
Ono by to třeba i šlo, ale nemáme se jak dostat k obsahu souboru.
v6ak:
To by šlo, ale...
... prostě je to relativně složitý a vracím se zpět k remote scriptingu...
Leoš Ondra:
Dvě poznámky (ne nutně relevantní k danému řešení, ale volně související).
1, Javascript nezahrnuje input type="image" do pole elements, alespoň donedávna tomu tak bylo a to konzistentně ve všech prohlížečích, a ani nemá (neměl) např. přístup k souřadnicím kliknutí, pokud nechcete použít obecný postup pro jakýkoliv element.
2, Pokud je komentářový formulář z nějakého důvodu na samostatné stránce může vadit, že se stránka kde uživatel vyplňoval formulář dostane zbytečně do historie prohlížeče. V tom případě pomůže odeslání AJAXem a následné použití metody window.location.replace().
Leo
Jakub Vrána :
2. Odeslání formuláře AJAXem děláme proto, abychom zůstali na stejné stránce. Pokud bychom se pak chtěli přesunout jinam, tak se AJAXem nemusíme trápit.
Leo:
Pokud se chcete presunout jinam ale stranku, na niz je formular vyradit z historie, pak se Ajaxem trapit musite. Zkuste si to precist jeste jednou, Leo
Juraj:
Inak gratulujem k 400vke!
Luděk:
Mám problém s odesíláním formulářových dat pomocí AJAXu. Konkrétně formularova data typu radio mi to odesila jako "undefined" i kdyz byla radne ve formuláři vybrána. Prosím o pomoc...
Jakub Vrána :
Jaký to je prohlížeč a jak vypadá HTML kód? Mě vše funguje správně.
Dave:
při kliknutí na odeslat se mi objeví jen odesílá se a pak to zmizí, ale nic se nepošle......čím to může být?
Pidusakola:
Vim, ze si ted budete pripadat ze prichazim odnekud z doby kamenné, ale kdyz to odesilam pres tuto funkci, tak jak to zhruba zpracuju v tom PHP potom? O:-) Dějuji všem za reakci. :D
Ofi:
mám ajax který využívá data z formuláře a přestože používám
header("Content-Type: text/html;charset=windows-1250");
tak mi není schopný pobrat české znaky. př. ze slova mašina vyleze "maĺˇina ", co s tím? jak to kódovat aby mi to bralo i háčky a čárky?
Pinqui:
Mohu se prosím zeptat, jak bude vypadat formulář, když budu chtít poslat data z jednoho inputu třeba name="test" do souboru test.php?
Děkuji. Nevím jak to navázat, aby se provedl ajax i s tím Odesílá se.
Nikolas:
Hm, tenhle kod ale nefunguje. Stránka se znovu načítá....
Aaron :):
nebylo by možné uvést celký kód i s formulářem ?? :)
Diskuse je zrušena z důvodu spamu.