81 látogatás.

Asztali és mobilmegjelenés szétválasztása

Pepita

2013-08-04 18:20:26

Cikk képEgyre szélesedik a különféle hordozható eszközökkel szörfölők köre, ezért egy valamirevaló honlapnak megfelelő megjelenést kell biztosítania ezeken a – többnyire a monitoroknál jóval kisebb – megjelenítőkön is. Írásommal egy lehetséges megoldást mutatok be, ami talán sokaknak új és idegen lesz, viszont lévén sok más megoldási lehetőség is: követni nem kötelező, de mindenkinek tanulságos lehet.

Ez a cikkem a Weblaboron jelent meg korábban, itt egy kicsit más formában, kevesebb forráskóddal jelenik meg, azért is, mivel megtekinthető működés közben és letölthető is.

Kezdetek

Eleinte én is – ahogy sokan mások – a link[media="handheld"] attribútummal operáltam, de hamar rá kellett jönnöm: ez nem a legjobb megoldás, mert nem minden mobileszköz használja megfelelően, illetve a mobilon nem megjelenítendő tartalmat is letölti a kliens, tehát a szűkös sávszélesség továbbra is problémát jelent.

Kicsit körülnéztem és azt láttam, hogy egyes honlapokon például m.example.com aldomainen szolgálják ki a mobilos látogatókat. Ezt sokszor összekötik egy user agent ellenőrzéssel, ami alapján – ha mobileszköz – átirányítanak az aldomainre. Ezt ma sem tartom egészen jó megoldásnak, mert szerintem egy honlap legyen egy domainen, illetve user agent kerülhet elő naponta is új, így az összehasonlításhoz szükséges adatbázist nagyon sűrűn kell(ene) frissíteni.

Ezek fényében lassan kialakult a koncepcióm:

  • Alapjában véve nem rossz dolog a user agent figyelése, de nem szabad mindent erre alapozni.
  • A látogatónak legyen lehetősége beavatkozni, ne döntsünk teljes mértékben helyette.
  • Ha a második pontot felhasználóbarát módon tudjuk kivitelezni, akkor már nem is olyan nagy baj, ha tévedünk a detektáláskor.
  • Azokat a tartalmi részeket, amelyeket a mobil-nézetben nem akarunk megjeleníteni, ne is küldjük ki a HTML-ben.
  • A honlap maradjon egy domainen, egyszerű, aránylag kevés munkát igénylő szoftver legyen (alacsony költség).
  • A megoldás újrahasznosítható legyen.
  • Természetesen mindkét megjelenítés alkalmazkodjon a többféle kijelzőmérethez.
A megoldás

Megelégedve használom a CodeIgniter keretrendszert, ezért most is ebben dolgozunk. A bemutatott példa eléggé egyszerűsített, szándékosan nem használunk most adatbázist és a HTML5/CSS3 lehetőségeit.

Először is sablonokra lesz szükségünk: desktop.php, mobile.php és menu.php. A menüt azért tesszük külön fájlba, hogy minden nézethez ugyanazt használhassuk.

Tegyük fel, hogy azt a feladatot kaptuk, hogy asztali nézetben képes fejléc, alatta menü, alatta három hasáb, alul pedig lábléc legyen. Valahol legyen üdvözlő üzenet, ha most érkezett a látogató. A mobilnézetet ránk bízták, annyi megkötéssel, hogy a jobb oldalsáv tartalma nem kell, de a középső és a bal hasábé igen, előbbi a fontosabb. Ezen túl szabad a gazda.

Lásd: application/views/templates/desktop.php

A sablon változóinak természetesen majd megfelelő értéket kell adnunk, ezt részben egy saját konfig fájlból fogjuk megtenni, másrészt ezekben lesznek a dinamikusan generált tartalmak is. A div#message-ben felhasználói üzeneteket fogunk megjeleníteni.

public/css/desktop.css:

Ez csupán a dobozok és a menü stílusa, a többi elemé (címek, bekezdés stb.) többnyire reszponzív méretezésű (em, %).

Nézzük a mobil sablont! application/views/templates/mobile.php

