Постове 1 - 5 от 80 в категория Coding

Mar 24

Днес прегледах поста – Image Resizing Made Easy with PHP от Nettuts+. Доста полезен пост, за начинаещи. Но както винаги имах няколко забележки относно от кода. Мислех да ги запиша като коментар, но той се оказа доста дълъг. Затова реших да го напиша като пост.

Като за начало ще е добре да видите началния код от тук ( променената от мен версия е тук). Така промените метод по метод.

openImage

private function openImage($file)
{
    // *** Get extension
    $extension = strtolower(strrchr($file, '.'));

    switch($extension)
    {
        case '.jpg':
        case '.jpeg':
            $img = @imagecreatefromjpeg($file);
            break;
        case '.gif':
            $img = @imagecreatefromgif($file);
            break;
        case '.png':
            $img = @imagecreatefrompng($file);
            break;
        default:
            $img = false;
            break;
    }
    return $img;
}
// става:
private function openImage($file){
    if (!is_file($file)){
        throw new Exception("File {$file} doesn't exists");
    }

    switch(pathinfo($file, PATHINFO_EXTENSION)){
        case 'jpg':
        case 'jpeg': return imagecreatefromjpeg($file);
        case 'gif':  return imagecreatefromgif($file);
        case 'png':  return imagecreatefrompng($file);
    }

    throw new Exception("Invalid image extension for {$file}. Acceptable image types are jpg,jpeg,gif,png");
}

Като за начало тук PHP има вградена функция pathinfo, която извикана с PATHINFO_EXTENSION константа като втори аргумент връща какво е разширението на файла.

Също така няма смисъл от дефинирането на променливите $extension  и $img защото реално се използват един път. $extension за switch-a, а пък $img само да се  return-е.

Също така @ е нещо което НЕ ТРЯБВА да се използва!  Освен че е страшно бавно (ако някой се интересува ще обясня вътрешно какви глупости прави и защо е толкова бавна операция).  От друга страна не е много редно да се крият грешките в програмите.

За това тук съм добавил два Exeption-а. По-принцип не обичам да ги ползвам, но в този случаи е наложително.  Трябва да се провери, първо дали файла който искаме да променяме съществува и второ дали от позволените типове.

resizeImage

public function resizeImage($newWidth, $newHeight, $option="auto")
{
    // *** Get optimal width and height - based on $option
    $optionArray = $this->getDimensions($newWidth, $newHeight, $option);

    $optimalWidth  = $optionArray['optimalWidth'];
    $optimalHeight = $optionArray['optimalHeight'];

    // *** Resample - create image canvas of x, y size
    $this->imageResized = imagecreatetruecolor($optimalWidth, $optimalHeight);
    imagecopyresampled($this->imageResized, $this->image, 0, 0, 0, 0, $optimalWidth, $optimalHeight, $this->width, $this->height);

    // *** if option is 'crop', then crop too
    if ($option == 'crop') {
        $this->crop($optimalWidth, $optimalHeight, $newWidth, $newHeight);
    }
}
// става:
public function resizeImage($newWidth, $newHeight, $option='auto'){
    list($width, $height) = $this->getDimensions($newWidth, $newHeight, $option);

    $this->imageResized = imagecreatetruecolor($width, $height);
    imagecopyresampled($this->imageResized, $this->image, 0, 0, 0, 0, $width, $height, $this->width, $this->height);

    if ($option == 'crop'){
        $this->crop($width, $height, $newWidth, $newHeight);
    }
}

List е доста подценявана PHP функционалност. Но за да може да се работи с нея промених и getDimensions да връща просто масив с два елемента на позиция 0 и 1. ( По-долу ще обясня за нея)

После промених имената на променливите $optimalWidth и $optimalHeight. От една страна са доста дълги и докато се пишат трябва да се натиска и shift заради W и H. От друга те се състоят от две думи, като едната е “optimal” която не ни трябва, даже и грешна. Защото се превеждат “оптимална ширина” и “оптимална височина” докато реално те са просто “ширина” и “височина” (т.е. $width и $height).

Описвам толкова дълго за тези променливи защото именуването на променливите е голяма част от красивия и разбираемия код.

