Űrlap mező helykitöltő jQuery kiegészítő
A weben, és ezáltal munkám során is gyakran találkozok olyan űrlapokkal, ahol valamelyik mezőben utalás szerepel arra, hogy mit várnak, mit írjunk be a mezőbe. Ekkor általában nem jelenítik meg a mezőhöz tartozó címkét sem. Ilyen űrlapok szoktak lenni a keresők, amikor a keresési mezőben csak annyi szerepel: Keresés…. Hasonló szokott lenni a belépési űrlap is, ahol az egyik mezőben az szerepel például hogy Felhasználói név míg a másikban pedig csillagok, így egyértelművé válik, hogy mi is a mezők, illetve az űrlap feladata.
Ekkor, ha belekattintunk a mezőbe ez a kitöltés eltűnik, esetleg a bevitt szöveg színe is kontrasztosabbá válik, ezáltal jobban elkülönül a még ki nem töltött mezőktől. HTML5 esetén erre már létezik mező tulajdonság placeholder
néven, ugyanakkor még nem minden böngésző támogatja a HTML5-öt, illetve a placeholder
attribútumot. Ennek ürügyén írtam egy nagyszerű kis jQuery kiegészítőt, ami ezt valósítja meg, sőt, ennél még valamivel többet is.
(function($, window, undefined){
$.fn.extend({
inputPlaceholder : function (settings) {
// Csak TEXT és PASSWORD típusú INPUT mezők kellenek.
var inputs = this.filter('input:text,input:password'),
iph = '.inputPlaceholder',
focus = 'focus' + iph,
blur = 'blur' + iph,
clearValues;
if (settings === 'remove') {
inputs.unbind(iph);
return this;
}
// A beállításokat kiegészítjük az alapértelmezettekkel.
settings = $.extend({
'blurClass' : '',
'focusClass' : '',
'clearOnSubmit': true,
'useAttribute' : false
}, settings);
if (inputs.length) {
inputs.each(function () {
var field = $(this),
placeholder = settings.useAttribute ?
field.attr(settings.useAttribute) :
field.val();
field.bind(focus, function(event) {
// Eltároljuk az aktuális értéket későbbi felhasználásra.
var fieldValue = field.val();
// A mező értéke megegyezik az alapértelmezett értékkel, akkor ürítsük ki.
if (placeholder === fieldValue) {
field.val('').removeClass(settings.blurClass).addClass(settings.focusClass);
}
}).bind(blur, function (event) {
// Eltároljuk az aktuális értéket későbbi felhasználásra.
var fieldValue = field.val();
// A mező értéke üres, rakjuk bele az alapértelmezettet.
if (!fieldValue) {
field.val(placeholder).removeClass(settings.focusClass).addClass(settings.blurClass);
}
}).triggerHandler(blur);
});
// Üríteni kell a mezőt a form elküldésekor?
if (settings.clearOnSubmit) {
clearValues = function () {
inputs.each(function () {
// Minden elemen végig kell mennünk `each`-csel
// mivel a `triggerHandler` csak az első elemen hajtja végre az eseményt.
$(this).triggerHandler(focus);
});
};
inputs.closest('form').bind('submit' + iph, clearValues);
$(window).bind('unload' + iph, clearValues);
}
}
return this;
}
});
}(jQuery, this));
Elemezzük kicsit a kódot, kívülről indulva, hátha okoz valakinek valamilyen meglepetést a jelölésmód, illetve a használt technikák.
Önkioldó névtelen függvény
(function($, window, undefined){
//…
}(jQuery, this));
Ez egy úgynevezett névtelen (anonymus) önkioldó (self executing) függvény. Ez már önmagában is több trükköt rejt. Miért is használna valaki ilyen függvényt? Erre több oka is lehet. A legfontosabb, hogy elrejti a függvényben levő változókat a globális névtérből, azaz nem szennyezi globális névteret feleslegesen a változóival, illetve függvényeivel. Másik indok, hogy mivel a lokális változók elérése gyorsabb, mint visszakeresni a globális változókat, így valamennyit tudunk gyorsítani a kódunkon. Harmadrészt a globális névtérben előforduló változókra más, illetve rövidebb néven tudunk hivatkozni, valamint a kód tömörítő algoritmusok is sokkal hatékonyabban tudnak működni így, mivel a lokális változókat szabadon át tudják nevezni. És van még itt egy utolsó kis dolog, amit ugyan nem használunk ki a kódunkban, de egyeseknek hasznos lehet, ez pedig az undefined
körül bonyolódik. Ugyanis, amelyik függvény paramétert nem adtunk meg a függvény meghívásakor az undefined
-nak inicializálódik. Viszont, ha a függvényen kívüli undefined
-ra hivatkozunk, az nem feltétlen lesz hamiskás (falsy) értékű, ugyanis véletlenül megadható a következő:
undefined = true;
var x = true;
if (x != undefined) {
// ide nem fogunk bejutni, ugyanis mindkettő értéke true.
}
Ezt mindenképpen el akarjuk kerülni, bár a mi kódunkban nem végzünk hasonló ellenőrzést, nem árt, ha fel vagyunk készülve. Az egyszerűbb megértés kedvéért sajnos az undefined
szerepelhet mind változónévként, mind pedig értékként, így ezt akarjuk elfedni azzal, hogy létrehozunk egy lokális változót, melynek mind a neve, mind az értéke undefined
. Ez egy tervezési hiba a nyelvben, de valamit kezdeni kell vele.
Inicializálás
$.fn.extend({
inputPlaceholder : function (settings) {
//…
}
});
Ez egy eléggé standard jQuery kiegészítő írási forma, a lényege, hogy a jQuery prototípusát kiegészítjük egy új függvénnyel, aminek a neve inputPlaceholder
. Ezt a $(selector).inputPlaceholder()
módon érhetjük majd később el. Mint látható átadhatunk neki egy settings
paramétert is, amit a későbbiekben majd még használunk. Amit fontos tudni a jQuery kiegészítő írásról, hogy a kódban a this
alapjában az éppen kiválasztott elemekre, azaz a jQuery objektumra vonatkozik. Ezért lehet rajta például egyből filtert, vagy más hasonló függvényt alkalmazni.
// Csak TEXT és PASSWORD típusú INPUT mezők kellenek.
var inputs = this.filter('input:text,input:password'),
iph = '.inputPlaceholder',
focus = 'focus' + iph,
blur = 'blur' + iph,
clearValues;
Mivel a helykitöltő szövegnek igazán szerepe csak text
és password
mezőkre van (ameddig el nem terjednek a speciális HTML5-ös új mezők), ezért egyből szűrjük is a kiválasztást ezekre. Ezután létrehozunk pár változót, amik igazából a tömörítést segítik elő, és csökkentik az azonos szövegek előfordulását a kódban.
Itt következik egy érdekes lépés:
if (settings === 'remove') {
inputs.unbind(iph);
return this;
}
Hasonlóan a jQuery UI megvalósításhoz, lehetővé tesszük hogy a plugin által az oldalhoz adott eseménykezelőket egy lépésben el tudjuk távolítani, ezzel szinte visszaállítva az eredeti állapotot. Ez hasznos tud lenni, ha valamilyen okból a működés már nem kedvező számunkra. Ennek egyszerű a végrehajtása, egyszerűen a következőt kell meghívni:
$(selector).inputPlaceholder('remove');
Itt használunk egy igazán hasznos kis trükköt, hogy az összes eseménykezelőnket az .inputPlaceholder
névtérbe helyezzük, így egyetlen lépéssel el tudjuk őket távolítani az elemekről. Persze ha nem voltak ilyen eseménykezelők hozzárendelve a kiválasztott elemekhez, akkor sincs semmi probléma ugyanis nem történik igazából semmi. Ami fontos még ebből a kódrészletből, az a return this;
, ugyanis ez teszi lehetővé hogy jQuery-s szokásnak megfelelően láncba tudjuk kötni a végrehajtandó utasításokat (chaining).
Ezt követi a settings
objektum kiegészítése az alapértelmezett értékekkel. Ez azért fontos, hogy a későbbi műveletek során a settings
minden szükséges tulajdonsága meglegyen, ezzel elkerülve a további komolyabb teszteléseket a tulajdonságok felé, illetve használható alapértelmezéseket adunk azokra, amit nem adtunk meg paraméterben. Azaz, amennyiben megelégszünk az alapértelmezett értékekkel, úgy azokat nem kell átadni a paraméterben, csak azokat, amikben el szeretnénk térni.
Események hozzákapcsolása
Mint említettem az eseménykezelőket egy névtérbe helyezzük, így kényelmesebb velük dolgozni. Az eseménykezelőket egy closure-ban rakjuk az elemekhez, ez azért fontos, mert így egyszerűen el lehet tárolni, hogy mi a mező helykitöltő értéke, nem kell azt mondjuk data
tulajdonságban tárolni. Az alapértelmezett értéknél lehetővé tesszük, hogy az DOM elem egy tulajdonságát használjuk, vagy a jelenlegi értékét. Például ha a placeholder
vagy a title
attribútum értékét szeretnénk ha mint helykitöltő szerepeljen, elég megadni a következőt:
$(selector).inputPlaceholder({useAttribute:'placeholder'});
vagy
$(selector).inputPlaceholder({useAttribute:'title'});
Az eseménykezelőket a bind
paranccsal kapcsoljuk az elemhez, ezt azért tesszük, mert csak így lehet névtérben hozzárendelni, és megspórolunk a JavaScript motornak egy függvényhívást, ugyanis ha a focus
, blur
metódusokat használnánk, az ugyanúgy meghívja a bind
utasítást a megfelelő paraméterekkel.
field.bind(focus, function(event) {
// …
}).bind(blur, function (event) {
// …
}).triggerHandler(blur);
A blur
eseményt egyből végre is hajtjuk, de egy kisebb trükkel. A triggerHandler
annyiban különbözik a trigger
-től - amit a legtöbbször használunk -, hogy a böngésző alapértelmezett eseménye nem fut le, csak az kódból hozzákapcsoltak futnak, valamint csak az első kiválasztott elemre fut le, nem az összesre. Ezt még annyival kiegészítettük, hogy csak az általunk megadott névtérbe tartozó eseménykezelők fussanak le ugyanis:
blur == 'blur.inputPlaceholder'
Maguk az eseménykezelők igazából nem csinálnak sokat, csak hozzáadják illetve leveszik a blurClass
és focusClass
osztályokat az elemről, valamint az értékét ürítik vagy feltöltik az helykitöltővel, attól függően, hogy focus
vagy blur
esemény következett be, és hogy mi a DOM elem aktuális értéke.
field.bind(focus, function(event) {
// Eltároljuk az aktuális értéket későbbi felhasználásra.
var fieldValue = field.val();
// A mező értéke megegyezik az alapértelmezett értékkel, akkor ürítsük ki.
if (placeholder === fieldValue) {
field.val('').removeClass(settings.blurClass).addClass(settings.focusClass);
}
}).bind(blur, function (event) {
// Eltároljuk az aktuális értéket későbbi felhasználásra.
var fieldValue = field.val();
// A mező értéke üres, rakjuk bele az alapértelmezettet.
if (!fieldValue) {
field.val(placeholder).removeClass(settings.focusClass).addClass(settings.blurClass);
}
}).triggerHandler(blur);
Az osztály hozzáadásához, és elvételéhez a removeClass
és addClass
parancsokat használja, ami akkor is működik, ha a blurClass
illetve focusClass
értéke üres, nem szükséges még egy feltételt csinálni a kódban.
Mező ürítése elküldéskor
A végére maradt a DOM elem tisztítása. Ennek célja, hogy ne küldje el a helykitöltő értékét, amennyiben a felhasználó anélkül küldené el az űrlapot, hogy a helykitöltővel ellátott mezőt kitöltené. A bejegyzés elején említett űrlapok esetén is ez a kívánt eljárás. Amennyiben mégis azt szeretnénk, hogy a helykitöltő elköldésre kerüljön, ahhoz clearOnSubmit
beállítást kell hamisra állítani.
clearValues = function () {
inputs.each(function () {
// Minden elemen végig kell mennünk `each`-csel
// mivel a `triggerHandler` csak az első elemen hajtja végre az eseményt.
$(this).triggerHandler(focus);
});
};
inputs.closest('form').bind('submit' + iph, clearValues);
$(window).bind('unload' + iph, clearValues);
Itt is használunk pár trükköt. Mivel a triggerHandler csak a kiválasztás első elemére fut le, ezért egy each
ciklusban hajtjuk végre a törlést, de azt se akárhogyan, egyszerűen meghívjuk a focus
eseményt, ami mindezt megteszi a számunkra mindenféle ellenőrzéssel egyetemben. A mezőt tartalmazó form elemhez hozzákapcsolunk egy submit
eseménykezelőt (természetesen ezt is megfelelően névterezve), ami az űrlap elküldésekor fogja a mezőket üríteni. Valamint, hogy az automata mezőkitöltő böngészők se akarják a helykitöltővel kitölteni a mezőt, az ablak unload
eseménye esetén is lefuttatjuk a mezők tisztítását.
Lezárás
Ami a végére maradt, az a jQuery objektum visszaadása, így téve lehetővé, hogy az utasításokat tovább láncoljuk, ahogy láttuk az eltávolítás esetén is. Remélem sikerült kellően részletesen bemutatni a kis kiegészítőt, ami ugyan nem túl hosszú, de kellő mennyiségű jQuery és JavaScript trükköt használ fel, valamint bemutatja, hogyan is érdemes kinéznie egy jQuery pluginnek.
comments powered by Disqus