Archive for the ‘HTML’ Category

Rendszeresen megkeresnek azzal a kéréssel, hogy hogyan kell egyedi Drupal sminket készíteni, mert nem akrarnyak egy már elkészített contrib fejlesztést használni.
Én is általában azt szoktam javasolni, hogy inkább készítsük el saját magunk a meglévő psd alapján a sminkünket, mert igaz az esetek legtöbbségében meg lehetne oldani valamely már meglévő fejlesztés css-ének módosításával, de az mégsem az igazi. Egyrészt azért, mert a contrib sminkek több dologra vannak felkészítve mint amire nekünk szükségünk van, másrészt a végeredményben úgyis egy 3000 soros css-t kapunk amiben egy rakás osztály, vagy id több helyen van definiálva. Másrészt ahogy a contrib modulokba se szokás beleirkálni, szerintem a sminkeket se kell piszkálni.

Ha saját sminket készítünk, saját tpl és css fájlokkal akkor ezek a problémák kiküszöbölhetőek, és nem egy ördöngös feladat.

Leszögezném, hogy ez a bejegyzés csak az alapokról szól, nagyon sok egyéb dologra felkészíthetjük a sminkünket, pl: $logo, $site_slogan, $mission, $search_box stb. Az elérhető változókat itt lehet megnézni.

Az első lépés az, hogy a psd alapján végig kell gondolnunk, hogy milyen régiókra lesz szükségünk. Persze könnyen lehet, hogy az alapértelmezett régiók bőven elegendőek, de általában nekem újakat kell létrehoznom. Itt egy fontos megjegyzés az, hogy az alapértelmezett régiók elvesznek ha akár csak egy sajátot is létrehozunk, ha az info fájlba definiálunk régiót, akkor az összeset meg kell adnunk!

Miután ez megvan elkezdhetjük a sminkünk elkészítését, vagyis a fizikai fájlok létrehozását.

A saját sminkeket különítsük el a core sminkektől, ezért hozzuk létre a sites/all/themes könyvtárat és ide helyezzük a contrib, illetve a custom sminkeket.

Hozzuk létre a sminkemneve könyvtárat és abba készítsük a sminkemneve.info fájlt. Fontos megjegyzés, hogy az info fájlokat a rendszer cache-eli, így ha abba módosítunk valamit, akkor be kell menni az admin/build/themes könyvtárba és nyomni egy mentést, ekkor újra menti az info fájl tartalmát.

De hogy is néz ki egy info fájl? Erről egy teljesen részletes leírás található itt. Az alapértelmezett értékek pedig itt szerepelnek (itt láthatjuk pl a default régiókat).

Íme egy info fájl:

  1. name = My theme's name
  2. description = My theme's description.
  3. version = VERSION
  4. core = 6.x
  5. engine = phptemplate
  6. stylesheets[all][] = style.css
  7. stylesheets[all][] = layout.css
  8. scripts[] = scripts.js
  9. regions[header] = Header
  10. regions[left_slidebar] = Left sidebar
  11. regions[left_banner] = Left banner
  12. regions[content_top] = Content top
  13. regions[in_top_of_content] = In top of content
  14. regions[content] = Content
  15. regions[right_slidebar] = Right sidebar
  16. regions[right_banner] = Right Banner
  17. regions[footer_top] = Footer top
  18. regions[footer_bottom] = Footer bottom

Talán csak annyit fűznék még hozzá, hogy a regions tömb kulcsa lesz a page.tpl.php-ban elérhető változó ami az adott régió tartalmát adja vissza html string formában.

Ezután jöhet a page.tpl.php.
Ebben állítjuk össze magát a layout-ot, tehát létrehozzuk a szükséges div-eket a psd implementálásához.

Én így szoktam kezdeni:

  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  2.   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  3. <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php print $language->language ?>" lang="<?php print $language->language ?>" dir="<?php print $language->dir ?>">
  4.   <head>
  5.     <?php print $head ?>
  6.     <title><?php print $head_title ?></title>
  7.     <?php print $styles ?>
  8.     <?php print $scripts ?>
  9.   </head>
  10.   <body class="<?php print $body_classes ?>">

Szerintem elég egyértelmű hogy mit is csinál, csak annyit fűznék hozzá, hogy a $body_classes változó egy csomó információt tartalmaz, pl hogy nyitólapon vagyunk-e vagy, be vagyunk-e jelentkezve vagy hogy mely régiókra lesz szükség. Ez szerintem hasznos lehet ha azt akarjuk hogy a sminkünk máshogy nézzen ki pl ha csak a jobb oldali slidebar létezik, vagy ha mind a kettő, esetleg egyik sem stb.

Ezután kezdjük el kialakítani a szükséges div-eket, ez nyilván a psd-től függ.

Jöhetnek a régiók! Ha nem akarjuk teleszemetelni üres div-ekkel a template-et akkor feltételek közé rakjuk hogy egy adott régiót körülvevő div megjelenjen, pl így:

  1. <?php if ($left_slidebar) : ?>
  2.   <div id="left_slidebar"><?php print $left_slidebar ?></div>
  3. <?php endif; ?>