getDimensions

private function getDimensions($newWidth, $newHeight, $option)
{

   switch ($option)
    {
        case 'exact':
            $optimalWidth = $newWidth;
            $optimalHeight= $newHeight;
            break;
        case 'portrait':
            $optimalWidth = $this->getSizeByFixedHeight($newHeight);
            $optimalHeight= $newHeight;
            break;
        case 'landscape':
            $optimalWidth = $newWidth;
            $optimalHeight= $this->getSizeByFixedWidth($newWidth);
            break;
        case 'auto':
            $optionArray = $this->getSizeByAuto($newWidth, $newHeight);
            $optimalWidth = $optionArray['optimalWidth'];
            $optimalHeight = $optionArray['optimalHeight'];
            break;
        case 'crop':
            $optionArray = $this->getOptimalCrop($newWidth, $newHeight);
            $optimalWidth = $optionArray['optimalWidth'];
            $optimalHeight = $optionArray['optimalHeight'];
            break;
    }
    return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight);
}
// става:
private function getDimensions($width, $height, $option){
   switch ($option){
        case 'portrait':   return array($this->getSizeByFixedHeight($height),  $height);
        case 'landscape':  return array($width, $this->getSizeByFixedWidth($width));
        case 'auto':       return $this->getSizeByAuto($width, $height);
        case 'crop':       return $this->getOptimalCrop($width, $height);
        case 'exact':
        default:           return array($width, $height);
    }
}

Първо имената на променливите $newWidth и $newHeight на  $width и $height. Причините ги обясних по-горе за $optimalWidth и $optimalHeight.

Промених и формата на масива който се връща от getDimensions от асоциативен масив с ключове optimalWidth и optimalHeight на прост масив с индекси 0 и 1. Кода става доста по четим и по-горе в resizeImage метода може да се ползва List.

Премахват се и излишните променливи $optimalWidth и $optimalHeight, защото реално ни трябва просто масива с двата елемента. И от съкращаването на формата му се вижда че тези две променливи са още по-излишни.  Когато се намерят техните стойности веднага може да се върнат в масив, вместо да се чака да се стигне най-долу.

И накрая премествам ‘exact’ да бъде и default случая в switch.

getSizeByFixedHeight /  getSizeByFixedWidth

private function getSizeByFixedHeight($newHeight)
{
    $ratio = $this->width / $this->height;
    $newWidth = $newHeight * $ratio;
    return $newWidth;
}

private function getSizeByFixedWidth($newWidth)
{
    $ratio = $this->height / $this->width;
    $newHeight = $newWidth * $ratio;
    return $newHeight;
}
// става:
private function getSizeByFixedHeight($height){
    return ($this->width / $this->height) * $height;
}

private function getSizeByFixedWidth($width){
    return ($this->height / $this->width) * $width;
}

Много мразя дефинирането на излишни променливи. Защото докато се чете кода трябва да се помни какво има във съответната променлива.

Когато се погледне примерно getSizeByFixedHeight в оригиналната версия, кода се чете:

- Дефинирам $ratio който е ширината на снимката разделена на височината и
- Дефинирам нова височина, която е новата ширина умножена по $ratio ( мисля и се сещам какво има в $ratio )
- Връщам новата височина

Докато моята версия се чете просто:

- Разделям оригналата ширината на снимката на височината, полученото го умножавам по $width и връщам стойността.

getSizeByAuto

