Pracovat s webovým formulářem na straně JavaScriptu se poměrně snadno může stát očistcem. Nebo jak se nazývá ta věc na čištění záchodové mísy. Přitom za všechno může jedno nešťastné rozhodnutí.
Mějme jednoduchý formulář
<form id=myform>
<input type=text name=query>
<input type=submit value="Vyhledat">
</form>
K jeho jednotlivým prvkům přistoupíme přes vlastnost
elements
:
var form = document.getElementById('myform');
var query = form.elements.query.value;
// pochopitelně také form.elements['query'].value
A iterovat nad prvky lze cyklem for:
for (var i = 0; i < form.elements.length; i++) {
alert(form.elements[i].value);
}
Z historických důvodů lze vlastnost elements
v příkladech
vynechat, neboť jednotlivé prvky se mapují přímo do objektu
form
. Takže by fungovalo i form.query.value
,
form.length
nebo form[i].value
. Což se záhy ukáže
jako nemilé. Formulářové prvky totiž přepisují nativní
metody a proměnné objektu form. Například metoda submit()
,
která je klíčová pro AJAXové odeslání formuláře, se stane nedostupnou,
pokud formulář obsahuje prvek nazvaný submit
. A uznejte, že to
je zrovinka název pro odesílací tlačítko jako dělaný. Pokud by tedy
formulář vypadal takto
<form id=myform>
<input type=text name=query>
<input type=submit name=submit value="Vyhledat">
</form>
nebude jej možné příkazem form.submit()
odeslat, místo toho
JavaScript zařve „form.submit is not a function“ a má pravdu,
form.submit
není funkce ale objekt HtmlInputElement. Dobrá, dáme
si pozor a nebudeme formulářové prvky nazývat submit
.
(prozradím trik, jak by formulář šlo odeslat i v tomto případě:
document.createElement('form').submit.call(form)
)
Jenže název submit
není tím jediným, kterému se musíme
vyhnout. Prvek pojmenovaný elements
způsobí, že
form.elements
nebude očekávaná kolekce a výše uvedené
příklady skončí chybou. Název length
zase znemožní nad
kolekcí iterovat. A tak by se dalo pokračovat, nativních prvků třeba DOM
Firefoxu definuje hodně přes stovku. Pro zajímavost uvádím seznam jen těch
jednoslovných (tj. vynechávám nodeName
nebo
innerHTML
):
action, attributes, blur, children, dir, draggable, elements, encoding, enctype, focus, id, lang, length, method, name, normalize, prefix, reset, spellcheck, style, submit, target, title
Bylo by skvělé, kdyby se HTML5 dokázalo s tímto nešvarem vypořádat. Zatím jsem ve specifikaci nic takového nenašel.
Podobně mi chybí možnost, jak v obsluze události onsubmit
zjistit, kterým tlačítkem byl formulář odeslán. Triviální a užitečná
věc a jak složitě se musí řešit.
<form id=myform onsubmit="kterým tlačítkem byl odeslán?">
<input type=hidden name=id value="123">
<input type=submit name=save value="Uložit">
<input type=submit name=delete value="Smazat">
</form>
Řeším to tak, že odchytávám událost click
jednotlivých
tlačítek a název prvku ukládám do vlastní proměnné formuláře. O něco
jednodušší je využít bublání a odchytávat click
přímo na
formuláři. Nicméně v HTML5 tohle už nebude fungovat spolehlivě, protože
prvek může být umístěn i mimo strom formuláře a přiřazen k němu
atributem form
. Bylo by tedy fajn, kdyby HTML5 zavedlo vlastnost
například form.submitter
, který by vracela název
odesílajícího tlačítka.
p. s. Nette Framework s těmito situacemi počítá a snaží se je v rámci možností řešit za programátora
Komentáře
Daniel Steigerwald #1
Zjistit tlačítko lze. Ne však pomocí čistého Javascriptu, protože form eventy: submit, focus, blur a reset nebublají. Kdyby jo, použil bys jednoduše event.target.
Chytřejší javascriptové knihovny (a jQuery) tohle řeší. Pro focus/blur použijí eventy náhradní nebo capture fázy. Tohle však u submit ani reset použít nelze. Interně to třeba jQuery řeší tak, že po registraci $(‚form‘).live(‚submit‘, fn') skutečně odchytává click, pak no následně volá na všech parentech, a tím simuluje bublání.
Mají tam však bug, takže i když submit buble, a currentTarget funguje, tak target se korektně nastaví pouze v IE. Chtěl sem jim napsat fix, ale musel sem se spokojit s nahlášením bugu, protože sem po ruce neměl potřebný „očistec“.
Shrnuto, nativně to HTML5 řešit nemůže, protože to je zpětně kompatibilní pragmatická „specifikace“ (na rozdíl od idealistické XHTML2).
Řeší to však knihovny, a jQuery ten bug dříve či později opraví též.
Daniel Steigerwald #2
Co se týká prvku elements a mapování names na form, mám pocit že jde o prehistorický pozůstatek DOM0.
Koukám teď, že nejenom YUI3 ale i jQuery zcela zbytečně tuto prehistorickou kolekci stále používají. jQuery v metodě http://api.jquery.com/serializeArray/
Mootools například elements nikdy nepoužívali. Serializovat přeci můžeš libovolný element, obsahující formulářové prvky. Mimochodem, výstup té jQuery metody je dost nepraktický, raději z formuláře vyrobím objekt https://web.archive.org/….org/1088700, a ten pak pošlu na server jako JSON, kde se mi nabinduje na existující třídy. Plochá querystring struktura je na prd, pokud chceš na server odeslat celé objektové grafy.
V Nette bych pak místo .elements použil .getElementsByTagName(‚*‘). Šlape to všude a žádného přepisování se není třeba obávat.
Co se týká mapování na form. O tom vím, ale nikdy sem to neřešil, protože sem nikdy nepoužíval syntetický submit (nepíšu weby, ale aplikace). Triku pomocí call bych se bál, ale pokud to funguje všude, nech to tak. (jen je třeba to testovat)
Daniel Steigerwald #3
Jestli chceš vidět dobrou implementaci (třeba pro inspiraci pro Nette), mrkni se na https://github.com/…ini/nwevents
vojta #4
Je to drobnost, ale všechny atributy HTML prvků by měly být v uvozovkách, kodéra to dráždí! 🙂
Jan Tichý #5
#4 vojto, Neměly. (Ajajaj, to bude zase flame.)
Patrik Votoček #6
#4 vojto, Neměly. Ale můžou.
v6ak #7
Ono by to asi šlo řešit bez úderu do zpětné kompatibility. Ale, na druhou stranu, dost hnusně. Mám pocit, že je možné nějakému objektů přiřadit ekvivalent __call. (Na phpfashion mi asi budete rozumět.)
Víte, kam mířím? Mohly by tu být metody jako getAction(), getLength() apod. Pokud by někdo čistě náhodou použil takto blbý název, dostal by onen element ono „__call“. Ale, na druhou stranu, pokud by takový element měl svoje id (například), šlo by třeba odesílat formulář pomocí document.getElementById(„foo“)(), měl-li bych
<button type="submit" name="submit" id="foo">
Odeslat</button>
. Takže, je to trošku vyhánění satana ďáblem.David Grudl #8
#4 vojto, do uvozovek dávají atributy jen méněcenní kodéři a tlusté ženy!
David Grudl #9
#1 Danieli Steigerwalde, Dane, stačí právě probublávání click a tím pádem to řešit lze.
vojta #10
#8 Davide Grudle, „Always Quote Attribute Values
Attribute values should always be enclosed in quotes“
https://www.w3schools.com:443/…tributes.asp
Jestli je tady kroužek opozice vůči standardům, mělo by to být uvedeno v hlavičce webu 😉
David Grudl #11
#10 vojto, w3schools.com není standard, ale web soukromé společnosti. Standard najdeš tady nebo tady.
vojta #12
#11 Davide Grudle, OK. Díky za odkazy. Na prvním místě se používání uvozovek výslovně vyžaduje, na druhém se připouští i zápis bez nich. Nevím tedy, jak někteří přišli na to že by se uvozovky psát „neměly“.
Opravdu mi připadá čistší zápis
<a href="#" title="Klikni na mě" name="odkaz123" id="odkaz123" />
než
<a href="#" title="Klikni na mě" name=odkaz123 id=odkaz123 />
a vůbec nerozumím, proč nekonzistentní 2. verzi zápisu někdo považuje za „méněcennou“. Osobně v ní vidím samé nevýhody a divím se, že se nikomu neekluje míchání názvu proměnné s její hodnotou. Zdar.
vojta #13
#12 vojto, Davide, teď vidím, že tvůj vlastní parser automaticky přidává hodnotám atributů uvozovky (v mém původním příspěvku u 2. odkazu nejsou uvozovky u atributů name a id).
Tak tohle je dokonalý vlastňák 😁 😁
David Grudl #14
#13 vojto, upravil jsem tvůj komentář, aby tam uvozovky nebyly. Obě HTML specifikace připouštějí možnost za určitých okolností uvozovky vynechat, čti pozorněji. A také tu nikdo neříkal, že by se psát neměly. Tohle téma prosím uzavřeme.
v6ak #15
Taky jsem pro, sice tu už asi nebude živá diskuze, ale myslím, že tato diskuze jen odvádí pozornost od tématu.
Tento článek byl uzavřen. Už není možné k němu přidávat komentáře.