Miután a div-ekkel megvagyunk, készítsük el a tabokat (view, edit stb), a rendszerüzeneteket (pl hibás felhasználónév) írassuk ki magát a content-et - esetleg az oldal címét is beletehetjük a page.tpl.php-ba:

  1. <?php print $title ?>
  2. <?php if ($tabs): print '<div id="tabs-wrapper" class="clear-block">'; endif; ?>
  3. <?php if ($tabs): print '<ul class="tabs primary">'. $tabs .'</ul></div>'; endif; ?>
  4. <?php if ($messages): print $messages; endif; ?>
  5. <?php print $content ?>

Végül egy fontos feladatunk van a page.tpl.php-vel, az pedig a $closure kiíratása:

  1. <?php echo $closure; ?>

Ez azért fontos, mert sok modul ebbe pakol dolgokat, pl a WYSIWYG modul esetében azt érzékeljük, hogy egyszerűen nem jelenik meg az editor ha ez kimaradt a sminkfájlból.

Amint írtam van még egy csomó dolog amiket beletehetünk a sminkünkbe (és ha a közössésgnek készítjük a sminket akkor bele is kell rakni), ezen változók listáját itt lehet megnézni.

A page.tpl.php-n kívül még létre kell hozunk a block.tpl.php-t, a comment.tpl.php-t és a node.tpl.php-t. Ha az alap sminkfájlokat használjuk, akkor tökéletesen működik a sminkünk.
Persze ezeket is egyedileg testre kell szabi, ezeket is felosztjuk a megfelelő div-ekkel hogy olyan kinézetet kapjunk mint a psd-ben.

Van még egy fájl ami általában kell, ez pedig a template.php. Fontos, hogy az ebben definiált fügvények nevét cache-eli a rendszer, tehát ha egy új fügvényt deklarálunk akkor be kell menni az admin/build/themes oldalra és menteni kell.

Nekem már többször előfordult olyan problémám, hogy a Drupal elsődleges menüjének (primary links) egyedi kinézete miatt, kizárólag képekből álló menüt kellett készítenem. Ezalatt azt értem, hogy nincs a háttérkép felett semmilyen szöveg, a menü címe is a képen szerepel.

A háttérképeket egyszerűen megadhatjuk, a menüpontok egyedi azonosítója miatt (pl. menu-130 stb.), viszont a rajta lévő szöveget el kell tüntetni valahogy.

CSS-ben ha levesszük a betűméretet 0px-re sajnos nem a megfelelő megoldás, mert IE-ben, Chrome-ban és Operában kicsiben, de látszik.

Egy megoldás, ha a hyperlinkek szövegét span elemek közé tesszük, és a span elemeket tüntetjük el CSS-ben a display: none; definícióval, tehát a cél az lenne, hogy így nézzen ki egy menüelem:

  1. <li class="menu-129"><a class="menu-129" href="/"><span class="primary-title">Nyitólap</span></a></li>

Ekkor minden további nélkül megadhatjuk CSS-ben a következőt:

  1. ul.primary-links span.primary-title {
  2. display: none;
  3. }

Igenám, de hogy érjük el, hogy a Drupal span elemek közé tegye a linkek címét?

A megoldás az, hogy a sminkünk template.php fájljába kifejtjük a theme_links() fgv-t.

Íme a kód:

  1. /**
  2.  * Csak kepekbol allo menu elkeszitesenek alapjai
  3.  *
  4.  * @param unknown_type $links
  5.  * @param unknown_type $attributes
  6.  * @return HTML
  7.  */
  8. function phptemplate_links($links, $attributes = array('class' => 'links')) {
  9.   global $language;
  10.   $output = '';
  11.  
  12.   if (count($links) > 0) {
  13.     $output = '<ul'. drupal_attributes($attributes) .'>';
  14.  
  15.     $num_links = count($links);
  16.     $i = 1;
  17.  
  18.     foreach ($links as $key => $link) {
  19.       $class = $key;
  20.  
  21.       // Add first, last and active classes to the list of links to help out themers.
  22.       if ($i == 1) {
  23.         $class .= ' first';
  24.       }
  25.       if ($i == $num_links) {
  26.         $class .= ' last';
  27.       }
  28.       if (isset($link['href']) && ($link['href'] == $_GET['q'] || ($link['href'] == '<front>' && drupal_is_front_page()))
  29.           && (empty($link['language']) || $link['language']->language == $language->language)) {
  30.         $class .= ' active';
  31.       }
  32.       $output .= '<li'. drupal_attributes(array('class' => $class)) .'>';
  33.      
  34.       $link['attributes'] = array('class' => $class);
  35.  
  36.       if (isset($link['href'])) {
  37.         // Pass in $link as $options, they share the same keys.
  38.         $link['html'] = true;
  39.         $output .= l('<span class="primary-title">' . $link['title'] . '</span>', $link['href'], $link);
  40.       }
  41.       else if (!empty($link['title'])) {
  42.         // Some links are actually not links, but we wrap these in <span> for adding title and class attributes
  43.         if (empty($link['html'])) {
  44.           $link['title'] = check_plain($link['title']);
  45.         }
  46.         $span_attributes = '';
  47.         if (isset($link['attributes'])) {
  48.           $span_attributes = drupal_attributes($link['attributes']);
  49.         }
  50.         $output .= '<span'. $span_attributes .'>'. $link['title'] .'</span>';
  51.       }
  52.  
  53.       $i++;
  54.       $output .= "</li>\n";
  55.     }
  56.  
  57.     $output .= '</ul>';
  58.   }
  59.  
  60.   return $output;
  61.  
  62. }