private function getSizeByAuto($newWidth, $newHeight)
{
    if ($this->height < $this->width)
    // *** Image to be resized is wider (landscape)
    {
        $optimalWidth = $newWidth;
        $optimalHeight= $this->getSizeByFixedWidth($newWidth);
    }
    elseif ($this->height > $this->width)
    // *** Image to be resized is taller (portrait)
    {
        $optimalWidth = $this->getSizeByFixedHeight($newHeight);
        $optimalHeight= $newHeight;
    }
    else
    // *** Image to be resizerd is a square
    {
        if ($newHeight < $newWidth) {
            $optimalWidth = $newWidth;
            $optimalHeight= $this->getSizeByFixedWidth($newWidth);
        } else if ($newHeight > $newWidth) {
            $optimalWidth = $this->getSizeByFixedHeight($newHeight);
            $optimalHeight= $newHeight;
        } else {
            // *** Sqaure being resized to a square
            $optimalWidth = $newWidth;
            $optimalHeight= $newHeight;
        }
    }

    return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight);
}
// става:
private function getSizeByAuto($width, $height){
    if ($this->height < $this->width){
        return array($width, $this->getSizeByFixedWidth($width));
    }

    if ($this->height > $this->width){
        return array($this->getSizeByFixedHeight($height), $height);
    }

    if ($height < $width){
        return array($width, $this->getSizeByFixedWidth($width));
    }

    if ($height > $width){
        return array($this->getSizeByFixedHeight($height), $height);
    }

    return array($width, $height);
}

Колкото по надълбоко влизат вложените структури, толкова по-грозен е кода. Реално тук всяка ситуация може да се изнесе в отделен if и просто ако този if се изпълни се връща резултат веднага и така докато се стигне до края.

getOptimalCrop

private function getOptimalCrop($newWidth, $newHeight)
{

    $heightRatio = $this->height / $newHeight;
    $widthRatio  = $this->width /  $newWidth;

    if ($heightRatio < $widthRatio) {
        $optimalRatio = $heightRatio;
    } else {
        $optimalRatio = $widthRatio;
    }

    $optimalHeight = $this->height / $optimalRatio;
    $optimalWidth  = $this->width  / $optimalRatio;

    return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight);
}
// става:
private function getOptimalCrop($width, $height){
    $ratio = min($this->height / $height, $this->width /  $width);
    return array(
        $this->width  / $ratio,
        $this->height / $ratio
    );
}

Мin е  друга малка PHP функция с чиято помощ този код се по изчиства. Защото реално на нас ни трябва минималната стойност от двете зависимости.

Тук пиша на два реда връщането на масива, отново заради четимостта.

saveImage и crop

private function crop($optimalWidth, $optimalHeight, $newWidth, $newHeight)
{
    // *** Find center - this will be used for the crop
    $cropStartX = ( $optimalWidth / 2) - ( $newWidth /2 );
    $cropStartY = ( $optimalHeight/ 2) - ( $newHeight/2 );

    $crop = $this->imageResized;
    //imagedestroy($this->imageResized);

    // *** Now crop from center to exact requested size
    $this->imageResized = imagecreatetruecolor($newWidth , $newHeight);
    imagecopyresampled($this->imageResized, $crop , 0, 0, $cropStartX, $cropStartY, $newWidth, $newHeight , $newWidth, $newHeight);
}
public function saveImage($savePath, $imageQuality="100")
{
    // *** Get extension
             $extension = strrchr($savePath, '.');
                $extension = strtolower($extension);

    switch($extension)
    {
        case '.jpg':
        case '.jpeg':
            if (imagetypes() & IMG_JPG) {
                imagejpeg($this->imageResized, $savePath, $imageQuality);
            }
            break;

        case '.gif':
            if (imagetypes() & IMG_GIF) {
                imagegif($this->imageResized, $savePath);
            }
            break;

        case '.png':
            // *** Scale quality from 0-100 to 0-9
            $scaleQuality = round(($imageQuality/100) * 9);

            // *** Invert quality setting as 0 is best, not 9
            $invertScaleQuality = 9 - $scaleQuality;

            if (imagetypes() & IMG_PNG) {
                 imagepng($this->imageResized, $savePath, $invertScaleQuality);
            }
            break;

        // ... etc

        default:
            // *** No extension - No save.
            break;
    }

    imagedestroy($this->imageResized);
}
// става:
private function crop($optimalWidth, $optimalHeight, $width, $height){
    $x = ( $optimalWidth  / 2) - ( $width  /2 );
    $y = ( $optimalHeight / 2) - ( $height /2 );

    $crop = $this->imageResized;

    $this->imageResized = imagecreatetruecolor($width , $height);
    imagecopyresampled($this->imageResized, $crop, 0, 0, $x, $y, $width, $height , $width, $height);
}

