13 апр. 2009 г.

Плагин для jQuery, реализующий наследование

Недавно понадобилось для проекта с jQuery реализовать наследование, написал плагин для jQuery, где релизованы идеи, описанные в «Еще раз о наследовании в JavaScript». Плагин находится на code.google — code.google.com/p/jquery-inheritance/

Работает так:

$.inherit([base], methods, [statical])

Пример:

// A — базовый тип
var A = $.inherit(
    {

        // __constructor — специальный метод, вызываемый при создании экземпляра
        __constructor : function(property) {
                this.property = property;
        },

        getProperty : function() {
            return this.property + ' of A';
        },

        getType : function() {
            return 'A';
        }

    },
    {
        // статическое свойство, доступное изнутри как this.__self.staticMember
        staticMember : 'staticA'
    });

// B — тип, наследуемый от A
var B = $.inherit(
    A,
    {

        // перекрытие с вызовом одноименного метода базового класса
        getProperty : function() {
            return this.__base() + ' of B';
        },

        // просто перекрытие
        getType : function() {
            return 'B';
        }

    },
    {
        staticMember : 'staticB'
    });

var instance = new B('value');

console.log(instance.getProperty());
console.log(instance.getType());
console.log(instance.__self.staticMember);

А кто вообще как использует, и использует ли вообще, наследование?

5 апр. 2009 г.

Знатокам JavaScript

Как это работает?

if('mySuperProperty' in window) {
    alert(window['mySuperProperty']);
}

var mySuperProperty = 1;

16 мар. 2009 г.

ZForms 3.0

Несколько недель потраченного свободного времени, переписывание кучи кода, документации, примеров — куча сил ушла на ZForms 3.0. Главное — что теперь все работает почти «само», количество кода, необходимого разработчику для описания форм, уменьшилось на порядок, а функционала только прибавилось.

27 февр. 2009 г.

Чего мне не хватает в CSS

Несмотря на всю мощь CSS, некоторых вещей мне там все-таки не хватает.

Константы

Очень часто хочется сделать какие-то правило общими для нескольких селекторов. Приходится, либо копировать часть правил из селектора в селектор:

.class1 {
  font-size: 0.9em;
  color: #FF0000;
  position: absolute;
}

.class2 {
  font-size: 0.9em;
  color: #FF0000;
  float: left;
}

Или отдельно описывать общие части:

.class1 {
  position: absolute;  
}

.class2 {
  float: left;
}

.class1,
.class2 {
  font-size: 0.9em;
  color: #FF0000;
}

Вроде бы неплохо, но не всегда удобно — часто приходится группировать селекторы из разных частей css, в итоге размазывая описание одной сущности.

Или же создавать еще один CSS-класс, содержащий общие части, и добавлять его потом ко всем нужным HTML-элементам, что мне вообще не нравится:

.class1 {
  position: absolute;
}

.class2 {
  float: left;
}

.class3 {
  font-size: 0.9em;
  color: #FF0000; 
}

Душа же хочет чего-нибудь такого:

$font1 {
  font-size: 0.9em;
  color: #FF0000; 
}

.class1 {
  $font;
  position: absolute;
}

.class2 {
  $font;
  float: left;
}

Где-то читал, что что-то подобное поддерживает webkit.

Селекторы, с возможностями похожими на XPath

Часто хочется назначить какие-то правила контейнеру, если внутри контейнера есть какой-нибудь элемент. Например, сделать нижний отступ у контейнера, если внутри контейнера есть изображение (пример надуман, обычно все намного сложнее):

.container[descendant::img] {
  margin-bottom: 1em;
}

Мне кажется это гораздо более нужным чем всякие drop shadow и css-анимации.

24 февр. 2009 г.

Самые эффективные решения - самые простые

Оказывается, как просто можно в js проверить, является ли что-то массивом:

function isArray(o) {
  return Object.prototype.toString.call(o) === '[object Array]'; 
}

Источник: http://thinkweb2.com/projects/prototype/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/.

Кстати, в jQuery (начиная с 1.3), это проверяется именно этим способом.

Мне как-то всегда хватало o instanceof Array, но оказалось что у этого способа есть проблемы с фреймами. И еще, после замены instanceof Array на isArray у меня перестала течь память в IE6 в ZForms (вернее, в том случае, если ZForms использует родной Common.js).

30 янв. 2009 г.

CSS: clip и Internet Explorer

На днях понадобилось воспользоваться css-свойством clip для создания закруглений у блоков с помощью css-спрайтов. В нормальных браузерах заработало быстро. А вот разработчики IE, как обычно, выделились.

Оказалось, что в режиме quirks, IE понимает запись значений через запятую (так как написано в спецификации w3c) — clip: rect(100px, 100px, 145px, 30px), а в режиме соответствия стандартам только через пробел — clip: rect(100px 100px 145px 30px). Проверил на IE6 и IE7. Хорошо хоть остальные браузеры тоже понимают такую неправильную запись.

Чем думают разработчики IE?

9 нояб. 2008 г.

Пакетирование js- и сss-ресурсов. Управление подключением ресурсов с помощью XML/XSL.