Ez a kód, majdnem megegyezik az API oldalon található fgv. kóddal. Nézzük az eltéréseket:

  1. $link['attributes'] = array('class' => $class);

Ezzel annyit érünk el, hogy ne csak a listaelemnek (li) legyen egyedi azonosítója, hanem a hyperlink-nek (a) is. Ez azért fontos, hogy a :hover eseményre is tudjunk képet rakni, ha esetleg különbözik.

  1. $link['html'] = true;
  2. $output .= l('<span class="primary-title">' . $link['title'] . '</span>', $link['href'], $link);

A link tömb lesz az l() fgv. harmadik paramétere, ebbe kell szerepelnie a beállításoknak, és mivel HTML-t adunk meg, ezért a $link tömb html kulcsát igaz értékre kell állítanunk. Az l() fgv. első paraméterének pedig beírjuk a span elemek közé a link címét.

Ha ezt elvégeztük, feltöltjük a template.php-t a smink könyvtárába, majd hogy a rendszer érzékelje az új fgv. létrejöttét, menjünk be a sminkek beállítási oldalába (admin/build/themes) és nyomjunk egy mentést.

Ezután minden menüelem egyedi azonosítoval fog rendelkezni, és a menü címe span elemek között lesz, amit könnyedén eltüntetetünk. Innenstől kezdve csak CSS kérédése az egész.

Észrevehetjük, hogy az Internet Explorer régebbi verziói nem tudják támogatni a PNG fájlokat. A jelenség az, hogy nem kezeli az átlátszóságukat, és ez elég nagy probléma mivel (sajnos) a mai napig is a felhasználók legnagyobb része IE6-ot használ.

Szerencsére erre is van megoldás, nem is bonyolult, nem is sok idő megoldani.

Mindösszesen három fájlra lesz szükségünk:

Az első css fájlt a Conditional Comment feltételes megoldással kell beilleszteni a weboldalunkba (Drupal CMS esetén a page.tpl.php fájlba):

  1. <!–[if lt IE 7]>
  2.   <link rel="stylesheet" href="http://www.kalman-hosszu.com/ie-fix/ie.css" type="text/css" media="screen" />
  3. <![endif]–>

Ezzel elérjük azt, hogy az IE7 előtt böngészőknél hozzáadja az oldalhoz az ie.css fájlt is.

Ha megnézzük ennek a css fájlnak a tartalmát akkor láthatjuk, hogy hova kell elhelyezni a többi fájlt:

  1. img {
  2.   behavior: url(/images/png.htc);
  3. }

Tehát az itt megadott könyvtárba (esetemben /images) elhelyezzük a png.htc és a transparent.gif fájlokat, és IE alatt is használhatunk PNG képeket.

Ügyfelünk kérése alapján el kellett készítenünk egy DropDown menüt a nyitólapjára. Most is - mint általában - a jQuery JavaScript könyvtárban kerestem a megoldást. Rövid keresgélés után megtaláltam a Superfish jQuery plugin-t, mely tökletesen megfelelőnek tűnt a célra…legalábbis első ránézésre.

A probléma a tesztelés során jelentkezett, mégpedig a szokásos IE6/IE7/FF kompatibilitási ellentétek keretében.
Mondanom sem kell, hogy a plugin FF alatt teljesen jól teljesített (ugyanúgy mint Safari-n vagy Opera-n), a Microsoft két böngészőjénél az oldal gyakorlatilag összeomlott.

A menü FF-ban:

DropDown menü FF

Az internet Explorerben elég érdeksen mutatott:

DropDown menü IE
DropDown menü IE
DropDown menü IE

Először úgy gondoltam megkeresem a megoldást, mert biztosan lokális gond van, hiszen az oldalon található bemutatókon nincs gond Internet Explorer alatt sem, de akárhogy csürtem csavartam nem akart működni. Nem csak a kinézettel volt összeakadás, hanem a funkcionalitás sem működött. IE alatt egyszerüen eltünt a DropDown menü amikor elhagytam az egérrel a legfelső meüelemet.

Néhány órányi küszködés után úgy felidegesítettem magam, hogy eldöntöttem inkább írok egy sajátot, hiszen nem egy atomfizika a DropDown menü, főleg a jQuery-t használva.

Nézzük meg, először a HTML osztályokat, az elrendezési szabályokat!

Íme egy főmeü, és a hozzá tartozó DropDown menü:

  1. <div class="menu-sor">
  2.   <div class="ddown-menu">
  3.       <div class="fomenu-ikon">
  4.         <a href="/beteg-vagyok-gyogyulni-akarok"><img src="/themes/nyitolap/images/segiteni_img.jpg" alt="" /></a>
  5.       </div>
  6.       <ul class="submenu">
  7.         <li><?php print l("Pozitív hozzáállás", "pozitiv-hozzaallas"); ?></li>
  8.         <li><?php print l("Gyógyító nevetés", "gyogyito-nevetes"); ?></li>
  9.         <li><?php print l("Függőségek", "fuggosegek"); ?></li>
  10.         <li><?php print l("Mozgásszervi betegségek", "mozgasszervi-betegsegek"); ?></li>
  11.         <li><?php print l("Orvosok, akik agykontrollt végeztek", "orvosok-akik-agykontrollt-vegeztek"); ?></li>
  12.         <li><?php print l("Kiadványok gyógyulni vágyóknak", "kiadvanyok-gyogyulni-vagyoknak"); ?></li>
  13.       </ul>
  14.   </div>
  15.   <div class="fomenu">
  16.     <p><a href="/beteg-vagyok-gyogyulni-akarok">Beteg vagyok és gyógyulni akarok</a></p>
  17.   </div>
  18.   <div class="clear"></div>
  19. </div>