Itt container-re sincs szükségünk, mivel nem csinálunk hasábokat, hadd legyenek a dobozok minél szélesebbek. Vegyük észre, hogy a fejlécbe csak a főcímet (oldal neve) írjuk ki, a szlogent nem. Ezzel további helyet spórolunk, hogy minél kevesebbet kelljen görgetni. (Tegyük fel, hogy a megrendelő beleegyezett.)

public/css/mobile.css:

Rövidebb lett a CSS is, a fejléc háttérképét lecseréltük egy egészen kicsire, amivel sávszélességet spórolunk. Mindkét sablon bal felső sarkában van a nézetváltó link: így görgetés nélkül azonnal látható. Már csak a menü van hátra:

application/views/templates/menu.php:

<ul>
  <li><a href="<?= base_url() ?>">Címlap</a></li>
  <li><a href="<?= base_url() ?>oldal/bongeszo">Böngésző</a></li>
  <li><a href="<?= base_url() ?>oldal/egyebek">Egyebek</a></li>
</ul>

Ennyi kód után azt is hihetnénk, hogy hátradőlhetünk, de nem így van. Most kezdhetünk gondolkodni azon, hogy mikor és hol (hogyan) kellene detektálni a böngészőeszközt. Kézenfekvő megoldásnak tűnne vezérlő előtti (pre controller) kampót (hook) használni, csakhogy van egy kis gond.

Úgy tudnánk kényelmesen megírni az osztályt, ha közben használhatnánk egy CI_Controller példányt is, ezen keresztül tudnánk betölteni és használni a szükséges session és user_agent osztályokat, valamint így tudunk közvetlenül vezérlő-adatokat létrehozni. Ehhez egy kicsit csalnunk kell: nem kampót írunk, hanem könyvtárat, amit az autoload.php megfelelő sorával fogunk betölteni.

$autoload['libraries'] = array('session', 'client_detect', 'parser');

A parser osztályt a vezérlőben fogjuk használni. A sorrend fontos, a session-t előbb töltjük be, mint a client_detect-et.

Ezeket az osztályokat a CI_Controller konstruktora tölti be, ezért az általunk írt vezérlő konstruktorában már elérhetjük, illetve módosíthatjuk a könyvtárunk által létrehozott adatokat, persze csak a parent::__construct(); sor után.

Szükségünk lesz még egy site.php nevű konfigurációs fájlra is, ebben tároljuk az alapértelmezett $title, $robots stb. értékeket:

<?php if (!defined('BASEPATH')) {
  exit('No direct script access allowed');
}

$config['def_title'] = 'Asztali-mobil megjelenítés';
$config['def_robots'] = 'all';
$config['def_keywords'] = 'mobilnézet, webdesign, webfejlesztés, mobileszköz';
$config['def_description'] = 'Asztali és mobilmegjelenés szétválasztása';
$config['site_name'] = 'Detektálás és választhatóság';
$config['def_slogan'] = 'Ez egy felhasználóbarát megoldás';
$config['welcome'] = '<h3>Üdvözöljük honlapunkon!</h3>';

Ezt a fájlt a $autoload['config'] = array('site'); sorral töltjük be, szintén az autoload.php-ben.

Mivel több egyidejű feladatunk is van az eszköz detektálásán kívül, ezeket is a Client_detect osztályban fogjuk megvalósítani.

application/libraries/Client_detect.php:

<?php if (!defined('BASEPATH')) {
  exit('No direct script access allowed');
}

class Client_detect {
  private $CI;
 
  public function __construct() {
    $this->CI =< get_instance();
    $this->CI->load->library(array('user_agent'));
 
    $this->Detect();
  }

A konstruktorban lekérjük a vezérlő referenciáját, majd ezzel betöltjük a user_agent osztályt, végül meghívjuk a detektáló függvényünket.