В этой статье я затрону тему подключения ресурсов, а именно, как можно управлять подключением нужных JavaScript и CSS-файлов в больших проектах, с большим количеством функционала, чтобы это быстро не превратилось в хаос.

Я расскажу о способе, который использую в своем фрэймворке «Конфигуратор», однако, если вашим проектам не чужды понятия XML и XSL, вы легко можете адаптировать предлагаемый способ к своим нуждам.

Если вкратце, то я предлагаю разбивать все js- и css-ресурсы проекта на пакеты, исходя из их функциональности. Внутри пакета собирать входящие в него файлы и минимизировать их. В режиме разработки отдавать исходники как они есть, а в «боевом» — собранные по пакетам и минимизированные (для уменьшения количества http-запросов и размера файлов). Кроме этого, за каждую функциональность проекта должен отвечать отдельный xsl (который непосредственно и знает о необходимых этой функциональности ресурсах). Причем все подключения, сборки пакетов, и их минимизации должны происходить при как можно меньшем количестве телодвижений со стороны разработчика.

Рассмотрим на примере двух пакетов — global (содержащего базовую функциональность проекта) и zforms (содержащий функциональность для форм). Пакет global должен подключаться везде, а пакет zforms — только на страницах форм.

Xml-описание пакетов (должно быть доступно везде внутри проекта):

<package name="global" version="1">
  <css media="screen">
    <file name="common-screen.css" dev-mode="development" />
    <file name="main-screen.css" dev-mode="development" />
    <file name="main-colors-screen.css" dev-mode="development" />
    <file name="main-screen-package.css" dev-mode="production" />
  </css>
  <css media="screen" cc="if IE">
    <file name="common-screen-ie.css" dev-mode="development" />
    <file name="main-screen-ie.css" dev-mode="development" />
    <file name="main-screen-ie-package.css" dev-mode="production" />
  </css>
  <js>
    <file name="Common.js" dev-mode="development" />
    <file name="Common_Ajax.js" dev-mode="development" />
    <file name="Common-package.js" dev-mode="production" />
  </js>
</package>

<package name="zforms" version="2.43">
  <css media="screen">
    <file name="ZForms-screen.css" dev-mode="development" />
    <file name="ZForms-screen-package.css" dev-mode="production" />
  </css>
  <css media="screen" cc="if IE">
    <file name="ZForms-screen-ie.css" dev-mode="development" />
    <file name="ZForms-screen-ie-package.css" dev-mode="production" />
  </css>
  <js>
    <file name="ZForms.js" />
  </js>
</package>

Рассмотрим подробнее из чего состоит пакет. Во-первых, у пакета есть имя (@name), и версия (@version). Во-вторых, пакет делится на блоки по типу ресурсов: css и js. Каждый такой блок может содержать несколько файлов с @dev-mode = ’development’ (которые будут подключаться в режиме разработки) и один файл с @dev-mode = ’production’ (подключаемый в «боевом» режиме). Если же используемые файлы уже собраны (например вы используете какую-то библиотеку), то @dev-mode указывать не нужно. Для примера, именно так подлючается ZForms.js.

Кроме того, блоки пакета могут содержать необязательные атрибуты:

  • для css-блоков:
    • @media (screen/print/handheld)
    • @cc (для указания conditional comments, если стили блока нужно подключать только для IE)
    • @version (версия блока)
  • для js-блоков
    • @cc (аналогично css-блоку)
    • @version (версия блока)
    • @pack, true/false (использовать ли алгоритм base-62 для сжатия production-версии)

Суммарная версия файла будет складываться из версии пакета и версии блока. Версия будет добавлена к имени файла в качестве параметра — ?v={номер_версии}. Добавление этого параметра позволяет избежать кэширования браузерами предыдущей версии файла.

Я не буду останавливаться на подробном описании xsl-шаблонов для подключения ресурсов, приведу лишь общие моменты.

В общем шаблоне для всех страниц:

<xsl:template match="&document;" mode="cdocument:head">
  <head>
    ...
    <xsl:apply-templates select="." mode="cdocument:include-packages">
    ...
  </head>
</xsl:template>

<xsl:template match="&document;" mode="cdocument:include-packages">
  <xsl:call-template name="cinclude:package">
    <xsl:with-param name="name" select="'global'" />
  </xsl:call-template>
</xsl:template>

В шаблоне для форм перекрываем:

<xsl:template match="&document;" mode="cdocument:include-packages">
  <xsl:apply-imports /> <!-- подключение остальных пакетов -->
  <xsl:call-template name="cinclude:package">
    <xsl:with-param name="name" select="'zforms'" />
  </xsl:call-template>
</xsl:template>

В итоге, получаем, что за каждый функционал отвечает отдельный xsl, который вызывает шаблон для подключения своих ресурсов и, что самое важное, вызывает через apply-imports шаблоны для подключения всех остальных ресурсов текущей страницы, причем не только из пакета global, но и из других возможных независимых пакетов (при этом ничего не зная о них).

Все файлы с @dev-mode = ’production’ можно и нужно создавать автоматически при выкладке в «боевой» режим, распарсив xml-описание пакетов.

Текущее состояние dev-mode определяется глобальной переменной $cglobal:dev-mode, которая, в свою очередь, берет его из конфига проекта.