Az l() fgv. használata ne tévesszen meg senkit sem, ez egy beépített Drupal fgv., síma link generálódik belőle.
Tehát a

  1. <li><?php print l("Pozitív hozzáállás", "pozitiv-hozzaallas"); ?></li>

Sorok nyugodtan lecserélhetőek síma HTML elemekre:

  1. <li><a href="/valamilyen-tartalom">Tartalom címe</a></li>

Nézzük meg a css-t, hogy ki is nézzen valahogy, nameg azért vannak benne fontos részek:

  1. <style type="text/css">
  2. ul.submenu {
  3.   position: absolute;
  4.   display: inline;
  5.   z-index: 99;
  6.   border: 5px solid #962ab2;
  7.   display: none;
  8.   margin-left: 65px;
  9.   margin-top: 0;
  10.   width: 250px;
  11.   margin-bottom: 0;
  12. }
  13. ul.submenu li {
  14.   margin: 0;
  15.   padding: 0;
  16.   width: 250px;
  17. }
  18. ul.submenu li a {
  19.   font-size: 10pt;
  20.   display: block;
  21.   color: #409066;
  22.   background: #fdf3fd;
  23.   padding: 5px 20px;
  24.   border-bottom: 1px solid #962ab2;
  25.  
  26.   margin: 0;
  27.   width: 210px;
  28.   line-height: 14pt;
  29. }
  30. ul.submenu li a:hover {
  31.   background: white;
  32.   text-decoration: none;
  33. }
  34. div.ddown-menu {
  35.   width: 70px;
  36.   float: left;
  37. }
  38. div.ddown-menu div.fomenu-ikon {
  39.   width: 70px;
  40. }
  41. div.fomenu {
  42.   width: 250px;
  43.   float: left;
  44. }
  45. div.fomenu p {
  46.   padding-left: 10px;
  47.   padding-top: 5px;
  48. }
  49. div.menu-sor {
  50.   padding-top: 10px;
  51. }
  52. div.clear {
  53.   clear: both;
  54. }
  55. </style>

Itt volt egy-két érdekesség, amit mindenképpen szeretnék megmelíteni!

Az ul.submenu css definícióban fontos elemek:

  1.   position: absolute;
  2.   display: inline;
  3.   z-index: 99;
  4.   width: 250px;

Az első három nélkül az oldal IE-ben szétcsúszik, a szélesség megadása nélkül pedig IE6-ban csak 70px lesz a lehullómenü szélessége.

Az “ul.submenu” és az “ul.submenu li a” css definíciónál is nagyon fontos a szélesség megadása, mert különben IE6-ban eltűnik a DropDown menü ha az első elemet elhagyjuk (tehát nem tudjuk kivűlasztani mondjuk a második, vagy harmadik elemet a mnüből).
Az a (link) elemnél nyiván a paddingot kivonjuk a szélességből, ezért lesz 40px-el kevesebb.

A harmadik dolog amire figyelni kell az, hogy mindenhol megadtam a margin-bottom értékét, mégpedig 0-nak. Ez is fontos, különben IE6 alatt nagy alsó margót rak minden menüelem alá.

Végül, mint IE6 mint IE7 alatt nem volt megfelelő az “ul.submenu” bal oldali margó értéke (65px), ezért teljesen kiment a DropDown menü a jobb oldalra. Erre a Conditional comments megoldással probálkoztam, vagyis:

  1. <!–[if lte IE 7]>
  2.   margin-left: -5px;
  3. <![endif]–>

De sajnos ez nem reagált semmit, úgyhogy megoldottam ezt is JavaScript-el:

  1. <script type="text/javascript">
  2. $(function() {
  3.   if ($.browser.msie) {
  4.     $('ul.submenu').css("margin-left", "-5px");
  5.   }
  6. });
  7. </script>

Nézzük meg végül a lényeget, a HTML-hez tarotó jQuery kódot, amiben kivitelezzük a DropDown menüt:

  1. <script type="text/javascript">
  2. $(function() {
  3.   var submenu_active = false;
  4.   $(".ddown-menu").hover(
  5.   function() {
  6.     $("ul.submenu", $(this)).show();
  7.     $("ul.submenu", $(this)).hover(
  8.     function() {
  9.       submenu_active = true;
  10.     },
  11.     function() {
  12.       $(this).hide();
  13.       submenu_active = false;
  14.     });
  15.   },
  16.   function() {
  17.     if (!submenu_active) {
  18.       $("ul.submenu", $(this)).hide();
  19.     }
  20.   }
  21.   );
  22. });
  23. </script>

Mit is csinál?