public function saveImage($savePath, $imageQuality="100"){
      switch(pathinfo($savePath, PATHINFO_EXTENSION)){
        case 'jpg':
        case 'jpeg':
            if (imagetypes() & IMG_JPG){
                imagejpeg($this->imageResized, $savePath, $imageQuality);
            }
            break;

        case 'gif':
            if (imagetypes() & IMG_GIF){
                imagegif($this->imageResized, $savePath);
            }
            break;

        case 'png':
            if (imagetypes() & IMG_PNG){
                // Scale quality from 0-100 to 0-9
                // Invert quality setting as 0 is best, not 9
                $invertScaleQuality = 9 - round(($imageQuality/100) * 9);
                imagepng($this->imageResized, $savePath, $invertScaleQuality);
            }
            break;
    }

    imagedestroy($this->imageResized);
}

Промените по saveImage и crop са просто стилистични, така че няма да се спирам много подробно на тях.

Финални думи

Като започнах да чистя кода си обещах, че няма да променям имената на методите. Но ако питате мен от публичните методи – resizeImage и saveImage бих махнал “Image” и да станат само resize и save. Защото класа най-често би се използвал с променливи кръстени $resizer, $resize, $image и подобни и затова rеsize и save биха достатъчно ясно.

Като цяло урока от Nettuts+ е много добър. Въпроса че според мен в уроците за начинаещи, които главно ползват Nettuts+, трябва да има максимално добър код. Без излишен “шум”.

Надявам се този малък анализ да е бил полезен на някого. Приемам всякакви критики и съвети, под формата на коментари :)

Mar 14

Наскоро се наложи да поработя по един доста стар PHP проект, по който не бях работил от години. И там видях нещо, което ведна опреличих като code smell (Всъщност видях много такива неща, но само на това ще обърна внимание).

$cart = CartManager::getCurrentCart();
$cart->setClientInfo('atype',    $user['account_type']);
$cart->setClientInfo('fname',    $user['fname']);
$cart->setClientInfo('lname',    $user['lname']);
$cart->setClientInfo('street',   $user['street']);
$cart->setClientInfo('postnum',  $user['postcode']);
$cart->setClientInfo('epost',    $user['mail']);
$cart->setClientInfo('city',     $user['city']);
$cart->setClientInfo('company',  $user['company']);
$cart->setClientInfo('orgnum',   $user['orgnum']);
$cart->setClientInfo('phone',    $user['phone']);
$cart->setClientInfo('country',  $user['country']);

Което ако идвате от  Java света може и да ви изглежда нормално, но мен ме дразни.

На първо време направих метода setClientInfo да връща $this зада може да използвам прост method chaining. И кода стана така:

CartManager::getCurrentCart()
    ->setClientInfo('atype',    $user['account_type'])
    ->setClientInfo('fname',    $user['fname'])
    ->setClientInfo('lname',    $user['lname'])
    ->setClientInfo('street',   $user['street'])
    ->setClientInfo('postnum',  $user['postcode'])
    ->setClientInfo('epost',    $user['mail'])
    ->setClientInfo('city',     $user['city'])
    ->setClientInfo('company',  $user['company'])
    ->setClientInfo('orgnum',   $user['orgnum'])
    ->setClientInfo('phone',    $user['phone'])
    ->setClientInfo('country',  $user['country']);

Малко по-добре, но пак ми изглеждаше не естествено. Затова просто добавих възможността да може да се предава масив като аргумент. И така стана доста добре:

CartManager::getCurrentCart()->setClientInfo(array(
    'atype',    => $user['account_type'],
    'fname',    => $user['fname'],
    'lname',    => $user['lname'],
    'street',   => $user['street'],
    'postnum',  => $user['postcode'],
    'epost',    => $user['mail'],
    'city',     => $user['city'],
    'company',  => $user['company'],
    'orgnum',   => $user['orgnum'],
    'phone',    => $user['phone'],
    'country',  => $user['country']
));

За нещастие в този проект, нямаше как да направя кода стане още по-чист, без да се налага пренаписване на голяма част от проекта:

CartManager::getCurrentCart()->setClient($user);
Feb 07