  private function Detect() {
    $this->CI->data['message'] = '';
 
    $x = file_get_contents(APPPATH . 'data/db.dat');
 
    if ($this->CI->session->userdata('usercount') === false) { // Most jött hozzánk, mert nincs usercount
    if ($this->CI->agent->is_mobile()) { // Mobil v. asztali?
      $this->CI->session->set_userdata('platform', 'mobile');
    } else {
      $this->CI->session->set_userdata('platform', 'desktop');
    }
 
    if ($this->CI->agent->is_robot()) {
// Robot, nem számoljuk $this->CI->session->set_userdata('usercount', $x); } else { // Nem robot, számoljuk $x += 1; file_put_contents(APPPATH.'data/db.dat', $x); $this->CI->session->set_userdata('usercount', $x); $this->CI->session->set_userdata('welcome', true); // Máshol felhasználható $this->CI->data['message'] = config_item('welcome'); } } else { // Már volt nálunk $this->CI->session->set_userdata('usercount', $x); $this->CI->session->unset_userdata('welcome'); } $this->Data_fill(); }

Itt a vezérlő $data tömbjét használjuk, ebben lesznek a nézet számára átadandó adatok, a vezérlőben majd kiegészítjük és továbbadjuk.

A $this->CI->session->userdata('usercount') a látogatók száma, ha ez nincs (false), akkor a látogató most érkezett. A 'platform' fogja tárolni a megjelenítő típusát, a 'welcome' pedig arra jó, ha esetleg a vezérlőben (vagy modellben) más műveletet is akarunk végezni új látogató érkezésekor (pl. cookie-login). Az üdvözlőüzenetet helyben beállítjuk.

A user_agent osztályhoz tartozó minták a user_agents.php-ben találhatók (config), mindenki tapasztalatai alapján bővítheti.

Végül ott egy $this->Data_fill() hívás: külön függvényben töltjük fel az alapértelmezett nézet adatokat, ezt a függvényt kell újrafelhasználáskor az adott honlapnak megfelelően átírnunk.

  private function Data_fill() {
    $this->CI->data['title'] = config_item('def_title');
    $this->CI->data['robots'] = config_item('def_robots');
    $this->CI->data['keywords'] = config_item('def_keywords');
    $this->CI->data['description'] = config_item('def_description');
    $this->CI->data['js2'] = ''; // Vezérlőben adjuk meg, ahol kell
    $this->CI->data['site_name'] = config_item('site_name');
    $this->CI->data['slogan'] = config_item('def_slogan');
 
    $this->CI->data['message'] .= $this->CI->session->flashdata('message');

    if ($this->CI->session->userdata('platform') == 'mobile') {
      $this->CI->data['js'] = 'mobile.js';
    } else {
      $this->CI->data['js'] = 'desktop.js';
    }
 
    $this->CI->data['left'] =
    '<h6>Bal box tartalma</h6>'
    . '<p>'
    . 'Látogatók száma:'
    . '<strong>'
    . $this->CI->session->userdata('usercount')
    . '</strong>'
    . '</p>'
    . '<p>Egyéb tartalom fantázia szerint.</p>'
    ;
 
    $this->CI->data['right'] = '<h6>Jobb box tartalma</h6><p>Akár reklámok/linkek is lehetnének.</p>';
  }
} // class Client_detect

Itt most egy nagyon leegyszerűsített adatfeltöltés van, természetesen az oldalsávokba valamilyen feldolgozott tartalom kellene, amit - pl. ha oldalanként különböző - kontrollerben vagy modelben állítanánk elő, és ha lehet, cache-elnénk.

A $this->CI->session->flashdata('message')-et átírányításkor (redirect) fogjuk használni felhasználói üzenet továbbítására. Fontos, hogy a $data['message'] csak detektáláskor lett beállítva, itt - és később kontrollerben - már hozzáfűzzük a további üzeneteket.

A $data['js']-t játékból állítjuk itt be, valójában inkább oldalanként különböző javascript csatolására hasznos. Ezt a két JS-t a sablonokban statikusan is megadhatnánk.

Detektáló osztályunk készen is van, szükségünk van még kontrollerre (Oldal), amit be is állítunk a routes.php-ben alapértelmezettnek: $route['default_controller'] = 'oldal';

application/controllers/oldal.php:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class Oldal extends CI_Controller {
 
  public function __construct() {
    parent::__construct();
    // Itt már manipulálhatjuk az adatokat:
      $this->data['message'] .= '<p>Üzenet átírva.</p>';
  } // public function __construct()

Mint már említettem: a szülő konstruktora után már a Client_detect által létrehozott adatot manipulálhatjuk. Természetesen ilyen állandó üzenetre ritkán van szükség, de a többi adat érdekes lehet számunkra már a konstruktorban is.

Mivel ez az alapértelmezett kontrollerünk, célszerű egy index() függvényt készítenünk, ez szolgálja ki a főoldalt:

  public function index() {
    // Főoldal
    $this->data['content'] = $this->parser->parse('pages/cimlap', array(), true);
    $this->view();
		
  } // public function index()

A főoldal tartalma az application/views/pages/cimlap.php fájlban van. Parser-t használunk, mert egyelőre változóba kell nekünk a tartalom, és mert annak ellenére is lehet a fájlban PHP kód, hogy csak üres adattömböt adtunk át.

Látható, hogy nem betöltöttük a view osztályt, hanem hivatkoztunk egy függvényünkre, tehát meg is kell írnunk:

  private function view() {
    // Mobil v. asztali?
    if ($this->session->userdata('platform') == 'desktop') {
$this->load->view('templates/all_desktop', $this->data); } else { $this->load->view('templates/all_mobile', $this->data); } } // private function view() } // class Oldal extends CI_Controller

Végre felhasználjuk a detektálás óta létező 'platform' session-adatot, ennek függvényében választunk a két (vagy akár több) sablonunk közül. Természetesen több függvényünk is szokott lenni a kontrollerben, ezért vettük külön ezt a függvényt.

Most még hátra van a kézi nézetváltás, aztán egy kis díszítés. Második kontrollerünk:

application/controllers/nezet.php:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class Nezet extends CI_Controller {
	
  public function __construct() {
    parent::__construct();
  } // public function __construct()
	
  public function mobil() {
    if ($this->session->userdata('platform') == 'mobile') {
      $this->session->set_flashdata('message', '<p>Már mobil-nézetet használ.</p>');
    }
    else {
      $this->session->set_flashdata('message', '<p>Mobil nézet.</p>');
      $this->session->set_userdata('platform', 'mobile');
    }
		
    $this->generate();
  } // public function mobil()
 
  public function asztali() {
    if ($this->session->userdata('platform') == 'desktop') {
      $this->session->set_flashdata('message', '<p>Már asztali-nézetet használ.</p>');
    }
    else {
      $this->session->set_flashdata('message', '<p>Asztali nézet.</p>');
      $this->session->set_userdata('platform', 'desktop');
    }
		
    $this->generate();
  } // public function mobil()
 
  private function generate() {
    $ref = $this->agent->referrer();
    if (($this->agent->is_referral()) and (mb_strpos($ref, base_url())) === 0) {
      redirect($ref, 'location', 302);
    }
    else {
      redirect(base_url(), 'location', 302);
    }
} // private function generate() } // class Nezet extends CI_Controller

Váltás végén egy átirányítással generáltatjuk újra az oldalt, ezért az üzenet továbbítására a Client_detect-ben látott 'message' flashdata-t használjuk.

A díszítést (JS) az így is nagy terjedelem miatt nem részletezném (a letölthető csomagban megtalálható), asztali verzión az aktuális menühöz adjuk az active_menu osztályt; mobilon egy SELECT-et készítünk a menüből, és végignézzük az IMG-ket, ha valamelyik szélesebb, mint a kijelző, akkor kicsinyítjük.

Ezzel a módszerrel lehet akár készülékenként eltérő design-t is megvalósítani - kellőképpen bővített és karbantartott user_agents.php-vel -, de mindig tartsuk szemelőtt, hogy agent string-et hamisat is kaphatunk, és a felhasználónak is hagyjuk meg a könnyű, egyszerű választás lehetőségét!

Itt, ezen a honlapon például ha túl kicsi a kijelző és asztalinak ismertük fel a készüléket, akkor a bal felső sarokban a Mobil link néhány másodpercig villog.

Remélem kezdők, haladók és profik számára is hasznos volt egy egyéni megoldásról olvasniuk, és azt is, hogy kedvet csináltam azok számára, akik még nem foglalkoztak mobilmegjelenéssel.

Horváth Péter


Nincsenek megjegyzések.