Az oldal betöltődése után létrehoz egy bool tipusú változót, melyben a DropDown menü aktivitását figyeljük, majd végigmegy az összes ddown-menu osztályon, és figyeli a hover esemény.
Amikor bekövetkezik, a hozzá tartozó submenu osztályt előhozza (show), majd ennek a megjelenített menünek figyeli a hover eseményét.
Ha bekövetkezik, akkor a submenu_active változó értéke igaz lesz, hiszen éppen a menüben vagyunk, ha pedig elhagyjuk, akkor elrejtjük, és a submenu_active értékét hamisra állítjuk.
Végül megnézzük, hogy a ddown-menu osztályba tartozó elemek hover eseményének végén a submenu_active igaz-e, és ha nem akkor a DropDown listát eltávolítjuk. Erre azért van szükség, mert olyan szituáció is elképzelhető, hogy valaki nem lép be a submenu-be, csak előhozza. Ha ez a sor kimaradna, akkor ilyen esetekben a sumenu nem tunne el.

Régebben írtam egy cikket a dinamikus form ellenőrzésről, és arra gondoltam leírom az eljárás modernebb változatát, azaz a hibaüzenetek nem alert ablakban jelennek meg, hanem DHTML segítségével.

Természetesen olyan kódot kell készíteni amely akárhány form-ra dinamikusan működik, tehát nem függ az input mezők számától, és az oldalon elhelyezett form-ok mennyiségétől.

A megoldást itt is a jQuery JavaScript könyvtár adja.

De mindenek előtt érdemes átnézni hogyan is kell felépíteni egy profi form-ot. Khauth György kollegám írt már erről néhány nagyon hasznos cikket:

Miután az alapelvek tanulmányozásával végeztünk készítsük el a form-ot:

  1. <style type="text/css">
  2. .form-row {
  3.   padding-bottom: 10px;
  4.   width: 290px;
  5. }
  6.  
  7. .form-row label {
  8.   float: left;
  9.   width: 100px;
  10.   padding-right: 10px;
  11. }
  12.  
  13. .form-row input {
  14.   float: left;
  15.   padding: 3px;
  16.   width: 160px;
  17. }
  18.  
  19. .clear {
  20.   clear: both;
  21. }
  22.  
  23. .form-error {
  24.   text-align: center;
  25.   color: #C52020;
  26.   border: 1px solid #DD7777;
  27.   background: #FFCCCC;
  28.   margin: 10px 0;
  29.   display: none;
  30. }
  31. </style>
  32.  
  33. <div style="margin: 0 auto; width: 300px;">
  34.   <form action="#" method="post">
  35.     <div class="form-row">
  36.       <label for="keresztnev">Keresztnév:</label><input type="text" name="keresztnev" class="kotelezo" id="keresztnev" />
  37.       <div class="clear"></div>
  38.       <div class="form-error hianyos">A keresztnév megadása kötelező!</div>
  39.     </div>
  40.    
  41.     <div class="form-row">
  42.       <label for="vezeteknev">Vezetéknév:</label><input type="text" name="vezeteknev" class="kotelezo" id="vezeteknev" />
  43.       <div class="clear"></div>
  44.       <div class="form-error hianyos">A vezetéknév megadása kötelező!</div>
  45.     </div>
  46.    
  47.     <div class="form-row">
  48.       <label for="email">Email:</label><input type="text" name="email" class="kotelezo email" id="email" />
  49.       <div class="clear"></div>
  50.       <div class="form-error hianyos">Az email cím megadása kötelező!</div>
  51.       <div class="form-error hibas">Nem valós az email cím!</div>
  52.     </div>
  53.    
  54.     <div class="form-row" style="text-align: center;">
  55.       <input type="submit" name="submit" value="Mehet" style="float: none;" />
  56.     </div>
  57.   </form>
  58. </div>

Ha megnézzük a kód a css stílus felépítésével kezdődik, ez gyakorlatilag csak az átlátható kinézetet kezeli. A lényeg természetesen a form, és a form-on belüli osztályok elhelyezkedése!

A form-ot körülöleli egy div, a JavaScript kóddal ezen belül fogunk keresni. Ennek az a lényege, hogy akárhány form-on el tudjuk sütni az eljárást, ne kelljen minden form-nál újra és újra megírni, hozzáigazítani a form-hoz.
Három adatok kérünk be: keresztnév, vezetéknév, és email cím.
Ha megfigyeljük mindegyik alatt megtalálható a hozzá tartozó hibaüzenet is. A hibaüzenetek mindegyike a form-error osztályba van sorolva, így a kinézetük egységesen definiálható, és láthatóságuk egységesen kezelhető.
A hibaüzenetek nem csak a form-error osztályba tartoznak, hanem a hiba típusába is. Ezesetben mind a három input mezőhöz tartozik egy hianyos osztály, ami akkor jelenik meg ha üresen hagytuk, az email címhez pedig egy hibas osztály, ami akkor jelenik meg ha az email cím nem valós.

Nézzük meg a hozzá tartozó JavaScript kódot, melyben dinamikusan elleőrizzük az input mezők kitöltöttségét, és az email cím helyességét, valósságát is:

  1. <script type="text/javascript">
  2.  
  3. $(function() {
  4.   $('form').submit(function() {
  5.     var mehet       = true;
  6.     var first_error = true;
  7.     var error_obj   = null;
  8.     $('div.form-error', $(this).parent()).each(function() {
  9.       $(this).hide();
  10.     });
  11.     $('input.kotelezo', $(this).parent()).each(function() {
  12.       if ($(this).val() == '') {
  13.         mehet = false;
  14.         if (first_error) {
  15.           first_error = false;
  16.           error_obj   = $(this);
  17.         }
  18.         $('div.hianyos', $(this).parent()).show();
  19.       }
  20.     });
  21.  
  22.     var email   = $('input.email', $(this).parent()).val();
  23.     var filter  = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
  24.     if (!filter.test(email)) {
  25.       mehet = false;
  26.       if (first_error) {
  27.         first_error = false;
  28.         error_obj   = $(this);
  29.       }
  30.       $('div.hibas', $(this).parent()).show();
  31.     }
  32.  
  33.     if (mehet) {
  34.       return true;
  35.     } else {
  36.       location = '#' + error_obj.attr('id');
  37.       return false;
  38.     }
  39.   });
  40. });
  41.  
  42. </script>