Като цяло тази седмица мина под знака на Rails 3, на който излезе бета версия.

Понеделник 01.02.2010

SafeBuffers and Rails 3.0 – в Rails 3 има вградена xss защита, и Yehuda Katz обяснява последните промени по тази система и защо са се наложили.

8 Chrome Extensions For Web Developers – Напоследък Firefox започва да се държи все по зле и леко да изостава от браузъри като Chrome и Safari. И докато в Safari все още ми липсват 2-3 малки неща, то Chrome може в много скоро време да стане предпочитания ми браузър.

Вторник 02.02.2010

Google Apps Drop IE6 Support – Е време беше. След скорошната атака срещу Google извършена с IE6, Google решиха да дръпнат шалтера на това нещо. В Германия вече го забраниха, така че още малко живот му остава. И усещам как скоро ще се вдигнем мерника към IE7.

One-click Minifier Gadget (OMG) – initial checkin – Българското Yahoo! гуру Стоян Стефанов пусна много полезен инструмент за минимизиране. Кода го има в github. ( Статията е от 31 Януари, но чак във Вторник я видях )

Basic Cappuccino Tutorial - Добър на урок за Cappuccino. Което е добре защото, един от най-големите проблеми на Cappuccino, според мен, е липсата на добра документация и уроци.

How Internet Explorer 8 document mode affects JavaScript – Оказва се че в IE8 имало доста промени по самия JavaScript.  Nicholas C. Zakas разяснява голяма част от тях.

The touch action – В България до голяма степен сме 1-2 години назад в IT отношение. Така че е доста лесно да се предскаже че в следващите години и у нас както навън ще навлязат все повече “умни телефони” и особено такива с touch screen (то май всички са такива). Затова е важно да се знае как може да се работи със touch събитието. В Quirksmode имаше цяла серия за тези събития през седмицата.

an HTML5 offline image editor and uploader application – Наскоро започнах да правя първите залежи за новата ни cms система ControlDepo 4. И мисля в нея да вградя нещото такова в една или друга степен.

HipHop for PHP: Move Fast – Това беше новината на деня. Facebook пуснаха HipHop, което компилира (или по-точно трансформира) PHP кода до оптимизиран C++ код, който след това се компилира с g++. От което е излишно да казвам какви са ползите като скорост и CPU. За повече подробности самия пост и това видео.

Явно вторник е бил много “натоварен” ден. :)

Сряда 03.02.2010

YUI Theater — Douglas Crockford: “Crockford on JavaScript — Volume 1: The Early Years” – Когато Douglas Crockford, говори и Чък Норис слуша :) . В случая говори за история на JavaScript и програмните езици като цяло. Първите 10-15 са необичайно скучни. Главно защото говори за първите компютри и темата е повече насочена към хардуера от колкото към софтуера. Но после нещата се оправят.

Stop Being an Idiot – Колкото пъти сме се оправдавали с това че потребителите са “идиоти”, е време е да спрем.

Четвъртък 04.02.2010

The Rails Module (in Rails 3) -  Rails 3 идва с доста променен Rails module, в който има доста полезни методи. От тази статия може да се научат доста странични неща за Rails като цяло.

Също интересен факт е и че Basecamp стана на 6 години. Като един ден преди това стори и Facebook.  Което като се замисли човек е много странно съвпадение. Защото никои не може да се отрече че и двата продукта косвено или директно промениха мрежата.

Петък 05.02.2010

SD Ruby – Episode 077: Building Your Own Hosting Environment – това стои във readera ми от доста време, но чак в Петък имах време и повод да го гледам. Доста добро ми се стори и ако някои мисли сам да си хоста Rails приложенията ( и даже каквото и да е друго сам да си хоства) е много добра начална точка.

Преч седмица най-сетне намерих една от най-запомнящите реклами, който съм гледал (незнайно защо бях останал с впечатлението  че е за Ferrari, а тя била за Porsche ):

Jan 31

Както написах предишната седмица съм решил всяка седмица да събирам малко линкове и да ги описвам тук. За съжаление за поредна седмица нямах време да довърша някои от дълбоко замразените постове. Но мисля, че следващата ще ми е по-свободна.

