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.

Jakub Vrána, Řešení problému, 28.11.2008, diskuse: 38 (nové: 0)

Diskuse

Rival:

Jen bych doplnil, že pracovat s Ajaxem pomocí nějakého frameworku je hezčí a co víc, je to neuvěřitelné zábavné ;)

http://www.prototypejs.org/api/ajax/request

Dundee:

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

marek:

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)

ikona v6ak:

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?

ikona dejf:

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ě".

ikona Jakub Vrána OpenID:

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.

Leoš Ondra:

"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;
});

ikona Jakub Vrána OpenID:

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?

ikona karf:

Nemělo by to vynechávat disabled prvky a odesílat submit, který byl aktivovaný?

ikona Aichi:

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

ikona Jakub Vrána OpenID:

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).

ikona Jakub Vrána OpenID:

Díky za připomínku, kontrolu disabled jsem doplnil, k odesílacímu tlačítku jsem chtěl napsat disclaimer (tuto úlohu už jsem řešil: http://php.vrana.cz/automaticke-odesilani-formularu.php), ale zapomněl jsem na to. Doplnil jsem to tedy alespoň dodatečně.

ikona David Grudl:

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.

ikona v6ak:

File? Jde to vůbec?

ikona Techi:

ne :)
XmlHttpRequest neumí posílat multipart/form-data

ikona Jakub Vrána OpenID:

Ono by to třeba i šlo, ale nemáme se jak dostat k obsahu souboru.

ikona david@grudl.com:

File se řeší třeba přes IFRAME.

ikona 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

ikona Jakub Vrána OpenID:

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

BlackSUN:

"plažin" v jQuery ;) na podobné téma:

http://code.google.com/p/aframework/source/….ajaxSubmit.js

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...

ikona Jakub Vrána OpenID:

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.

avatar © 2005-2024 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.