A dinamikus form ellenőrzésről írt cikkemhez képes van néhány eltérés, most ezekre térnék ki elsősorban.
Az első, hogy nem csak a mehet változót deklaráljuk, hanem a first_error bool típusút, és az error_obj html objektum típusút is. Ezek azt a célt szolgálják, hogy hibás kitöltés esetén le tudjuk tárolni melyik hiba volt az első a sorban, és automtaikusan oda ugorjunk az oldalon.
Utána láthatjuk a

  1. $('div.form-error', $(this).parent()).each(function() {
  2.       $(this).hide();
  3.     });

részt, ami arra való, ha már egyszer hibásan töltöttük ki a form-ot, akkor eltűntetjük az összes hibaüzenetet, különben ottmaradhat második hibás kitöltés esetén olyan üzenet is, ami már nem aktuális.

Ezután ellenőrizzük a kitöltöttséget, tehát végigmegyünk a kötelező input mezőkön. Ha valameny nincs kitöltve akkor a szülö objektumának hianyos osztályát megjelenítjük. Végül ellenőrizzük az email cím helyességét, tehát hogy illeszkedik-e a szabványra, és ha nem a szülő html objektumának hibas osztályát megjelenítjük.

Végül ha nem találtunk hibát tobábbengedjük a submit eseményt, ha találtunk akkor az első hibás mezőhöz ugrunk.

Olyan oldalaknál ahol egy bizonyos node tipushoz sok beviteli mező tartozik elvárás lehet, hogy ne kelljen minden módosításnál lemenni a lap aljára.

A logikus megoldás az, hogy az ENTER gomb lenyomásával jelzi a felhasználó, hogy végzett a tartalom módosításával, feltöltésével.

Az eseményt a jQuery JavaScript könyvtárral könnyen meg lehet oldalni, utána pedig már csak egy nagyon egyszerű Drupal modult kell hozzá írni, és minden olyan weblapon alkalmazni lehet ahol szükség van rá.

Íme a JavaScript fájl amint majd hozzárendelünk az oldalhoz:

  1. var writing   = false;
  2. var node_edit = false;
  3.  
  4. $(document).ready(function() {
  5.  
  6.   if ($('#node-form').length) {
  7.     node_edit = true;
  8.   }
  9.  
  10.   if (node_edit) {
  11.     $('.form-textarea', $('#node-form').parent()).focus(function() {
  12.       writing = true;
  13.     });
  14.     $('.form-textarea', $('#node-form').parent()).blur(function() {
  15.       writing = false;
  16.     });
  17.   }
  18. });
  19.  
  20. $(document).keypress(function(event) {
  21.   if (event.keyCode == 13 && !writing && node_edit) {
  22.  
  23.     if (confirm_submit()) {
  24.       $('#edit-submit', $('#node-form').parent()).click();
  25.     }
  26.   }
  27. });
  28.  
  29. function confirm_submit() {
  30.   var agree = confirm("Valóban menti a tartalom változásait?");
  31.   if (agree) {
  32.     return true ;
  33.   } else {
  34.     return false ;
  35.   }
  36. }

Menjünk végig a kódon, mi miért van!

Először is a writing változó, a node_edit változó és az oldal betöltésekor lefutó fgv-ek, vagyis a $(document).ready(function()).
A writing változó azért fontos, mert az ENTER lenyomása esetén nem feltétlenül a tartalom szerkesztésének befejeztét jelzi a felhasználó, hanem esetlegesen éppen egy olyan szöveget gépel be, amiben van sortörés. Az pedig elég nagy gond lenne, ha nem lehetne több sort bevinni egy node-ba :-)
Ezért aztán létrehozzuk a writing bool tipusú változót alapértelmezetten hamisra állítva, és figyelve az eseményeket változtatjuk, hogy éppen gépelés történik vagy sem.
Az odlal betöltődésekor a form-textarea osztályokhoz hozzárendeljük a focus() és blur() fgv-eket.
Amikor egy textarea-ba bekattint (focus) a felhasználó a writing változó értéke igazra változik, amikor elhagyja (blur) hamisat vesz fesz vel.
A node_edit bool tipusú változót azért hozzuk létre, mert a későbbiekben a hook_form_alter()-t fogjuk használni a js beépítésére, ez azonban nem csak a node-ok szerkesztésénél fut le, hanem például a felhasználók kezelési felületén is. Ezért megvizsgáljuk, hogy a node-form létezik-e, és csak akkor futtatjuk a kódót ha igen.