Понеделник 25.01.2010

Yehuda’s – evented programming with jQuery – това се явява като (макар и само косвено) продължение на Creating UI Elements With Low-Coupling And Conditional Event Handling, за което писах миналата седмица. Което за пореден път ми показва, че май javascript компонентите на там са се насочили.

In praise of git’s index – много добра статия за git index-ите.

Вторник 26.01.2010

The Path to Rails 3: Approaching the upgrade и rails-upgrade: Automating a portion of the Rails 3 upgrade process. Rails 3 е почти готов след малко повече от година разработка. И от където и да го погледнеш си е огромна стъпка на пред, даже няколко. В постовете се обяснява за проблеми и съвети при преминаването към Rails 3.

JRuby за Аndroid – време беше. Как ви се струва едно ruby приложение за телефон през JRuby за Android, през MacRuby за iPhone и … (не че някой ще ползва windows mobile), но IronRuby за Windows Мobile. Преди време си бях говорил с познати за такава идея и че на теория е възможно (а според тях на практика не). Но това е още една стъпка в тази посока. Въпреки че анатомиите на iPhone / Android приложенията са доста различни, все повече си мисля, че е възможно да стане нещо такова.

Answering Baranovskiy’s JavaScript quiz – Явно Вторник е бил доста мързелив ден.  Nicholas C. Zakas показва отговорите на js теста на Dmitry  Baranovskiy. Аз с радост мога да се похваля, че само за единия въпрос се подвоумих малко (на този с arguments[2] = 10).

Сряда 27.01.2010

Put that data-* attribute away, son…You might hurt someoneDan Webb е страшно уважаван от мен дивелопър. Беше част от PrototypeJs Core и беше работил по един от първите Rails плъгини за Unobtrusive JavaScript. Да не говорим колко идеи почерпих от неговата LowPro за моя CD3.Behaviors.  За нещастие, доколкото разбрах, вече работи главно с jquery.

Но да се върнем на темата, че малко се отнесох. Тук той обяснява за data-атрибутите и че колкото и да са яки, не трябва да се използват прекомерно. Поста му се явява като отговор на Yehuda’s – evented programming with jQuery.

Четвъртък 28.01.2010

Emile.js talk (video & slides)Thomas Fuchs обяснява неговия mini css animation framework – Emile.js. Който е само 51 реда (+ празните редове и Copyright-а :) ). Много полезно и абсолютно задължително за тези, които не искат цял живот само да ползват неща, които не разбират.

Why Arel – ActiveRecord 3.0 ( който е част от Rails 3 ) е изграден върху Аrel. Като наскоро  излезе и Active Record Query Interface 3.0. Но какво е Arel, ами Relational Algebra (и моята първа реакция като го чух беше – ЪЪъъ). В поста се обясняват мотивите за написването на Аrel. Важно е да се каже, че Arel е напълно отделно от Rails и може се ползва независимо.

The HTML 5 sandbox Attribute Improves iFrame Security – Една от критиките към html5 е, че не помага на този етап за сигурността на мрежата. Това е една малка стъпка напред. Жалко, че май никой не е чувал за <module> тага предложен от Douglas Crockford още в далечната вече 2006.

New ActionMailer API in Rails 3.0 – Голямото пренаписване в Rails 3 върви с пълни сили и сега ActionMailer си намери майстора. (Все пак май ActionMailer си е като недоносеното дете в Rails пакета).

Петък 29.01.2010

По-рано през седмицата Apple обявиха така дългоочаквания iPad. И общо взето никой не го харесва. Като за мен си е просто играчка. На който ако и бяха сложили просто MacOsX щяха да разбият всички, но такъв е живота. Общо взето в Петък имах много работа и нищо интересно не ми попадна. Само 2-3-4… статии за iPad. Всичките еднакви от една страна Apple хейтърите го нареждат от всякъде, Аpple феновете леко разочаровани, поради големите очаквания и само неутралните наблюдатели с положителни оценки (леко резервирани, но положителни като цяло).

Бонус