A billentyű lenyomása, a keypress(function).
Ezt magához az oldalhoz ($(document)) kell hozzárendelni, hiszen nem valamely beviteli mezőt figyeljük.
A fgv paramétere az event változó, ez tartalmazza a billentyűlenyomás tulajdonságait.
A keyCode a lenyomott billentyű ID-ját tartalmazza ami egy egész. Az ENTER gomb kulcsa a 13-as szám.
Emiatt a feltételben megvizsgáljuk, hogy a 13-as kódszámmal rendelkező billentyű lett-e lenyomva (ENTER), és hogy éppen folyik-e a gépelés (writing változó).
Ha a feltételnek megfelelőek az értékek és a felhasználó megerősítette (confirm_submit), hogy befelyezte a szerkesztést akkor végrehajtjuk a node mentését.

Lehetséges, hogy a

  1. $('#edit-submit', $('#node-form').parent()).click();

sor egy kis magyarázatra szorul.

Az node-form a form id attributuma.
Gondolhatnánk a legegyszerűbb meghívni a submit eseményt a submit() fgv-el. A probléma az, hogy a node szerkesztési felületén három submit gomb van: beküldés, előnézet, törlés. Emiatt a submit esemény nem egyértelmű, hiszen mind a három gomb, ugyanannak a form-nak a submit gombjai, és mindegyik mást csinál. Emiatt nem történik semmi.
A következő gondolat az, hogy oldajuk meg a problémát az edit-submit id-jú gomb click esemény meghívásával.
Ezzel a gond az, hogy a Drupal beépített form-jainak mind ez az azonosítója. Így pl ha engedélyezve van a search modul akkor annak a submit gombaja is ugyanezzel az id-val rendelkezik, tehát könnyen előfordulhat, hogy az enter lenyomása mondjuk keresést eredményez, nem pedig a tartalom elmentését.
Ezért magában a form-ban kell megkeresnünk a megfelelő gombot, viszont form-on belül nem lehet html objektumot keresni, ezért a form-ot körülölelő div-ben kell megtennünk.

Az utolső fgv. a megerősítés, vagyis rákérdez a felhasználótól, hogy valóban mentsük-e a tartalmat.

Ez a jQuery megvalósítás működik, mostmár csak be kell építeni a site-ba.

Mivel ennek a JavaScript-nek csak és kizárólag a node szerkeztési felületén kell megjelnnie, így a hook_form_alter()-t a legérdemesebb használni.

A modulunk, tehát az .info fájlon kívül ennyi:

  1. <?php
  2. /**
  3.  * hook_form_alter().
  4.  */
  5. function enter_node_submit_form_alter($form_id, &$form) {
  6.   drupal_add_js(drupal_get_path('module', 'enter_node_submit') .'/enter_node_submit.js');
  7. }
  8. ?>

A modul letölthető innen:
enter_node_submit.module

Sokszor felmerül a kérdés, hogy lehet a form mezőinek kitöltöttségét egyszerűen és újrahasznosíthatóan megírni.

Erre egy nagyon jó megoldást ad a jQuery JavaScript könyvtár.
A lényeg az, hogy egy kód megírásával lehessen ellenőrizni az oldalon található összes form kötelező mezőinek kitöltöttségét, függetlenül attól, hogy mely formnak hány kötelező mezője van.

Ehez fontos, hogy a form-okat a megfelőle stratégia szerint építsük fel…a lényeg a helyes osztályok használata. Ha a megfelelő input mezőket a megfelelő osztályokba soroljuk, akkor a megoldás néhány sor JS-el megoldható, és el lehet sütni akármikor a jövőben is.

Néhány napja az egyik ügyfelünknek kellett elkészítenem egy site részletet, ahol az oldalak összességét tekintve, kb. 20 formot kellett elhelyezni, namost ez sok, és ha mindet a szabványos JS módszerekkel próbáltam volna ellenőrizni (a mezőket ID alapján lekérdezem és ellenőrzöm, hogy ki van-e töltve), akkor sok kódot kellett volna írni, valamint a hibalehetőség is nagy. Ahol nagy a hibalehetőség azt minimum tízszer annyi idő tesztelni, mint egy megfelően elkészített form ellenőrzést.

Vegyünk egy egyszerű példát egy négy mezőböl álló form-ra, melynek három kötelező mezője, amiből az egyik email, tehát az email cím valósságát is ellenőrizni kell.

A form a következő képp néz ki:

  1. <div class="form">
  2.  
  3. Ide jöhet a form bevezető szövege
  4. <form class="feliratkozo-form" action="#" method="post">
  5. <div style="display: block; width: 70%; margin: 0 auto;">
  6. <div class="form-row" style="clear: left;">
  7.         <label style="float: left; clear: left;" for="lastname">Vezetéknév</label>
  8. <input id="lastname" class="register-input-narrow kotelezo" name="mssys_lastname" type="text" /></div>
  9. <div class="form-row" style="clear: left;">
  10.         <label style="float: left; clear: left;" for="firstname">Keresztnév</label>
  11. <input id="firstname" class="register-input-narrow kotelezo" name="mssys_firstname" type="text" /></div>
  12. <div class="form-row" style="clear: left;">
  13.         <label style="float: left; clear: left;" for="email">Email cím</label>
  14. <input id="email" class="register-input-narrow kotelezo email" name="email" type="text" /></div>
  15. <div class="form-row" style="clear: left;">
  16.         <label style="float: left; clear: left;" for="honnan">Honnan hallottál rólunk?</label>
  17. <input id="honnan" class="register-input-narrow" name="honnan" type="text" /></div>
  18. </div>
  19. <input class="desc" type="submit" value="Kérem a tanulmányt!" />
  20.   </form>
  21.  
  22. Ide is jöhet valamilyen szöveg, mondjuk arról, hogy nem adjuk az email címed másnak
  23. </div>

Menjünk szépen végig a kódon mikre kell figyelni, mik a lényeges részletek!
Nyilván nem a CSS-en van a lényeg, az csak az elrendezés miatt van.
Ha megnézzük az egész form egy div-be helyezkedik el, és osztályba van sorolva. Ez nem véletlen. A későbbiekben ezen div adott osztályain fogunk végigmenni, és ellenőrizni a kitöltöttség helyességét. Azért nem a form-on, mert a mezők a div-hez tartoznak, nem pedig a form-hoz, tehát a formon belül nem tudunk osztályokat keresni.
A felépítést tekintve megfigyelhetjük, hogy a mezőknek van id paraméterük és egy label követi. Ez fontos és sokan elfelejtik. Egy modert formban a mező címére kattintva is a mező válik aktívvá, ez különösen pl. a checkbox-oknál igaz. Gondoljunk bele, milyen rosz megoldás ha van egy checkbox mellette egy sor szöveg, és csak akkor válik kiválaszottá a checkbox ha a kis négyzetébe kattintuk, ha a szövegre akkor nem történik semmi. Erre teljesen tökéletes a mező id és a label for párosítás.
A három kötelező mező a Vezetéknév, Keresztnév, és Email cím. Ezeket a kotelezo osztályba soroljuk. Megfigyelhetjük az Email cím mellett ott van az email oszály tag is. Itt megjegyezném, hogy ha egy HTML elemet szóközökkel elválasztva osztályokba sorolunk az azt jeleni, hogy az összes adott osztályba tartozik. Tehát például az Email cím az egyszerre tartozik a register-input-narrow, a kotelezo és az email osztályokba.

Nézzük meg a hozzá tartozó jQuery kódot:

  1. <script type="text/javascript">
  2. $(document).ready(function() {
  3.   $('.feliratkozo-form').submit(function() {
  4.     var mehet = true;
  5.     $('input.kotelezo', $(this).parent()).each(function() {
  6.       if ($(this).val() == '') {
  7.         mehet = false;
  8.       }
  9.     });
  10.    
  11.     if (mehet) {
  12.       var x = $('input.email', $(this).parent()).val();
  13.       var filter  = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
  14.       if (!filter.test(x)) {
  15.         alert('Nem valós az e-mail cím');
  16.         return false;
  17.       }
  18.       return true;
  19.     } else {
  20.       alert('Minden mező kitöltése kötelező!') ;
  21.       return false;
  22.     }
  23.   });
  24. });
  25. </script>

Láthatjuk, hogy az oldal betoltődésekor fut le a kód. Végigmegyünk az összes feliratkozo-form osztályon és lekezeljük a submit eseményét. Ebbe az osztályba soroltuk be az oldalon talalhátaó összes ellenőrizendő form-ot.
Eztán végigmegyünk az összes olyan input mezőn amit a kotelezo osztalyba soroltunk, és a form szülő objektumába tartozik. Ez az objektum az a div amibe elhelyzetük a form-ot.
Ha valami nincs kitöltve akkor a mehet változót hamis értékre állítjuk.

Ha a mehet változó értéke nem igaz, akkor alert ablakban informáljuk a felhasználót a hiba okáról és hamis értékkel térünk vissza a ciklusból, tehát nem fog lefutni a submit esemény. Ha igaz akkor folytatjuk a további szükséges ellenörzéseket. ez jelen esetben az email cím helyessége.

Miután minden szükséges ellenőrzést megtettünk az adatok függvényében fut le a submit esemény, az oldalon elhelyezett akárhány form esetén.

Előnye hogy pár sor, nem függ a form mezőinek számától és lényegesen lerövidül a tesztelés időtartalma. Az osztályokkal való mezőellenőrzés ha működik egy form-ra akkor működik a többire is, ha nem működik akkor semmelyiknél se fog.

Természetesen olyan ellenőrzésekre is szükség lehet mint például hogy egy legördülő select-ből mindenképpen válasszon az felhasználó egyet, de azt is hasonlóképpen bele lehet vinni a kódba.

Biztos sokakban felmerült már a kérdés, hogy hogyan lehet olyan hyperlink-et készíteni ami új ablakban nyílik meg, mégis valid.

Ugyebár megvan a lehetőség HTML segítségével új ablakba nyíló hivatkozás készítésére, de sajnos a hyperlink-nek nincs valid target attributuma.

A kellő eredményt hozza a következő kódrészlet, de nem sajnos nem lesz valid:

  1. <a href="http://drupal.org/" title="http://drupal.org/" target="_blank">drupal.org</a>

Én a megoldásra a jQuery JavaScript könyvtárat használom. Ez amúgy alapól benne van a Drupal CMS-ben. Egy nagyon egyszerű eljárást alkalmazok:
Azokat a hyperlink-ekekt amiket új ablak szeretnénk megnyitni elhelyezem a new_window osztályba, és az oldal betöltődésekor a jQuery segítségével helyettesíthetem a target=”_blank” paramétert.

  1. <script type="text/javascript">
  2. $(document).ready(function() {
  3.   $('a.new_window').click(function(){
  4.     window.open(this.href);
  5.     return false;
  6.   });
  7. });
  8. </script>
  9.  
  10. <a class="new_window" href="http://drupal.org/" title="http://drupal.org/">drupal.org</a>