Седмицата беше много изморителна и затова нямах много време да си прегледам Google Reader-a (все още имам към 200 не прочетени статии). Затова чак днес видях серия от 3 поста от Amix, създателят на Todoist и Plurk, и първият блог, който започнах да следя.

Накрая ще сложа едно футболно клипче, на което попаднах тази седмица, за голове вкарани чрез отборна игра:

Jan 19

В последно време много се дразня на хората, които си мисля, че изграждането на един сайт е супер проста и лесна работа. И че от техническа гледна точка един сайт (или даже уеб приложение) е в пъти по-прост от едно десктоп приложение. Затова реших да драсна няколко реда, за някои от важните неща за който трябва да се мисли в един сайт от frontend страната.

Като начало – html

За създаването на един сайт, трябва html. По принцип html е просто нещо, но и той като php-то е “измамно лесен”. За една добра html структура какво трябва да се гледа?

Първо дали е семантична – всеки  html си има някакво значение. И това значение трябва да се ползва. Въпреки, че понякога това е много трудно. Примерно тага за списък <ul> трябва да се използва всеки път, когато има меню или списък от каквото и да е естество.  За всяка форма към всеки елемент трябва да има <label> със съответния for атрибут, оказващ за кои елемент е този label. И т.н.

След това идва и seo-то. Много е важно html да е построен така, че една търсеща машина (google например) много лесно да може да се ориентира в самия сайт какво има. От една страна това е свързано със семантиката на html. Примерно е важно заглавието на страницата да е със <h1>, да има коректни meta тагове (по възможност различни за всяка страница), всяка връзка, ако няма текст, да има поне title атрибут. Както и всяка картинка. Също така е важно и реда на връзките в html къде ще са, за google колкото по-нагоре е връзката толкова по-голям приоритет има. Затова примерно в даден сайт в кода текста на страницата може да е преди менюто, но визуално менюто да е от ляво, а текста от дясно.

Картинки

Всеки сайт трябва да има картинки, нали така? Обаче и тук има няколко особености. Като за начало е важно да се разграничат два вида картинки – едните са част от дизайна на сайта, а другите са част от неговото съдържание. Тези, които са част от дизайна са примерно фона на сайта, различни орнаменти и други подобни. А тези които са част от съдържанието са примерно снимки на продукт, ако това е каталожен сайт или снимка от новина, ако в сайта има новини.

При добре свършена работа <img> тага се ползва само за снимки от съдържанието. Като всяка една от тях трябва да има алтернативен текст в alt пропъртито. Докато другите картинки, част от дизайна, се слагат като фон чрез css (за него по-долу).

Много важна е оптимизацията на снимките. Като за начало е почти задължително вече всички картинки от дизайна да се обединяват в sprite. Т.е. да се обединят в една снимка и тя да се прилага. Вече, в зависимост от дизайна, може да се ползват и няколко sprite-а. Така от една страна е по-лесно да се организира сайта (колкото и странно да се струва това на пръв поглед). От друга, сайтът се зарежда по-бързо, защото трябва да се изтегли само една снимка, а не примерно 10-20.

Също така е важно всяка снимка (особено sprite)  да мине през нещо като SmushIt, за да и се намали размера, като и се изчистят ненужни мета данни.

Друга важна подробност е, че ако по дизайн някоя връзка, примерно от менюто, трябва да е картинка (например, защото там се ползва някой изчанчен шрифт с antialiasing), тя трябва да се сложи като фон. А текста, който е написан в нея, да се сложи и като title атрибут на връзката и да се сложи като текст в самата връзка (и да се скрие с css).

Cascading Style Sheets или за по-кратко css

Стиловете трябва да са отделени в css файл, за предпочитане само в един за по-лесно зареждане. Не е препоръчително ползването на <style> тага или inline стилове (style=”").

Ако се ползват стилове предназначени само за IE, не трябва да се ползват с хакове, а да са отделени във файлове според версията на IE чрез conditional comments. Например <!–[if lte IE 6]> <link rel=”stylesheet” type=”text/css” href=”stylesheets/IE6fix.css” /> <![endif]–> ще зареди файл важащ само за версии на IE до 6 включително. Стиловете трябва да се организират поне според структурата на html-а (header, content, sidebar, footer…) и по значението им (за текст, форми, заглавия…).

Не лоша идея е да се използва css reset.

Хубаво е пропъртитата да са подредени по азбучен ред и задължително да се ползват shorthand. Например padding-top: 5px; padding-right: 15px; padding-bottom: 12px; padding-left: 10px може да се изпише padding: 5px 15px 12px 10px. Имената на class и id трябва да са смислени, указващи частта от html, за която са предназначени (header, footer, nav_main, nav_categories). Ползването на дълги селектори като #header .menu .home li a се отразява зле на IE, а и по принцип е затормозяващо.

Ползването на нови селектори като box-shadow не е невъзможно, тъй като те имат поддръжка или алтернатива в повечето браузъри, включително IE, което намалява значително ползването на изображения. За намаляване и оптимизиране на css кода могат да се ползват css компресори, но такива, които не ползват нестандартни вредящи похвати, като да заменят strong с b. Освен това може да се приложи компресия gzip.

Приложна магия – JavaScript

JavaScript е един от най-неразбраните програмни езици в света. Поради различни причини, за които, ако трябва да пиша, ще ми трябват поне 1-2 поста още. Но въпреки това е един от любимите ми езици.  :)

Надявам се да не се налага да казвам че всичкия JavaScript трябва да е  unobtrusive.

За JavaScript си имам едно правило – всичко в сайта трябва да работи без javascript. Тук идеята е следната. Ако примерно имаме галерия със снимки – една голяма и малки тъмбнейли – при натискане на даден тъмбнейл се сменя голямата снимка. Обаче ако няма javascript, какво правим? Много просто – всяка снимка при натискане да се отваря в нов прозорец, за да може потребителя така или иначе да я види. А ако има javascript просто ще спрем отварянето и ще я покажем с хубав ефект.

Естествено, има неща, които без javascript не работят. Тук правя следното – просто ги крия, т.е. при зареждане на страницата са маркирани като невидими и след като се зареди javascript-а ги показвам.

Само тук искам да отбележа, че става дума за най-нормален сайт, който трябва да бъде достъпен отвсякъде и понякога без JavaScript просто няма накъде.

Браузъри

Yahoo имат един списък с браузъри – http://developer.yahoo.com/yui/articles/gbs/. Дефакто в A-Grade (и IE6) браузърите се налага сайта да работи. За IE6 може и да не работи толкова добре. Все пак, който ползва IE6 (и като цялата IE фамилия де), си е свикнал да не вижда хубави работи…

Скорост

Една от новите ми любими теми.

Като за начало има 2 инструмента, с които може да се замери скоростта на сайта по различни показатели (става дума само за html/css/js/assets часта) – YSlow и Google Page Speed.

Някои от най-важните неща са:

  • css най-отгоре и в един файл
  • javascript най-отдолу и в един файл
  • компресиране на всичко, което се праща от сървъра (тук има и малко работа по server-side)
  • expire headers
  • по-малко DOM  (html) елементи
  • по-прости css селектори

Една нова дума взе да се появява из различни презентации и постове при темата за Скорост и това е – Progressive HTML Rendering.

Идеята там е, че за потребителя страницата се “построява” пред очите му и така се създава усещането за скорост. За това много помага JavaScript да е възможно най-долу. Защото когато браузърът стигне <script> таг, забравя всичко останало. Затова много хора се насочват към това с малко javascript да зареждат останалия javascript, защото така не се блокира нишката на браузъра.

Заключение

И това е само една малка част от нещата, които се правят само по обвивката на един сайт. По принцип за всяка точка тук може да се пишат серии от постове. Да не говорим колко много развитие и промени има по темата.

Естествено. винаги може някой да си хване един dreamweaver и да си нареди 2-3 картинки и да каже, че има сайт. То и аз така съм почнал. Но в един момент това си е професия със своите тънкости и особености. Даже и името и е много яко -  Frontend Engineering.

п.п. Само да вметна, че аз се водя web developer  в Pixel Depo и основно работя по нещата, които стоят под frontend-a и JavaScript-a . :)