<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Антон Шевчук &#187; JavaScript</title>
	<atom:link href="http://anton.shevchuk.name/category/javascript/feed/" rel="self" type="application/rss+xml" />
	<link>http://anton.shevchuk.name</link>
	<description>Web-разработчик</description>
	<lastBuildDate>Wed, 01 Feb 2012 12:54:21 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Google Closure для начинающих. Компоненты</title>
		<link>http://anton.shevchuk.name/javascript/google-closure-for-beginners-components/</link>
		<comments>http://anton.shevchuk.name/javascript/google-closure-for-beginners-components/#comments</comments>
		<pubDate>Tue, 14 Jun 2011 10:06:19 +0000</pubDate>
		<dc:creator>Anton Shevchuk</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Closure]]></category>

		<guid isPermaLink="false">http://anton.shevchuk.name/?p=1789</guid>
		<description><![CDATA[Продолжим изучение Google Closure Library. В данной статье я постараюсь отклонится от &#171;jQuery way&#187; и продемонстрировать как удобны в использовании и просты в создании компоненты фреймворка. Давненько я технических статей не писал, отвыкли небось, но отвлекать не буду, читайте на здоровье ;) Это все же, в большей степени, перевод статьи из официальной документации, но он [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://anton.shevchuk.name/wp-content/uploads/2011/01/google-closure-logo.png" alt="" title="Google Closure" width="128" height="128" class="aligncenter size-full wp-image-1683" /></p>
<p>Продолжим изучение <a href="http://code.google.com/closure/library/">Google Closure Library</a>. В данной статье я постараюсь отклонится от &laquo;jQuery way&raquo; и продемонстрировать как удобны в использовании и просты в создании компоненты фреймворка.</p>
<blockquote><p>Давненько я технических статей не писал, отвыкли небось, но отвлекать не буду, читайте на здоровье ;)</p></blockquote>
<p><span id="more-1789"></span></p>
<blockquote><p>Это все же, в большей степени, перевод <a href="http://code.google.com/p/closure-library/wiki/IntroToComponents">статьи из официальной документации</a>, но он слегка сокращен и адаптирован</p></blockquote>
<p>Много вкусного несет в себе пакет goog.ui, я совсем чуть-чуть рассказывал о нем в <a href="http://anton.shevchuk.name/javascript/google-closure-for-beginners-with-examples/">предыдущей статье</a>, но напомню &mdash; этот пакет содержит в себе готовые компоненты для реализации интерфейсов: кнопки, меню, календари, и т.д.</p>
<p>Основой всех компонентов является класс goog.ui.Component, и для создания своих компонентов без него не обойтись. Он требует от нас определенную структуры класса состоящую из шести методов, которые описывают жизненный цикл компонента (в хронологическом порядке):</p>
<table class="inline">
<tr>
<th width="220px">Метод</th>
<th>Описание метода (жизненного цикла)</th>
</tr>
<tr>
<td>constructor</td>
<td>создание компонента</td>
</tr>
<tr>
<td>createDom()</td>
<td rowspan="2">создание DOM структуры компонента</td>
</tr>
<tr>
<td>decorateInternal() (optional)</td>
</tr>
<tr>
<td>enterDocument()</td>
<td>после создания DOM структуры будет запущен данный метод (обработчики событий вешаем тут)</td>
</tr>
<tr>
<td>exitDocument()</td>
<td>перед удалением DOM структуры (чистим обработчики событий)</td>
</tr>
<tr>
<td>dispose()</td>
<td>удаление компонента</td>
</tr>
</table>
<p>Отдельно особняком стоит метод <code>canDecorate(existingElement)</code> &mdash; он расскажет нам можно ли создать компонент основываясь на уже существующих элементах (existingElement). По умолчанию, суперкласс всегда возвращает true.</p>
<p>И так, перейдем к созданию своего компонента&#8230;</p>
<h2>Инициализация</h2>
<p>Начинается все с шага ноль &mdash; подключения <code>goog.ui.Component</code>, и объявления имени нашего компонента.</p>
<pre class="brush: jscript; title: ; notranslate">
goog.provide('tutorial.component');

goog.require('goog.ui.Component');
</pre>
<p>Теперь шаг первый &mdash; это создание конструктора компонента:</p>
<pre class="brush: jscript; title: ; notranslate">
tutorial.component = function(opt_label, opt_domHelper) {
    // вызов конструктора предка
    goog.ui.Component.call(this, opt_domHelper);

    /**
    * Заголовок виджета
    * @type String
    * @private
    */
    this.initialLabel_ = opt_label || 'Link';

    /**
    * Наш обработчик событий
    * @type goog.events.EventHandler
    * @private
    */
    this.eh_ = new goog.events.EventHandler(this);

    /**
    * Обработчик клавиатуры, у нас в компоненте не используется, но правила именования следует соблюдать
    *
    * @type {goog.events.KeyHandler?}
    * @private
    */
    this.kh_ = null;
};
// наследуем goog.ui.Component
goog.inherits(tutorial.component, goog.ui.Component);
</pre>
<h2>Создание DOM структуры</h2>
<p>После создания экземпляра компонента необходимо создать соответствующую DOM структуру, для этого потребуется реализовать метод <code>createDom()</code> и <code>decorateInternal()</code> (опционально):</p>
<table class="inline">
<tr>
<td>createDom()</td>
<td>создание DOM структуры компонента, и сохранение в <code>this.element_</code></td>
</tr>
<tr>
<td>decorateInternal() (optional)</td>
<td>создаем компонент используя уже существующую DOM структуру, и так же сохраняем в <code>this.element_</code></td>
</tr>
</table>
<p>Ну и пример кода:</p>
<pre class="brush: jscript; title: ; notranslate">

/**
 * создаём необходимую нам DOM структуру
 * в нашем примере необходим лишь один div
 */
tutorial.component.prototype.createDom = function() {
    // вызываем decorateInternal который внесет изменения в новосозданный элемент
    this.decorateInternal(this.dom_.createElement('div'));
};

/**
 * изменяем существующую структуру под наши нужды
 *
 * @param {HTMLElement} element DIV который мы изменяем
 */
tutorial.component.prototype.decorateInternal = function(element) {
    tutorial.component.superClass_.decorateInternal.call(this, element);

    var elem = this.getElement();
    goog.dom.classes.add(elem, 'tutorial-component');
    goog.dom.setTextContent(elem, this.initialLabel_);

};
</pre>
<h2>Еще один шаг &mdash; после того как построили DOM</h2>
<p>Зачастую, инициализация компонента требует уже заранее подготовленную структуру DOM. К примеру, вы создаете popup меню и вам необходимо повесить обработчик на div с id=&#8221;menu&#8221;, но этот элемент мы создаем лишь на этапе <code>createDom</code>, таким образом у нас получается неувязочка, но не стоит беспокоится, создатели фреймворка приберегли на этот случай метод <code>enterDocument()</code>:</p>
<table class="inline">
<tr>
<td>enterDocument()</td>
<td>в данном методе реализуют функционал, который требует наличие структуры компонента в DOM</td>
</tr>
</table>
<p>Реализуя метод <code>enterDocument()</code> необходимо всегда вызывать одноименный метод суперкласса, который вызовет все дочерние компоненты. Собственно, в методе <code>enterDocument()</code> лучше всего описывать методы, которые вешают обработчики событий на элементы DOM, и это тем более обязательно для элементов, которые не являются частью компонента (а лишь добавляют нам пачку зависимостей =).</p>
<p>Но вернемся к нашему примеру:</p>
<pre class="brush: jscript; title: ; notranslate">
tutorial.component.prototype.enterDocument = function() {
    // вызываем метод супер класса
    tutorial.component.superClass_.enterDocument.call(this);

    // простой обработчик на click
    this.eh_.listen(
        this.getElement(),
        goog.events.EventType.CLICK,
        this.onDivClicked_
    );
};
</pre>
<p>Резюмируя &mdash; не хочешь проблем с обработчиками &mdash; вешай их в enterDocument().</p>
<h2>Методы render() и decorate()</h2>
<p>Если мы все делали правильно, то можем попробовать инициализировать свой компонент используя метод <code>render()</code>:</p>
<pre class="brush: jscript; title: ; notranslate">
var comp1 = new tutorial.component(&quot;Button&quot;);
     comp1.render(goog.dom.$('button1'));
</pre>
<p>Метод <code>render()</code> вызывает <code>createDom()</code>, получившийся <code>element_</code> помещает в DOM и затем вызывает <code>enterDocument()</code>. Если метод <code>render()</code> дергать с параметром Element (в примере выше это &#8220;button1&#8243;), то <code>element_</code> будет помещен внутрь него, иначе станет потомком <code>body</code>.</p>
<p>В примере есть и второй способ вызова компонента:</p>
<pre class="brush: jscript; title: ; notranslate">
var comp2 = new tutorial.component(&quot;Button Two&quot;);
     comp2.decorate(goog.dom.$('button2'));
</pre>
<p>Метод <code>decorate()</code> получает уже готовый <code>element_</code> в качестве параметра, который и скармливает методу <code>decorateInternal()</code>, и затем точно так же вызывается <code>enterDocument()</code>. </p>
<blockquote><p>Внутри компонента вы не должны вызывать методы <code>render()</code> или <code>decorate()</code>. Хотя, если вы не используете методы <code>render()</code> или <code>decorate()</code>, следует вызывать <code>enterDocument()</code> после того как будут добавлены все необходимые элементы компонента в DOM</p></blockquote>
<h2>&laquo;Я тебя породил, я тебя и уничтожу&raquo;</h2>
<p>Создавая компонент не забИвайте описывать методы для подчистки DOM&#8217;а после его удаления, для этого следует подчистить все, что мы наворотили в методах <code>createDom()</code> и <code>enterDocument()</code>. В этом нам помогут следующие методы:</p>
<table class="inline">
<tr>
<td>exitDocument()</td>
<td>вызывается перед удалением DOM структуры &mdash; тут чистим обработчики событий</td>
</tr>
<tr>
<td>dispose()</td>
<td>удаление компонента и DOM элементов</td>
</tr>
</table>
<p>Ну и пример метода <code>exitDocument()</code>:</p>
<pre class="brush: jscript; title: ; notranslate">
tutorial.component.prototype.exitDocument = function() {
    tutorial.component.superClass_.exitDocument.call(this);

    this.eh_.unlisten(
        this.getElement(),
        goog.events.EventType.CLICK,
        this.onDivClicked_
    );

    this.eh_.unlisten(
        this.getElement(),
        [goog.events.EventType.MOUSEOVER, goog.events.EventType.MOUSEOUT],
        this.onDivHover_
    );
};
</pre>
<blockquote><p>Обратите внимание на вызов метода <code>exitDocument()</code> нашего суперкласса &mdash; данная махинация необходима для вызова методов <code>exitDocument()</code> у всех дочерних компонентов (если таковые имеются). Этот вызов обязателен!</p></blockquote>
<p>Теперь <code>dispose()</code>:</p>
<pre class="brush: jscript; title: ; notranslate">
tutorial.component.prototype.dispose = function() {
    if (!this.isDisposed()) {
        tutorial.component.superClass_.dispose.call(this);
        this.eh_.dispose();
        if (this.kh_) {
          this.kh_.dispose();
        }
    }
};
</pre>
<p>Опять же &mdash; дергаем супер класс, затем подчищаем обработчики событий.</p>
<h2>В заключение</h2>
<p>Как видите &mdash; в создании компонента нет ничего сложного, стоит лишь следовать простым правилам, которые диктует на фреймворк, и вы не запутаетесь и все будет работать.</p>
<p>И да, пощупать пример можно на страничке <a href="http://anton.shevchuk.name/wp-demo/closure-tutorials/component.html">closure-tutorials/component.html</a>.</p>
<h2>Читаем еще</h2>
<ul>
<li><a href="http://code.google.com/p/closure-library/wiki/IntroToComponents">Introduces goog.ui.Component</a> &mdash; первоисточник статьи</li>
<li><a href="http://anton.shevchuk.name/javascript/google-closure-for-beginners-with-examples/">Google Closure для начинающих</a></li>
</ul>
<h2>Offtopic</h2>
<p>Я тут еще в <a href="https://twitter.com/#!/AntonShevchuk">твиттер</a> на досуге пишу, так что следите и не пропустите ;)</p>
<h3  class="related_post_title">Другие посты на эту тему</h3><ul class="related_post"><li><a href="http://anton.shevchuk.name/javascript/google-closure-for-beginners-with-examples/" title="Google Closure руководство для начинающих">Google Closure руководство для начинающих</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://anton.shevchuk.name/javascript/google-closure-for-beginners-components/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Google Closure руководство для начинающих</title>
		<link>http://anton.shevchuk.name/javascript/google-closure-for-beginners-with-examples/</link>
		<comments>http://anton.shevchuk.name/javascript/google-closure-for-beginners-with-examples/#comments</comments>
		<pubDate>Tue, 18 Jan 2011 16:18:37 +0000</pubDate>
		<dc:creator>Anton Shevchuk</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Closure]]></category>
		<category><![CDATA[Google]]></category>

		<guid isPermaLink="false">http://anton.shevchuk.name/?p=1653</guid>
		<description><![CDATA[Мне как всегда не сидится на месте, в этот раз я взялся за Google Closure Library. В заголовке красуется &#171;для начинающих&#187;, но мы то знаем, если вы добрались до Closure, то ваш уровень уже далек от озвученного, хотя основы мы будем проходить по натоптанному пути. Подключение библиотеки Для подключения Closure нам понадобиться скачать библиотеку с [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://anton.shevchuk.name/wp-content/uploads/2011/01/google-closure-logo.png" alt="" title="Google Closure" width="128" height="128" class="aligncenter size-full wp-image-1683" /></p>
<p>Мне как всегда не сидится на месте, в этот раз я взялся за <a href="http://code.google.com/closure/library/docs/gettingstarted.html">Google Closure Library</a>.</p>
<blockquote><p>В заголовке красуется &laquo;для начинающих&raquo;, но мы то знаем, если вы добрались до Closure, то ваш уровень уже далек от озвученного, хотя основы мы будем проходить по натоптанному пути.</p></blockquote>
<p><span id="more-1653"></span></p>
<h2>Подключение библиотеки</h2>
<p>Для подключения Closure нам понадобиться скачать библиотеку с <a href="http://code.google.com/p/closure-library/downloads/list">домашней странички проекта</a> либо <a href="http://code.google.com/p/closure-library/source/checkout">используя SVN</a>, и подключить используя простой синтаксис:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;script type=&quot;text/javascript&quot; src=&quot;js/closure/goog/base.js&quot;&gt;&lt;/script&gt;
</pre>
<p>Насчет создания единого CDN хранилища для своей библиотеки Google ответил кратко:</p>
<blockquote><p>Closure Library differs from a lot of other JavaScript libraries in that it&#8217;s very broad and large and, in general, every class or namespace is stored in its own file, so just what is needed can be included in an app. As such, it&#8217;s infeasible to require users to pull all of Closure as they would another library in the AJAX APIs, as the download would be very large and split over a lot of individual requests. So we don&#8217;t have any immediate plans for AJAX API support.</p></blockquote>
<p>Хотя, если посмотреть внимательно на тот же Dojo Toolkit, то при похожей архитектуре у них есть возможность организации CDN из коробки.</p>
<h2>Подключение пакетов</h2>
<p>Closure состоит из отдельных пакетов, которые необходимо подключать по отдельности:</p>
<pre class="brush: jscript; title: ; notranslate">
goog.require('goog.dom');
goog.require('goog.events');
goog.require('goog.events.EventType');
</pre>
<p>Про расчет зависимостей между пакетами &mdash; не стоит беспокоиться, они позаботятся о себе самостоятельно (по крайней мере постараются, если им <a href="http://code.google.com/closure/library/docs/depswriter.html">прописаны зависимости</a>).</p>
<h2>Поиск DOM элементов</h2>
<p>Тут все просто и очень похоже на &#8230; обычный JavaScript, т.е. универсального врапера который нам предоставляет jQuery у нас нет, а есть пару пакетов:</p>
<pre class="brush: jscript; title: ; notranslate">
// подключаем DOM пакет
goog.require('goog.dom');
// еще один, который базируется на коде Dojo, и обеспечивает подмену QuerySelectorAll
goog.require('goog.dom.query');
</pre>
<p>Ну теперь непосредственно о выборках:</p>
<pre class="brush: jscript; title: ; notranslate">
// получаем элемент по ID
goog.dom.getElement('header')
// или
goog.dom.$('header')

// получаем первый элемент с определенным классом
goog.dom.getElementByClass('myclass')
// или все
goog.dom.getElementsByClass('myclass')
</pre>
<p>Очень часто придется использовать метод getElementsByTagNameAndClass, т.к. он достаточно универсален:</p>
<pre class="brush: jscript; title: ; notranslate">
// получаем все DOM элементы
goog.dom.getElementsByTagNameAndClass()

// получаем все &lt;p class=&quot;myclass&quot;&gt;
goog.dom.getElementsByTagNameAndClass('p','myclass')

// или используя алиас
goog.dom.$$('p','myclass')

// получаем все &lt;div&gt; элементы
goog.dom.$$('div')

// получаем все элементы с class=&quot;myclass&quot;
goog.dom.$$(null, 'myclass')

// получаем все &lt;div&gt; с классом &quot;myclass&quot; внутри элемента id=&quot;id&quot;
goog.dom.$$('div', 'myclass', goog.dom.$('id'))
</pre>
<p>Если вам и этого показалось мало, тут на помощь приходит <a href="https://developer.mozilla.org/En/DOM/Document.querySelectorAll">document.QuerySelectorAll</a>, ну а точнее его врапер, который основывается на коде Dojo Toolkit (кому нужны эти подробности?):</p>
<pre class="brush: jscript; title: ; notranslate">
// так работает
goog.dom.query('h1+h2, a:last-child')
</pre>
<p>Еще стоит упомянуть поиск элементов среди предков:</p>
<pre class="brush: jscript; title: ; notranslate">
// все предки
goog.dom.getAncestorByTagNameAndClass(goog.dom.$('id'))

// лишь &lt;div&gt; элементы
goog.dom.getAncestorByTagNameAndClass(goog.dom.$('id'), 'div')

// все предки с классом class=&quot;myclass&quot;
goog.dom.getAncestorByTagNameAndClass(goog.dom.$('id'), null, 'myclass')

// все &lt;div&gt; элементы с классом class=&quot;myclass&quot;
goog.dom.getAncestorByTagNameAndClass(goog.dom.$('id'), 'div', 'myclass')
</pre>
<h2>Манипуляции с DOM</h2>
<p>Кроме как взять готовое, можно создать DOM элемент ручками:</p>
<pre class="brush: jscript; title: ; notranslate">
// создаем элемент
var newHeader = goog.dom.createDom('h2', {'style':'color:red;', 'class':'title'}, 'Hello World!');
// или
var newHeader = goog.dom.$dom('h2', {'style':'color:red;', 'class':'title'}, 'Hello World!');
// добавляем к DOM
goog.dom.appendChild(document.body, newHeader);
</pre>
<h2>Изменяем классы и стили</h2>
<p>Ничего сложного, приведу лишь наглядные примеры, начну с работы с классами:</p>
<pre class="brush: jscript; title: ; notranslate">
// нам понадобится goog.dom.classes
goog.require('goog.dom.classes');

// устанавливаем имя класса(-ов) элементу
goog.dom.classes.set(goog.dom.$('id'), 'className') 

// получаем все классы элемента
goog.dom.classes.get(goog.dom.$('id'))

// добавляем класс или несколько
goog.dom.classes.add(goog.dom.$('id'), 'className', 'anotherClass') 

// удаляем классы или несколько
goog.dom.classes.remove(goog.dom.$('id'), 'className', 'anotherClass') 

// переключаем класс с одного на другой
goog.dom.classes.swap(goog.dom.$('id'), 'fromClass', 'toClass') 

// добавляем и удаляем классы одним махом
goog.dom.classes.addRemove(goog.dom.$('id'), 'addClass', ['removeClass']) 

// проверяем наличие класса
goog.dom.classes.has(goog.dom.$('id'), 'className') 

// добавляем/удаляем класс по флагу
goog.dom.classes.enable(goog.dom.$('id'), 'className', true||false) 

// &quot;мигаем&quot; классом - если есть удаляем, нет - добавляем
goog.dom.classes.toggle(goog.dom.$('id'), 'className')
</pre>
<p>Работа со стилями тоже не многим сложнее:</p>
<pre class="brush: jscript; title: ; notranslate">
// подключаем
goog.require('goog.style');

// получаем значение атрибута
goog.style.getStyle(goog.dom.$('id'), 'color') 

// устанавливаем значения
goog.style.setStyle(goog.dom.$('id'), 'color', '#ff0000')
// или
goog.style.setStyle(goog.dom.$('id'), { 'margin-top':'20px', 'margin-bottom':'20px' })
</pre>
<p>Поиграемся с размерами:</p>
<pre class="brush: jscript; title: ; notranslate">
// получение размеров
goog.style.getSize(goog.dom.$('id'))
// задание размеров
goog.style.setSize(goog.dom.$('id'), width, height)
// или
goog.style.setSize(goog.dom.$('id'), new goog.math.Size(width, height)) 

// задаем высоту
goog.style.setHeight(goog.dom.$('id'), height)
// задаем ширину
goog.style.setWidth(goog.dom.$('id'), width)
</pre>
<p>Расположение элементов: </p>
<pre class="brush: jscript; title: ; notranslate">
// указываем top и left
goog.style.setPosition(goog.dom.$('id'), left, top)
// или
goog.style.setPosition(goog.dom.$('id'), new goog.math.Coordinate(x, y)) 

// получаем offsetLeft и offsetTop свойства элементов
// нам вернется объект goog.math.Coordinate
goog.style.getPosition(goog.dom.$('id')) Opacity manipulation:
</pre>
<p>Прозрачность и видимость элементов:</p>
<pre class="brush: jscript; title: ; notranslate">
// устанавливаем значение opacity
goog.style.setOpacity(goog.dom.$('id'), 0.5)
// устанавливаем значение opacity
goog.style.getOpacity(goog.dom.$('id')) 

// прячем элемент
goog.style.setStyle(goog.dom.$('id'), &quot;display&quot;, &quot;none&quot;);
// или
goog.style.showElement(goog.dom.$('id'), false); 

// показываем
goog.style.setStyle(goog.dom.$('id'), &quot;display&quot;, &quot;&quot;);
// или
goog.style.showElement(goog.dom.$('id'), true);
</pre>
<h2>Работаем с событиями</h2>
<p>Если вы работали с событиями в JavaScript&#8217;е или в jQuery, то для вас тут будет все знакомо, пожалуй стоит упомянуть, что привычного по jQuery DOM ready в Closure нет:</p>
<blockquote><p>The short story is that we don&#8217;t want to wait for DOMContentReady (or worse the load event) since it leads to bad user experience. The UI is not responsive until all the DOM has been loaded from the network. So the preferred way is to use inline scripts as soon as possible. </p></blockquote>
<p>Хотя, есть возможность повесить обработчик, но его не принято использовать:</p>
<pre class="brush: jscript; title: ; notranslate">
goog.events.listen(window, goog.events.EventType.LOAD, function() {
    // тут код вашей функции
});
</pre>
<p>Это немного сбивает с толку, и вы даже можете встретить использование &lt;body load=&#8221;&#8230;&#8221;&gt;. В остальном, в Closure велосипед не изобрели и работа с событиями похожа на большинство существующих фреймворков, вот что нам необходимо сделать:</p>
<pre class="brush: jscript; title: ; notranslate">
// подключаем
goog.require('goog.events');
goog.require('goog.events.EventType');

/**
 * &quot;Вешаем&quot; свой обработчик используя метод goog.events.listen
 *
 * @param {EventTarget|goog.events.EventTarget} src
 * @param {string|Array.&lt;string&gt;} type
 * @param {Function|Object} listener
 * @param {boolean=} opt_capture
 * @param {Object=} opt_handler
 */
goog.events.listen(src, type, listener, opt_capture, opt_handler);

// пример
goog.events.listen(
    goog.dom.$('id'),
    goog.events.EventType.CLICK, // == &quot;click&quot;
    function(event){
         /*...*/
    }
);
</pre>
<p>Чуть-чуть пояснений:</p>
<ul>
<li><strong>src</strong> &mdash; источник события, обычно это некий DOM элемент, но по факту любой объект с методом dispatchEvent()</li>
<li><strong>type</strong> &mdash; имя события, или массив имен</li>
<li><strong>listener</strong> &mdash; наша функция-обработчик события</li>
<li><strong>opt_capture</strong> &mdash; необязательны булевый параметр, отвечает за то, в какой момент будет происходить перехват события &mdash; всплытия или погружения</li>
<li><strong>opt_handler</strong> &mdash; необязательный параметр, позволяет перебиндить this для нашей функции (так которая listener)</li>
</ul>
<p>Еще немного примеров:</p>
<pre class="brush: jscript; title: ; notranslate">
// если нам необходимо обработать событие лишь единожды
goog.events.listenOnce(goog.dom.$('id'), 'click', function(event){ /*...*/ } );

// добавляем обработчик
var key = goog.events.listen(goog.dom.$('id'), 'click', myFunction);
// и удаляем
goog.events.unlistenByKey(key); 

// добавляем
goog.events.listen(goog.dom.$('id'), 'click', myFunction, true);
// и удаляем
goog.events.unlisten(goog.dom.$('id'), 'click', myFunction, true);

// удаляем все обработчики
goog.events.removeAll(goog.dom.$('id'));
// со всего
goog.events.removeAll();
</pre>
<p>Ух, много текста, но вы не отчаивайтесь, сохраните лучше <a href="http://anton.shevchuk.name/wp-demo/closure-tutorials/cheatsheet.html">шпаргалку</a> в избранном ;)</p>
<h2>Разрабатываем пакет</h2>
<p>Когда решите, что в вашем проекте требуется нечто большее, чем всплывающие подсказки, то значит вы созрели для написания своих пакетов, и да это тоже не сложно. Стоит начать приблизительно со следующего кода:</p>
<pre class="brush: jscript; title: ; notranslate">
// это имя нашего пакета
goog.provide('tutorial.simple');

// наши зависимости
goog.require('goog.dom');
goog.require('goog.style');

/**
 * constructor и этим все сказано
 */
tutorial.simple = function() {
    /* code here */
};
</pre>
<p>Теперь стоит немного расширить функционал, надо бы получить какой-то элемент и провести с ним какие-нибудь махинации:</p>
<pre class="brush: jscript; title: ; notranslate">
/**
 * @param {Element} el
 */
tutorial.simple = function(el) {
    this.el = el;
};

/**
 * @type {Element}
 */
tutorial.simple.prototype.el = null;

/**
 * меняем цвет текста элемента на красный
 */
tutorial.simple.prototype.red = function(color) {
    goog.style.setStyle(this.el, {&quot;color&quot;:color});
};
</pre>
<p>Теперь как работать с этим кодом-то:</p>
<pre class="brush: jscript; title: ; notranslate">
&lt;script type=&quot;text/javascript&quot; src=&quot;js/closure/goog/base.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;js/simple.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
var simple = tutorial.simple(document.getElementById(&quot;header&quot;))
     simple.red();
&lt;/script&gt;
</pre>
<blockquote><p>Если у вас прописаны зависимости, то вторая строчка преобразуется в лаконичную goog.require(&#8216;tutorial.simple&#8217;);</p></blockquote>
<h2>AJAX</h2>
<p>Как же это, писать статью о JavaScript фреймворке и не упомянуть работу с AJAX &mdash; конечно же тут есть свой врапер, работа с ним дело не хитрое и каждому по плечу, вот <a href="http://anton.shevchuk.name/wp-demo/closure-tutorials/ajax.html">пример</a>:</p>
<pre class="brush: jscript; title: ; notranslate">
// потребуется
goog.require('goog.net.XhrIo');

// ну и пример загрузки контента
// воспользуемся статическим методом goog.net.XhrIo.send
// первым параметром идет URL, затем callback
// имя метода и данные передаваемые на сервер
goog.net.XhrIo.send('ajax/response.json', function(ev){
    // берем JSON
    var data = ev.target.getResponseJson();

    // находим некий контейнер на страничке
    var container = goog.dom.$('articles');

    // идем в цикле по статьям из ответа сервера
    goog.array.forEach(data.articles, function(article){
          // создаем элементы
          var title = goog.dom.$dom('h2', {'innerHTML':article.title});
          var content = goog.dom.$dom('p', {'innerHTML':article.content});

          // добавляем их в контейнер
          goog.dom.appendChild(container, title);
          goog.dom.appendChild(container, content);
    })
}, 'POST', 'give=data&amp;or=lost');
</pre>
<blockquote><p>Хватит наверное уже вступительной информации, пора переходить к примерам&#8230; Хотя вся приведенная информация удобно представлена в виде шпаргалки</p></blockquote>
<h2>Выдвижная панель</h2>
<p>Простой пример, для начала &mdash; слайд-панель, она у нас будет двигаться вверх/вниз по клику на ссылке (см. <a href="http://anton.shevchuk.name/wp-demo/closure-tutorials/slide-panel.html">пример</a>):</p>
<p><img src="http://anton.shevchuk.name/wp-content/uploads/2011/01/slidepanel.png" alt="" title="Slide Panel" width="657" height="195" class="aligncenter size-full wp-image-1693" /></p>
<p>Для реализации данного функционала нам понадобится пакет &#8220;<a href="http://closure-library.googlecode.com/svn/docs/class_goog_ui_AnimatedZippy.html">AnimatedZippy</a>&#8220;:</p>
<pre class="brush: jscript; title: ; notranslate">
goog.require('goog.dom');

/* zippy */
goog.require('goog.ui.AnimatedZippy');
goog.require('goog.ui.Zippy');
goog.require('goog.ui.ZippyEvent');
</pre>
<p>После создания DOM элементов, т.е. внизу HTML страницы пишем код, который должен состоять из одной строки, если бы не мои комментарии:</p>
<pre class="brush: jscript; title: ; notranslate">
new goog.ui.AnimatedZippy(
    'button',  // id элемента/элемент который отвечает за управление
    'panel',   // id элемента/элемент который будет открываться/закрываться
    false      // состояние по умолчанию - false - закрыт
);
</pre>
<p>Для того, чтобы у нас был индикатор со стрелочкой, необходимо описать следующий CSS (за применение данных стилей отвечает сама пакет):</p>
<pre class="brush: css; title: ; notranslate">
/* открыто */
.goog-zippy-expanded {
  background: url(images/white-arrow.gif) no-repeat right 12px;
}
/* закрыто */
.goog-zippy-collapsed {
  background: url(images/white-arrow.gif) no-repeat right -50px;
}
</pre>
<h2>Магические исчезновения</h2>
<p>Еще один незамысловатый пример код &mdash; пришел, увидел, <a href="http://anton.shevchuk.name/wp-demo/closure-tutorials/disappear.html">скрыл с глаз долой</a>:</p>
<p><img src="http://anton.shevchuk.name/wp-content/uploads/2011/01/disappear.png" alt="" title="Disappear" width="482" height="201" class="aligncenter size-full wp-image-1692" /></p>
<p>Логика элементарна &mdash; при клике не картинке с class=&#8221;delete&#8221; находим родительский элемент и скрываем его:</p>
<pre class="brush: jscript; title: ; notranslate">
// инициализация
goog.require('goog.dom');
goog.require('goog.array');  // не обязательно, но стоит знать о данном пакете, и уметь использовать
goog.require('goog.events');
goog.require('goog.events.EventType');
goog.require('goog.fx');
goog.require('goog.fx.dom');

/* ... */
goog.array.forEach(goog.dom.getElementsByClass('delete'), function(el) {
    // обработчик событий
    goog.events.listen(
        el,   /* DOM ELement*/
        goog.events.EventType.CLICK,    /* событие, либо константа, либо просто строкой &quot;click&quot; */
        function(ev) {                   /* функция, будет вызвана по событию */
            var anim = new goog.fx.dom.FadeOutAndHide(el.parentNode, 400);
                anim.play();
            ev.preventDefault(); // отменяем событие по умолчанию
        });
});
</pre>
<h2>Анимация</h2>
<p>Более сложный пример, будет засталять квадрат двигаться, изменять размер и прозрачность (см. <a href="http://anton.shevchuk.name/wp-demo/closure-tutorials/animation.html">пример</a>):</p>
<p><img src="http://anton.shevchuk.name/wp-content/uploads/2011/01/animation.png" alt="" title="Animation" width="484" height="177" class="aligncenter size-full wp-image-1695" /></p>
<p>Для этого нам понадобятся следующие пакеты:</p>
<pre class="brush: jscript; title: ; notranslate">
goog.require('goog.fx');
goog.require('goog.fx.dom');
/* реализация очередей для анимации */
goog.require('goog.fx.AnimationQueue');
goog.require('goog.fx.AnimationSerialQueue');
goog.require('goog.fx.AnimationParallelQueue');
</pre>
<p>Теперь непосредственно код, который будет отвечать за все превращения и перемещения:</p>
<pre class="brush: jscript; title: ; notranslate">
// функция, которая вызывает при клике на ссылку &quot;Run&quot;
function run() {
    // берем наш box
    var elem = goog.dom.getElement(&quot;box&quot;);

    // создаем очередь анимации
    var queue = new goog.fx.AnimationSerialQueue();
        // изменение прозрачности за 1.2 секунды с 1 до 0.1
        queue.add(new goog.fx.dom.Fade(elem, 1, 0.1, 1200));
        // перемещаем элемент из текущего положение до координат 400,100 за 2 секунды
        queue.add(new goog.fx.dom.SlideFrom(elem,
                [400, 100], 2000,
                goog.fx.easing.easeOut
              ));
        // анимация из функции animationInner, листинг ниже
        queue.add(animationInner(elem));
        // анимация из функции animationInner2, листинг ниже
        queue.add(animationInner2(elem));
        // опять перемещение, до координат 100,100
        queue.add(new goog.fx.dom.SlideFrom(elem,
                [100, 100], 500,
                goog.fx.easing.easeOut
              ));
        // запускаем анимацию
        queue.play();
}

function animationInner(elem){
    // создаем очередь анимаций, которые будут происходить параллельно
    var innerAnim = new goog.fx.AnimationParallelQueue();
        // прозрасность
        innerAnim.add(new goog.fx.dom.Fade(elem, 0.1, 0.5, 2000));
        // перемещение
        innerAnim.add(new goog.fx.dom.SlideFrom(elem,
                [400, 300], 2000,
                goog.fx.easing.easeOut
              ));
        // изменение размеров с 100х100 до 20х20
        innerAnim.add(new goog.fx.dom.Resize(elem,
                [100, 100], [20, 20], 2000,
                goog.fx.easing.easeOut
              ));
    // возвращаем анимацию, дабы поставить ее в другую очередь
    return innerAnim;
}

function animationInner2(elem){
    var innerAnim = new goog.fx.AnimationParallelQueue();
        // прозрачность
        innerAnim.add(new goog.fx.dom.FadeIn(elem, 1200));
        // перемещение
        innerAnim.add(new goog.fx.dom.SlideFrom(elem,
                [100, 300], 1200,
                goog.fx.easing.easeOut
              ));
        // изменение размеров
        innerAnim.add(new goog.fx.dom.Resize(elem,
                [20, 20], [100,100], 1200,
                goog.fx.easing.easeOut
              ));
    // возвращаем анимацию, дабы поставить ее в другую очередь
    return innerAnim;
}
</pre>
<p>Вот такой код, как мы можем заметить, кода поболее чем <a href="http://anton.shevchuk.name/wp-demo/jquery-tutorials/chainable-effects.html">при использовании jQuery</a>.</p>
<h2>Гармошка</h2>
<p><img src="http://anton.shevchuk.name/wp-content/uploads/2011/01/accordion.png" alt="" title="Accordion" width="580" height="248" class="aligncenter size-full wp-image-1698" /></p>
<p>Знаю по jQuery, пример создания &laquo;гармошки&raquo; из элементов пользуется популярностью, в данном руководстве я тоже не смог обойти его стороной, благо это <a href="http://anton.shevchuk.name/wp-demo/closure-tutorials/accordion.html">не так трудно</a>, нам необходимо: при клике на заголовке открыть последующий параграф; при повторном клике &mdash; скрыть; одновременно открытым должен быть лишь один элемент.</p>
<p>Для реализации, нам потребуются следующие пакеты:</p>
<pre class="brush: jscript; title: ; notranslate">
goog.require('goog.ui.AnimatedZippy');
goog.require('goog.ui.Zippy');
goog.require('goog.ui.ZippyEvent');
</pre>
<p>Ну и сама реализация с комментариями:</p>
<pre class="brush: jscript; title: ; notranslate">
var panels = goog.dom.$$('h3');
var anims  = {};
var open   = null;

// для каждой панельки (в данном случае это теги H3)
goog.array.forEach(panels, function(pane){
    // создаем анимацию, знакомую нам по примеру с панелькой
    var animation = new goog.ui.AnimatedZippy(pane, goog.dom.getNextElementSibling(pane));

    // создаем обработчик события и вешаем его на событие анимации
    // листинг функции чуть ниже
    goog.events.listen(animation, goog.ui.Zippy.Events.TOGGLE, zippyToggle);

    // сохраняем идентификатор анимации - он нам еще понадобится
    anims[goog.getUid(animation)] = animation;
});

function zippyToggle(event) {
    // получаем идентификатор &quot;цели&quot;
    var uid = goog.getUid(event.target);

    // реализуем логику скрытия других панелей
    if (event.expanded) {
        if (open) {
            anims[open].setExpanded(false);
        }
        open = uid;
    }
}
</pre>
<h2>Гармошка, вариант 2</h2>
<p>Небольшое изменение предыдущего варианта &mdash; сделаем открытие/закрытие одновременным, и <a href="http://anton.shevchuk.name/wp-demo/closure-tutorials/accordion2.html">откроем панельку</a> по умолчанию, для этого потребуется не так много изменений в коде:</p>
<pre class="brush: jscript; title: ; notranslate">
// открытый элемент (отсчет с 0, если кто забыл)
var def    = 2;

goog.array.forEach(panels, function(pane, ind /* добавляем порядковый номер в цикл */){
    // добавляем проверку на совпадение ind и def
    var animation = new goog.ui.AnimatedZippy(pane, goog.dom.getNextElementSibling(pane), (def==ind));
    // отслеживаем другое событие
    goog.events.listen(animation, goog.fx.Animation.EventType.BEGIN, zippyToggle);
    anims[goog.getUid(animation)] = animation;
});
</pre>
<h2>Анимация для события hover</h2>
<p>Данный <a href="http://anton.shevchuk.name/wp-demo/closure-tutorials/animation-hover1.html">пример</a> реализует анимацию для события hover &mdash; в меру симпатично и функционально:</p>
<p><img src="http://anton.shevchuk.name/wp-content/uploads/2011/01/hover.png" alt="" title="Hover" width="528" height="418" class="aligncenter size-full wp-image-1700" /></p>
<p>Для реализации потребуется:</p>
<pre class="brush: jscript; title: ; notranslate">
// DOM
goog.require('goog.dom');
goog.require('goog.dom.query');
// forEach
goog.require('goog.array');
// события
goog.require('goog.events');
goog.require('goog.events.EventType');
// анимация
goog.require('goog.fx');
goog.require('goog.fx.dom');
goog.require('goog.fx.AnimationQueue');
goog.require('goog.fx.AnimationSerialQueue');
goog.require('goog.fx.AnimationParallelQueue');
</pre>
<p>Код реализации:</p>
<pre class="brush: jscript; title: ; notranslate">
// для каждой ссылки в элементе с class=menu
goog.array.forEach(goog.dom.query('.menu a'), function(el){
   // скрыть следующий элемент в доме
   goog.dom.getNextElementSibling(el).style.display = 'none';

   // вешаем обработчик на событие MOUSEOVER
   goog.events.listen(el, goog.events.EventType.MOUSEOVER, function(ev){
        // находим следующий элемент - это &lt;em&gt;
        var em = goog.dom.getNextElementSibling(this);
        // создаем очередь анимации
        var anim = new goog.fx.AnimationParallelQueue();
            anim.add(new goog.fx.dom.FadeInAndShow(em, 1500));
            anim.add(new goog.fx.dom.Slide(em,
                    [-15, -85], [-15, -75], 1500,
                    goog.fx.easing.easeOut
                  ));
        // запускаем
        anim.play();
    }, false,  el);

   // вешаем обработчик на событие MOUSEOUT
    goog.events.listen(el, goog.events.EventType.MOUSEOUT, function(ev){
        // находим следующий элемент - это &lt;em&gt;
        var em = goog.dom.getNextElementSibling(this);
        // создаем очередь анимации
        var anim = new goog.fx.AnimationParallelQueue();
            anim.add(new goog.fx.dom.FadeOutAndHide(em, 500));
            anim.add(new goog.fx.dom.Slide(em,
                    [-15, -75], [-15, -85], 500,
                    goog.fx.easing.easeOut
                  ));
        // запускаем
        anim.play();
    }, false,  el);
})
</pre>
<h2>Анимация для события hover. Вариант 2</h2>
<p>Наличие лишнего тега &lt;em&gt; лишь для подсказки меня не радует, куда как лаконичней использовать атрибут title тега &lt;a&gt;:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;ul class=&quot;menu&quot;&gt;
    &lt;li&gt;
        &lt;a href=&quot;http://anton.shevchuk.name&quot; title=&quot;Personal Blog&quot;&gt;Anton Shevchuk&lt;/a&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;a href=&quot;http://hohli.com&quot; title=&quot;My open source projects&quot;&gt;My Projects&lt;/a&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;a href=&quot;http://photo.hohli.com&quot; title=&quot;Full size photos under CC&quot;&gt;Photoblog&lt;/a&gt;
    &lt;/li&gt;
&lt;/ul&gt;
</pre>
<p>Теперь посредством Javascript&#8217;a создадим элемент &lt;em&gt;, и добавим в существующий код:</p>
<pre class="brush: jscript; title: ; notranslate">
goog.array.forEach(goog.dom.query('.menu a'), function(el){
    goog.dom.append(el.parentNode, goog.dom.$dom('em', {'style':'display:none;', 'innerHTML':el.title}));

    /* код из предыдущего листинга */

});
</pre>
<p><a href="http://anton.shevchuk.name/wp-demo/closure-tutorials/animation-hover2.html">Результат</a> не отличим визуально от предыдущего.</p>
<h2>Кликабельные блоки</h2>
<p>Теперь решим следующую задачу, у нас есть блок текста, внутри которого есть ссылка, при клике на ссылку должен осуществляться переход по ссылке:</p>
<p><img src="http://anton.shevchuk.name/wp-content/uploads/2011/01/clickable.png" alt="" title="Сlickable" width="550" height="320" class="aligncenter size-full wp-image-1702" /></p>
<p>Подключаем пакеты:</p>
<pre class="brush: jscript; title: ; notranslate">
goog.require('goog.dom');
goog.require('goog.array');
goog.require('goog.events');
goog.require('goog.events.EventType');
</pre>
<p>Создаем обработчик события для каждого &lt;li&gt;:</p>
<pre class="brush: jscript; title: ; notranslate">
goog.array.forEach(goog.dom.$$('li'), function(el){
    goog.events.listen(el, goog.events.EventType.CLICK, function(ev){
        // находим первую ссылку в контексте нашего &lt;li&gt;
        var link = goog.dom.$$('a', null, this)[0];
        // переходим по ссылке
        window.location = link.href;
        ev.preventDefault();
    }, false,  el);
});
</pre>
<p>Смотрим на <a href="http://anton.shevchuk.name/wp-demo/closure-tutorials/block-clickable.html">пример в живую</a>.</p>
<h2>Складывающиеся панельки</h2>
<p>Данный <a href="http://anton.shevchuk.name/wp-demo/closure-tutorials/collapsible-panels.html">пример</a> чем-то схож с &laquo;гармошкой&raquo;, но добавлено немного полезны фич:</p>
<p><img src="http://anton.shevchuk.name/wp-content/uploads/2011/01/collapsed-panels.png" alt="" title="collapsed-panels" width="505" height="280" class="aligncenter size-full wp-image-1708" /></p>
<p>Задача требует от нас следующих действий:</p>
<ul>
<li>скрываем все элементы &lt;div class=message_body&gt; после первого</li>
<li>скрываем все элементы &lt;li&gt; после пятого</li>
<li>клик по &lt;p class=message_head&gt; &mdash; вызывает изменение отображение (показываем/прячем) для следующего элемента &lt;div class=message_body&gt;</li>
<li>клик по &lt;a class=collpase_all_message&gt; &mdash; скрывает все &lt;div class=message_body&gt;</li>
<li>клик по &lt;a class=show_all_message&gt; &mdash; скрывает элемент, и отображает &lt;a class=show_recent_only&gt;, так же скрывает все элементы &lt;li&gt; после пятого</li>
<li>клик по &lt;a class=show_recent_only&gt; &mdash; скрывает элемент, и отображает &lt;a class=show_all_message&gt;, так же отображает все &lt;li&gt; после пятого</li>
</ul>
<p>Нам потребуются:</p>
<pre class="brush: jscript; title: ; notranslate">
goog.require('goog.dom');
goog.require('goog.dom.query');
goog.require('goog.array');

// события
goog.require('goog.events');
goog.require('goog.events.EventType');

// работа со стилями
goog.require('goog.style');

// анимация Zippy
goog.require('goog.ui.AnimatedZippy');
goog.require('goog.ui.Zippy');
goog.require('goog.ui.ZippyEvent');
</pre>
<p>Ну и листинг кода с комментариями:</p>
<pre class="brush: jscript; title: ; notranslate">
// функция, которая скрывает/отображает элементы после 5-го
function showMore(flag) {
    // тут используется селектор из CSS3, лучше понимать что он делает ;)
    goog.array.forEach(goog.dom.query('#messages li:nth-child(n+6)'), function(el){
        // скрываем/отображаем элемент
        el.style.display = (flag?'':'none');
    });

    // заменяем кнопочку управления
    goog.style.showElement(goog.dom.$('show_recent_only'), flag);
    goog.style.showElement(goog.dom.$('show_all_message'), !flag);

}
// скроем элементы
showMore(false);

// покажем кнопку id=show_recent_only
goog.style.showElement(goog.dom.$('show_recent_only'), false);

// инициализируем для каждого сообщения AnimatedZippy
var zippy = [];
goog.array.forEach(goog.dom.$$('h4',null,goog.dom.$('messages')), function(pane, index){
    zippy.push(new goog.ui.AnimatedZippy(pane, goog.dom.getNextElementSibling(pane), (index==0)));
});

// обработчик события click для кнопки скрытия текста сообщений
goog.events.listen(goog.dom.$('collpase_all_message'), 'click', function(ev){
    // методом перебора всех элементов из массива zippy
    goog.array.forEach(zippy, function(anim){
        // собственно - складывание
        anim.setExpanded(false);
    });
    ev.preventDefault();
});

// обработчик для кнопки отображения всех сообщений (т.е. всех после 5-го)
goog.events.listen(goog.dom.$('show_all_message'), 'click', function(ev){
    showMore(true);
    ev.preventDefault();
});

// обработчик для кнопки скрытия всех сообщений кроме первых пяти
goog.events.listen(goog.dom.$('show_recent_only'), 'click', function(ev){
    showMore(false);
    ev.preventDefault();
});
</pre>
<h2>Управление комментариями (аля WordPress)</h2>
<p>Еще один пример &mdash; <a href="http://anton.shevchuk.name/wp-demo/closure-tutorials/wordpress-comments.html">управление комментариями</a> в стиле wordpress:</p>
<p><img src="http://anton.shevchuk.name/wp-content/uploads/2011/01/wordpress.png" alt="" title="Wordpress Comments" width="493" height="346" class="aligncenter size-full wp-image-1709" /></p>
<p>Для этого потребуется:</p>
<pre class="brush: jscript; title: ; notranslate">
goog.require('goog.dom');
goog.require('goog.dom.query');
goog.require('goog.dom.classes');
goog.require('goog.array');
goog.require('goog.fx');
goog.require('goog.fx.dom');
goog.require('goog.events');
goog.require('goog.events.EventType');
</pre>
<p>Ну и листинг кода:</p>
<pre class="brush: jscript; title: ; notranslate">
// рисуем &quot;зебру&quot;, каждому 2-му элементу добавляем класс &quot;alt&quot;
goog.array.forEach(goog.dom.query('.pane:nth-child(2n)'), function(el) {
    goog.dom.classes.add(el, 'alt');
});

// функция, которая создает обработчик для кнопки unapprove
function unapprove(el) {
    // вешаем обработчик на click
    goog.events.listen(el, goog.events.EventType.CLICK, function(event){
        // получаем родителя элемента, которые &lt;div class=&quot;pane&quot;&gt;
        var pane = goog.dom.getAncestorByTagNameAndClass(event.target, 'div', 'pane');

        // изменяем цвет элемента
        goog.fx.dom.bgColorFadeIn(pane, [255, 240, 120], 2000);
        // добавляем класс
        goog.dom.classes.add(pane, 'spam');

        event.preventDefault();

        // создаем кнопку approve и вешаем на нее соответствующий обработчик
        var button = goog.dom.createDom('a', {'href':'#','class':'btn-approve','innerHTML':'Approve'});
        // обработчик
        approve(button);

        // заменяем кнопку unapprove на созданную approve
        goog.dom.replaceNode(button, event.target);
    });
}

// функция, которая создает обработчик для кнопки approve
function approve(el) {
    // вешаем обработчик на click
    goog.events.listen(el, goog.events.EventType.CLICK, function(event){
        // получаем родителя элемента, которые &lt;div class=&quot;pane&quot;&gt;
        var pane = goog.dom.getAncestorByTagNameAndClass(event.target, 'div', 'pane');

        // изменяем цвет элемента
        goog.fx.dom.bgColorFadeIn(pane, [200, 240, 180], 2000);
        // добавляем класс
        goog.dom.classes.remove(pane, 'spam');

        event.preventDefault();

        // создаем кнопку unapprove и вешаем на нее соответствующий обработчик
        var button = goog.dom.createDom('a', {'href':'#','class':'btn-unapprove','innerHTML':'Unapprove'});
        // обработчик
        unapprove(button);

        // заменяем кнопку approve на созданную unapprove
        goog.dom.replaceNode(button, event.target);
    });
}

goog.array.forEach(goog.dom.query('.pane .btn-unapprove'), unapprove);
goog.array.forEach(goog.dom.query('.pane .btn-approve'), approve);

// обработчик для кнопок spam
goog.array.forEach(goog.dom.query('.pane .btn-spam'), function(el) {
    goog.events.listen(el, goog.events.EventType.CLICK, function(event){
        // получаем родителя элемента, которые &lt;div class=&quot;pane&quot;&gt;
        var pane = goog.dom.getAncestorByTagNameAndClass(event.target, 'div', 'pane');

        // анимация фона
        goog.fx.dom.bgColorFadeIn(pane, [250, 200, 200], 2000);
        // прячем
        new goog.fx.dom.FadeOutAndHide(pane, 500).play();
        event.preventDefault();

    });
});
// обработчик для кнопок delete
goog.array.forEach(goog.dom.query('.pane .btn-delete'), function(el) {
    goog.events.listen(el, goog.events.EventType.CLICK, function(event){
        alert(&quot;This comment will be deleted!&quot;);

        // получаем родителя элемента, которые &lt;div class=&quot;pane&quot;&gt;
        var pane = goog.dom.getAncestorByTagNameAndClass(event.target, 'div', 'pane');

        // анимация фона
        goog.fx.dom.bgColorFadeIn(pane, [250, 200, 200], 2000);
        // прячем
        new goog.fx.dom.FadeOutAndHide(pane, 500).play();
        event.preventDefault();
    });
});
</pre>
<h2>Галерея изображений</h2>
<p>Простейший пример реализации галереи, без перезагрузки страницы. (см. <a href="http://anton.shevchuk.name/wp-demo/closure-tutorials/img-replacement.html">пример</a>):</p>
<p><img src="http://anton.shevchuk.name/wp-content/uploads/2011/01/gallery.jpg" alt="" title="Gallery" width="716" height="604" class="aligncenter size-full wp-image-1710" /></p>
<p>Листинг с комментариями:</p>
<pre class="brush: jscript; title: ; notranslate">
// потребуется
goog.require('goog.dom');
goog.require('goog.dom.query');
goog.require('goog.array');
goog.require('goog.style');
goog.require('goog.events');

// логика
// для каждой ссылки внутри class=thumbs
goog.array.forEach(goog.dom.query('.thumbs a'), function(el) {
    // добавляем обработчик для события clikc
    goog.events.listen(el, 'click', function(ev){
        // событие &quot;по умолчанию&quot; нам не нужно
        ev.preventDefault();
        // заменяем атрибуты &quot;большой&quot; картинки на данные из тега &lt;a&gt;
        goog.dom.setProperties(goog.dom.$(&quot;largeImg&quot;), { src: this.href, alt: this.title });
        // так же заменяем текст тега &lt;em&gt; в заголовке
        goog.dom.query('h2 em')[0].innerHTML = &quot;(&quot;+this.title+&quot;)&quot;;
    }, false, el);
});
</pre>
<h2>Стилизируем ссылки</h2>
<p>Данный <a href="http://anton.shevchuk.name/wp-demo/closure-tutorials/link-types.html">пример</a> хорошо иллюстрирует работу с CSS селекторами, в данном случае &mdash; необходимо изменить класс ссылок в тексте в зависимости от того куда она ведет:<br />
<img src="http://anton.shevchuk.name/wp-content/uploads/2011/01/links.png" alt="" title="Links" width="512" height="166" class="aligncenter size-full wp-image-1711" /></p>
<p>Для реализации даной задачи нам потребуются:</p>
<pre class="brush: jscript; title: ; notranslate">
goog.require('goog.dom');
goog.require('goog.dom.query');
goog.require('goog.array'); // да я просто ленивый, мне влом писать for
goog.require('goog.style');
</pre>
<p>И следующий код:</p>
<pre class="brush: jscript; title: ; notranslate">
// находим все ссылки в которых href заканчивается на pdf, и добавляем соответствующий класс
goog.array.forEach(goog.dom.query('a[href$=pdf]'), function(el){
    goog.dom.classes.add(el, 'pdf')
});

// находим все ссылки в которых href заканчивается на zip, и добавляем соответствующий класс
goog.array.forEach(goog.dom.query('a[href$=zip]'), function(el){
    goog.dom.classes.add(el, 'zip')
});

// находим все ссылки в которых href заканчивается на png, и добавляем соответствующий класс
goog.array.forEach(goog.dom.query('a[href$=png]'), function(el){
    goog.dom.classes.add(el, 'png')
});

// находим все ссылки которые ведут на внешние ресурсы
goog.array.forEach(goog.dom.query('a:not([href^=http://anton.shevchuk.name]):not([href^=#])'), function(el){
    // добавляем класс external
    goog.dom.classes.add(el, 'external');
    // изменяем target на _blank
    goog.dom.setProperties(el, { target: &quot;_blank&quot; });
});
</pre>
<h2>Использование UI</h2>
<p>В Closure так же есть множество пакетов для быстрой реализации пользовательских интерфейсов, собственно приведу небольшой список из всего <a href="http://closure-library.googlecode.com/svn/docs/namespace_goog_ui.html">обилия</a>:</p>
<ul>
<li><a href="http://closure-library.googlecode.com/svn/trunk/closure/goog/demos/css3button.html">CSS 3 Buttons</a></li>
<li><a href="http://closure-library.googlecode.com/svn/trunk/closure/goog/demos/checkbox.html">Checkbox</a></li>
<li><a href="http://closure-library.googlecode.com/svn/trunk/closure/goog/demos/combobox.html">Combobox</a></li>
<li><a href="http://closure-library.googlecode.com/svn/trunk/closure/goog/demos/dimensionpicker.html">Dimension picker</a></li>
<li><a href="http://closure-library.googlecode.com/svn/trunk/closure/goog/demos/css3menubutton.html">Menu Buttons</a></li>
<li><a href="http://closure-library.googlecode.com/svn/trunk/closure/goog/demos/select.html">Select</a></li>
<li><a href="http://closure-library.googlecode.com/svn/trunk/closure/goog/demos/tabbar.html">Tab Bar</a></li>
<li><a href="http://closure-library.googlecode.com/svn/trunk/closure/goog/demos/editor/editor.html">WYSIWYG Editor</a></li>
<li><a href="http://closure-library.googlecode.com/svn/trunk/closure/goog/demos/progressbar.html">Progress bar</a></li>
<li><a href="http://closure-library.googlecode.com/svn/trunk/closure/goog/demos/slider.html">Slider</a></li>
<li><a href="http://closure-library.googlecode.com/svn/trunk/closure/goog/demos/datepicker.html">Datepicker</a></li>
</ul>
<p>Все UI пакеты имеют два метода render() и decorate() &mdash; первый создаёт все необходимые DOM элементы в указанном контейнере, второй же использует заранее созданную структуру и пытается с ней работать. Хотя на этом остановлюсь, если получиться &mdash; то о UI я расскажу в рамках следующей статьи.</p>
<h2>P.S.</h2>
<p>Обычно, после подобных статей, у меня спрашивают &mdash; &laquo;А лучше ли этот фреймворк, чем jQuery?&raquo;. И я отвечаю &mdash; это разного уровня фреймворки, и предназначены для решения различных задач, если вы успешно справляетесь с использованием jQuery и плагинов, то не стоит сильно заморачиваться, ведь всё и так работает. Если же говорить о Dojo Toolkit &mdash; это как двоюродный брат, много общих черт, но они таки разные. И да, те кто говорит, что по Dojo мало документации, то вы наверное не читали документацию по Closure ;)</p>
<h3  class="related_post_title">Другие посты на эту тему</h3><ul class="related_post"><li><a href="http://anton.shevchuk.name/javascript/google-closure-for-beginners-components/" title="Google Closure для начинающих. Компоненты">Google Closure для начинающих. Компоненты</a></li><li><a href="http://anton.shevchuk.name/javascript/update-charts-hohli-com/" title="Обновление сервиса Charts.HoHli.com">Обновление сервиса Charts.HoHli.com</a></li><li><a href="http://anton.shevchuk.name/google/the-official-google-blog/" title="The Official Google Blog">The Official Google Blog</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-and-google-search-api-or-yahoo-search-api-yahoo-search-api/" title="jQuery + AJAX + (Google Search API || Yahoo Search API)">jQuery + AJAX + (Google Search API || Yahoo Search API)</a></li><li><a href="http://anton.shevchuk.name/internet/analyser/" title="Analyser &#8211; специализированный поиск">Analyser &#8211; специализированный поиск</a></li><li><a href="http://anton.shevchuk.name/google/update-google-pr/" title="Обновление Google PR">Обновление Google PR</a></li><li><a href="http://anton.shevchuk.name/google/inside-adsense-now-in-russian/" title="Блог Inside AdSense теперь на русском языке">Блог Inside AdSense теперь на русском языке</a></li><li><a href="http://anton.shevchuk.name/google/charts-online/" title="Новый сервис &#8211; строим графики online">Новый сервис &#8211; строим графики online</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://anton.shevchuk.name/javascript/google-closure-for-beginners-with-examples/feed/</wfw:commentRss>
		<slash:comments>33</slash:comments>
		</item>
		<item>
		<title>PHP jQuery Cookbook</title>
		<link>http://anton.shevchuk.name/php/php-jquery-cookbook/</link>
		<comments>http://anton.shevchuk.name/php/php-jquery-cookbook/#comments</comments>
		<pubDate>Tue, 04 Jan 2011 12:57:43 +0000</pubDate>
		<dc:creator>Anton Shevchuk</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://anton.shevchuk.name/?p=1673</guid>
		<description><![CDATA[Давеча, меня попросили написать рецензию на новую книгу от издательства PACKT &#171;PHP jQuery Cookbook&#187;, и вот моя оценка готовым блюдам&#8230; Надеюсь я никого не обижу тем, что выложу оглавление книги, и буду идти по нему: Управление событиями в jQuery Первая глава посвящена полностью jQuery &#8212; основной акцент конечно сделан на события: события мышки и клавиатуры, [...]]]></description>
			<content:encoded><![CDATA[<p>Давеча, меня попросили написать рецензию на новую книгу от издательства <a href="https://www.packtpub.com/">PACKT</a> &laquo;<a href="https://www.packtpub.com/php-jquery-cookbook-to-create-interactive-web-applications/book">PHP jQuery Cookbook</a>&raquo;, и вот моя оценка готовым блюдам&#8230;<br />
<span id="more-1673"></span><br />
Надеюсь я никого не обижу тем, что выложу <a href="https://www.packtpub.com/toc/php-jquery-cookbook-table-contents">оглавление книги</a>, и буду идти по нему:</p>
<h3>Управление событиями в jQuery</h3>
<p>Первая глава посвящена полностью jQuery &mdash; основной акцент конечно сделан на события: события мышки и клавиатуры, отправка формы и манипуляции с ее элементами, затронуты методы live и die, но как-то мало о селекторах &mdash; лишь ссылка на официальную документацию, я бы привел больше примеров с <a href="http://api.jquery.com/category/selectors/form-selectors/">селекторами форм</a>. В целом, примеры достаточно простые и будут понятны любому новичку.</p>
<h3>Связка PHP и jQuery</h3>
<p>Данная глава посвящена AJAX&#8217;у, тема раскрыта практически полностью, хорошо описана обработка ошибок, за что автор получает плюс в карму ;)</p>
<h3>Работа с XML документами и JSON</h3>
<p>Каждому формату по главе, и да работа с XML и JSON описано очень хорошо, особенно то что касается PHP части . Порадовало, что автор ссылается на PHP 5.3, за что получает еще один плюс к своей карме.</p>
<h3>Работа с формами</h3>
<p>Работа с формами &mdash; этот момент вызывает ступор у многих web-разработчиков, особенно если требуется динамическая форма, с валидацией, и другими плюшками, а тут всё просто и на примерах &mdash; must have (<a href="https://www.packtpub.com/sites/default/files/2749OS-Chapter-5-Working-with-Forms.pdf">скачайте и посмотрите</a>). За использование <a href="http://ua.php.net/manual/en/function.filter-var.php">filter_var</a> еще один плюс.</p>
<h3>Добавление визуальных эффектов для форм</h3>
<p>Конечно, первый пример с игрой крестики-нолики меня немного озадачил, но остальные действительно будут полезны, особенно порадовал удачный пример с корзиной покупок.</p>
<h3>Создание прикольных навигационных меню</h3>
<p>Созданию различных меню посвящена целая глава, примеры конечно тривиальны, но в этом то и соль подобных книг, я ведь тоже <a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-2/">не смог обойти стороной менюшки</a>.</p>
<h3>Работа с данными используя PHP и jQuery</h3>
<p>Тривиальные задачи (автодополнение, облако тегов) и простые примеры работы с БД (хотя для кого-то они окажутся в новинку), но почему-то без использования PDO.</p>
<h3>Улучшаем сайт</h3>
<p>Это правильная глава, тут о кросс-доменных запросах пишут и почему-то сюда попала статья о создании jQuery плагинов, но от этого не стало хуже.</p>
<h3>Firebug</h3>
<p>В конце книги нас решили познакомить с полезным инструментом &mdash; Firebug, но как-то скромно.</p>
<h3>Оформление книги</h3>
<p>Тут чуть-чуть покритикую так как мне не понравилось сразу несколько вещей:</p>
<ul>
<li>Неужели сложно для примеров кода сделать адекватное форматирование?</li>
<li>Подсветка синтаксиса, даже элементарная, очень положительно сказывается на читаемости кода</li>
<li>Скриншоты с браузера &mdash; лучше убрать адресную строку и подобную лабуду, или по крайней мере делать их в одном браузере</li>
</ul>
<h3>Итого</h3>
<p>И что мы имеем? <a href="https://www.packtpub.com/php-jquery-cookbook-to-create-interactive-web-applications/book">Хорошая книга</a>, которая содержит много &laquo;правильныx&raquo; примеров &mdash; простых, доступных, охватывающих большинство задач, которые ставятся web-разработчикам в 90% проектов. Книга рассчитана на начинающих разработчиков, но пригодиться в качестве шпаргалки и для более продвинутой аудитории. </p>
<p>Немного огорчило, что автор забыл про все jQuery плагины, да и про jQuery UI стоило замолвить слово, хотя возможно не тот формат.</p>
<h3  class="related_post_title">Другие посты на эту тему</h3><ul class="related_post"><li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-write-animation-plugins/" title="jQuery для начинающих. Часть 9. Пишем плагины анимации">jQuery для начинающих. Часть 9. Пишем плагины анимации</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-filter-extend/" title="jQuery для начинающих. Часть 8. Расширяем фильтры">jQuery для начинающих. Часть 8. Расширяем фильтры</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-write-plugins/" title="jQuery для начинающих. Часть 7. Пишем плагины">jQuery для начинающих. Часть 7. Пишем плагины</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-events/" title="jQuery для начинающих. Часть 6. События">jQuery для начинающих. Часть 6. События</a></li><li><a href="http://anton.shevchuk.name/javascript/a-sexy-images-jquery-plugin/" title="(a) Sexy Images jQuery плагин">(a) Sexy Images jQuery плагин</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-effects/" title="jQuery для начинающих. Часть 5. Эффекты">jQuery для начинающих. Часть 5. Эффекты</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-iphone-ui/" title="jQuery iPhone UI">jQuery iPhone UI</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-datagrid-plugins/" title="10 jQuery Datagrid плагинов">10 jQuery Datagrid плагинов</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://anton.shevchuk.name/php/php-jquery-cookbook/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>jQuery для начинающих. Часть 9. Пишем плагины анимации</title>
		<link>http://anton.shevchuk.name/javascript/jquery-for-beginners-write-animation-plugins/</link>
		<comments>http://anton.shevchuk.name/javascript/jquery-for-beginners-write-animation-plugins/#comments</comments>
		<pubDate>Mon, 22 Nov 2010 12:58:45 +0000</pubDate>
		<dc:creator>Anton Shevchuk</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://anton.shevchuk.name/?p=1555</guid>
		<description><![CDATA[Новая статья совсем не для начинающих. Будем разрабатывать плагины для расширения анимации, а это уже другой уровень&#8230; Обучаем animate Большинство разработчиков уже сталкивалось с функцией animate в jQuery, это достаточно удобный инструмент для анимирования элементов на странице, а я расскажу как вы можете легко изменить поведение данной функции под свои цели. Для начала затравка &#8211; [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://anton.shevchuk.name/wp-content/uploads/2008/03/jquery-logo.gif" alt="jQuery Logo" title="jQuery Logo" width="168" height="146" class="aligncenter size-full wp-image-167" /><br />
Новая статья совсем не для начинающих. Будем разрабатывать плагины для расширения анимации, а это уже другой уровень&#8230;<br />
<span id="more-1555"></span></p>
<h3>Обучаем animate</h3>
<p>Большинство разработчиков уже сталкивалось с функцией <a href="http://api.jquery.com/animate/">animate</a> в jQuery, это достаточно удобный инструмент для анимирования элементов на странице, а я расскажу как вы можете легко изменить поведение данной функции под свои цели. </p>
<p>Для начала затравка &#8211; метод animate манипулирует объектом $.fx, в себе он содержит следующие методы: </p>
<ul>
<li>cur &#8211; возвращает текущее значение атрибута </li>
<li>custom &#8211; отвечает за запуск анимации </li>
<li>hide/show &#8211; простые функции hide/show </li>
<li>step &#8211; каждый отдельный шаг анимации, т.е. вызывается много-много раз </li>
<li>update &#8211; данная функция отвечает за применение атрибутов</li>
</ul>
<p>Из всего приведенного мы будем расширять объект $.fx.step. Возьмем достаточно тривиальную задачу &#8211; заставим плавно изменить цвет шрифта для заданного набора элементов:</p>
<pre class="brush: jscript; title: ; notranslate">
$('p').animate({color:'#ff0000'});
</pre>
<p>Приведенный выше код не даст никакого эффекта, нам потребуется прокачать $.fx.step:</p>
<pre class="brush: jscript; title: ; notranslate">
(function($) {
// color - свойство с которым мы будем работать
$.fx.step.color = function(fx) {

	// настройки анимации
	fx.options;

	// свойство которое изменяем
	fx.prop;

	// начальные значения
	fx.start;

	// результирующие значения
	fx.end;

	// коэффициент, изменяется от 0 до 1
	fx.pos;

	// еденицы измерения
	fx.unit;

};
})(jQuery);
</pre>
<p>Перед вами основные свойства объекта $.fx &#8211; это и есть наши кирпичики, с их помощью будем строить нашу анимацию. Перед работой стоит заглянуть внутрь каждого из приведенных свойств: </p>
<pre class="brush: jscript; title: ; notranslate">
// добавим логирование внутрь нашего плагина
console.log(fx.options, fx.prop, fx.start, fx.end, fx.pos, fx.unit);

/* вывод в консоли:
Object duration=5000 old=false curAnim=Object orig=Object / color / 0 / #ff0000 / 0 / px
Object duration=5000 old=false curAnim=Object orig=Object / color / 0 / #ff0000 / 0.0008351307317917 / px
Object duration=5000 old=false curAnim=Object orig=Object / color / 0 / #ff0000 / 0.0009670080649619717 / px
Object duration=5000 old=false curAnim=Object orig=Object / color / 0 / #ff0000 / 0.0010877292712792586 / px
Object duration=5000 old=false curAnim=Object orig=Object / color / 0 / #ff0000 / 0.0012155411253085835 / px
..
*/
</pre>
<p>Как и предполагалось &#8211; метод вызывается N кол-во раз, в зависимости от продолжительности анимации, при этом fx.pos постепенно наращивает своё значение с 0 до 1 (аналогично ведет себя и параметр fx.state, только знаков после запятой у него поменьше, есть ли еще какая разница &#8211; я не знаю).</p>
<blockquote><p>fx.pos, по умолчанию, наращивается линейно, если надо как-то иначе &mdash; то стоит посмотреть на <a href="http://gsgd.co.uk/sandbox/jquery/easing/">easing плагин</a> или дочитать статью до конца  (об этом я уже упоминал в статье <a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-effects/">Эффекты</a>)</p></blockquote>
<p>Теперь не мешало бы заставить эту систему работать, начнем с инициализации крайних значений (листинг функции parseColor вы сможете найти в <a href="http://anton.shevchuk.name/wp-demo/jquery-tutorials-9/animation.html">самом примере</a>): </p>
<pre class="brush: jscript; title: ; notranslate">
// инициализация должна проходить лишь однажды
if (fx.state == 0 ) {
	// получаем текущее значение элемента
	var start = $.curCSS(fx.elem,'color');

	// разбираем на составляющие текущее состояние элемента
	// start содержит строку вида rgb(255, 255, 255)
            var res = start.match(/rgb\(([0-9]{1,3}),\s?([0-9]{1,3}),\s?([0-9]{1,3})\)/);

            if (!res) {
                // или #ffffff;
                var res = start.match(/^#(\w{2})(\w{2})(\w{2})$/);
                var rgb = [parseInt(res[1], 16), parseInt(res[2], 16), parseInt(res[3], 16)];
            } else {
                var rgb = [parseInt(res[1]), parseInt(res[2]), parseInt(res[3])];
            }

	// составляющие запихиваем в fx.start
	fx.start = rgb;

	// разбираем что нам нужно получить, и закидываем в fx.end
	fx.end   = parseColor(fx.end);
}
</pre>
<p>Теперь необходимо изменить текущее значение свойств и применить его: </p>
<pre class="brush: jscript; title: ; notranslate">
// нехитрое вычисление
var R = Math.round(((fx.end[0] - fx.start[0]) * fx.pos) + fx.start[0]);
var G = Math.round(((fx.end[1] - fx.start[1]) * fx.pos) + fx.start[1]);
var B = Math.round(((fx.end[2] - fx.start[2]) * fx.pos) + fx.start[2]);

// непосредственно измение свойств элемента
fx.elem.style.color = 'rgb('+R+','+G+','+B+')';
</pre>
<p>Всё вместе: </p>
<pre class="brush: jscript; title: ; notranslate">
$.fx.step.color = function(fx) {
	// инициализация должна проходить лишь однажды
	if (fx.state == 0 ) {
		// получаем текущее значение элемента
		var start = $.curCSS(fx.elem,'color');

		// разбираем на составляющие текущее состояние элемента
		// start содержит строку rgb(255, 255, 255)
		var res = start.match(/rgb\(([0-9]{1,3}),\s?([0-9]{1,3}),\s?([0-9]{1,3})\)/);

		// составляющие запихиваем в fx.start
		fx.start = [parseInt(res[1]), parseInt(res[2]), parseInt(res[3])];

		// разбираем что нам нужно получить, и закидываем в fx.end
		fx.end   = parseColor(fx.end);
	}
	// нехитрое вычисление
	var R = Math.round(((fx.end[0] - fx.start[0]) * fx.pos) + fx.start[0]);
	var G = Math.round(((fx.end[1] - fx.start[1]) * fx.pos) + fx.start[1]);
	var B = Math.round(((fx.end[2] - fx.start[2]) * fx.pos) + fx.start[2]);

	// непосредственно измение свойств элемента
	fx.elem.style.color = 'rgb('+R+','+G+','+B+')';
};
</pre>
<p><a href="http://anton.shevchuk.name/wp-demo/jquery-tutorials-9/animation.html">Результатом</a> станет плавное перетекание исходного цвета к красному (#F00 == #FF0000 == 255,0,0)</p>
<h3>Своя анимация</h3>
<p>Теперь, познав основы, можем попробовать анимировать некоторый неизвестный параметр shiver (да пусть дрожат пред нами): </p>
<pre class="brush: jscript; title: ; notranslate">$('p').animate({shiver:100});</pre>
<p>Идем по натоптанному пути: </p>
<pre class="brush: jscript; title: ; notranslate">
(function($) {
	var left;
	$.fx.step.shiver = function(fx) {

		// инициализация должна проходить лишь однажды
		if (fx.state == 0 ) {

			// начальное расположение элемента от левого края
			// дрожать будем по горизонтали
			left = $.curCSS(fx.elem,'left');

			// устанавливаем относительное расположение элемента
			fx.elem.style.position = 'relative';
		}
	};
})(jQuery);
</pre>
<p>Так же реализуем саму дрожь элемента: </p>
<pre class="brush: jscript; title: ; notranslate">
// если это последняя итерация - вернем всё на своё место
if (fx.now == fx.end) {
	fx.elem.style.left = left;
} else if (Math.round(1000 * fx.pos)%2==0) {
	// четная итерация
	// непосредственно измение свойств элемента
	fx.elem.style.left = - Math.round(Math.random()*fx.end) + fx.unit;

} else {
	// нечетная итерация
	// непосредственно измение свойств элемента
	fx.elem.style.left = + Math.round(Math.random()*fx.end) + fx.unit;
}
</pre>
<p>Собираем вместе, и смотрим на <a href="http://anton.shevchuk.name/wp-demo/jquery-tutorials-9/animation2.html">результат работы</a>&#8230;</p>
<h3>Пишем свой easing плагин</h3>
<p>Возвращаясь к easing&#8217;у &#8211; мы можем легко написать свою функцию, которой будет следовать анимация. Дабы особо не фантазировать &#8211; я взял пример из статьи o MooTools &#8211; очень наглядный пример с сердцебиением, которое описывается следующими функциями:</p>
<p><img src="http://anton.shevchuk.name/wp-demo/jquery-tutorials-9/heartbeat.function.gif" alt="Heart Beat Functions" /></p>
<p>Теперь напишем соответствующий код: </p>
<pre class="brush: jscript; title: ; notranslate">
$.extend($.easing, {
    /**
     * Heart Beat
     *
     * @param x
     * @param t current time
     * @param b begInnIng
     * @param c change In value
     * @param d duration
     *
     * @link http://habrahabr.ru/blogs/mootools/43379/
     */
    heart:function(x, t, b, c, d) {
        if (x &lt; 0.3)
            return Math.pow(x, 4) * 49.4;

        if (x &lt; 0.4)
            return 9 * x - 2.3;

        if (x &lt; 0.5)
            return -13 * x + 6.5;

        if (x &lt; 0.6)
            return 4 * x - 2;

        if (x &lt; 0.7)
            return 0.4;

        if (x &lt; 0.75)
            return 4 * x - 2.4;

        if (x &lt; 0.8)
            return -4 * x + 3.6;

        if (x &gt;= 0.8)
            return 1 - Math.sin(Math.acos(x));
    }
});
</pre>
<p>Чуть-чуть расширю пример дополнительными функциями (развернем и скомбинируем): </p>
<pre class="brush: jscript; title: ; notranslate">
heartIn:function (x, t, b, c, d) {
    return $.easing.heartIn(x, t, b, c, d);
},
heartOut:function (x, t, b, c, d) {
    return c - $.easing.heartIn(1 - x, t, b, c, d) + b;
},
heartInOut: function (x, t, b, c, d) {
    if (t &lt; d/2) return $.easing.heartIn(1 - x, t, b, c, d) + b;
    return $.easing.heartOut(1 - x, t, b, c, d) + b;
}
</pre>
<p>Графически это будет выглядеть так:</p>
<p><img src="http://anton.shevchuk.name/wp-demo/jquery-tutorials-9/heartbeat.in.gif" alt="HeartIn" /></p>
<p><img src="http://anton.shevchuk.name/wp-demo/jquery-tutorials-9/heartbeat.out.gif" alt="HeartOut" /></p>
<p><img src="http://anton.shevchuk.name/wp-demo/jquery-tutorials-9/heartbeat.in.out.gif" alt="HeartInOut" /></p>
<p><a href="http://anton.shevchuk.name/wp-demo/jquery-tutorials-9/easing.html">Результат</a> лучше не показывать кардиолагам.</p>
<h3>Цикл статей</h3>
<ol>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners/">jQuery для начинающих</a></li>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-2/">jQuery для начинающих. Часть 2. JavaScript Меню</a></li>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-ajax/">jQuery для начинающих. Часть 3. AJAX</a></li>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-selectors/">jQuery для начинающих. Часть 4. Селекторы</a></li>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-effects/">jQuery для начинающих. Часть 5. Эффекты</a></li>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-events/">jQuery для начинающих. Часть 6. События</a></li>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-write-plugins/">jQuery для начинающих. Часть 7. Пишем плагины</a></li>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-filter-extend/">jQuery для начинающих. Часть 8. Расширяем фильтры</a></li>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-write-animation-plugins/">jQuery для начинающих. Часть 9. Пишем плагины анимации</a></li>
</ol>
<h3  class="related_post_title">Другие посты на эту тему</h3><ul class="related_post"><li><a href="http://anton.shevchuk.name/php/php-jquery-cookbook/" title="PHP jQuery Cookbook">PHP jQuery Cookbook</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-filter-extend/" title="jQuery для начинающих. Часть 8. Расширяем фильтры">jQuery для начинающих. Часть 8. Расширяем фильтры</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-write-plugins/" title="jQuery для начинающих. Часть 7. Пишем плагины">jQuery для начинающих. Часть 7. Пишем плагины</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-events/" title="jQuery для начинающих. Часть 6. События">jQuery для начинающих. Часть 6. События</a></li><li><a href="http://anton.shevchuk.name/javascript/a-sexy-images-jquery-plugin/" title="(a) Sexy Images jQuery плагин">(a) Sexy Images jQuery плагин</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-effects/" title="jQuery для начинающих. Часть 5. Эффекты">jQuery для начинающих. Часть 5. Эффекты</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-iphone-ui/" title="jQuery iPhone UI">jQuery iPhone UI</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-datagrid-plugins/" title="10 jQuery Datagrid плагинов">10 jQuery Datagrid плагинов</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://anton.shevchuk.name/javascript/jquery-for-beginners-write-animation-plugins/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>jQuery для начинающих. Часть 8. Расширяем фильтры</title>
		<link>http://anton.shevchuk.name/javascript/jquery-for-beginners-filter-extend/</link>
		<comments>http://anton.shevchuk.name/javascript/jquery-for-beginners-filter-extend/#comments</comments>
		<pubDate>Tue, 19 Oct 2010 10:22:27 +0000</pubDate>
		<dc:creator>Anton Shevchuk</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://anton.shevchuk.name/?p=1556</guid>
		<description><![CDATA[Продолжаем заумные статьи о jQuery, сегодня поведу речь о движке Sizzle и возможности его расширения. Sizzle &#8212; Javascript движок для поиска элементов по CSS селектору, был неразлучен с jQuery, но с некоторых пор так же распространяется как отдельная библиотека, и может использоваться в том же Dojo Toolkit, или в вашем собственном фреймворке API для расширения [...]]]></description>
			<content:encoded><![CDATA[<p align="center"><img src="http://anton.shevchuk.name/wp-content/uploads/2008/03/jquery-logo.gif" alt="jQuery Logo" title="jQuery Logo" width="168" height="146" class="aligncenter size-full wp-image-167" /></p>
<p>Продолжаем заумные статьи о jQuery, сегодня поведу речь о движке Sizzle и возможности его расширения.<br />
<span id="more-1556"></span></p>
<blockquote><p><a href="http://sizzlejs.com/">Sizzle</a> &mdash; Javascript движок для поиска элементов по CSS селектору, был неразлучен с jQuery, но с некоторых пор так же распространяется как отдельная библиотека, и может использоваться в том же Dojo Toolkit, или в вашем собственном фреймворке</p></blockquote>
<h3>API для расширения фильтров</h3>
<p>Для начала приведу пример фильтров, которые вы возможно использовали, да не заглядывали внутрь:</p>
<pre class="brush: jscript; title: ; notranslate">
$(&quot;div:animated&quot;); // поиск анимированных элементов
$(&quot;div:hidden&quot;); // поиск скрытых элементов div
$(&quot;div:visible&quot;); // поиск видимых элементов div (противоположен предыдущему)
</pre>
<p>Почему я привел только эти фильтры? Всё просто &mdash; только они не входят в Sizzle, и относятся лишь к jQuery, именно такие плагины мы будем тренироваться разрабатывать. Начнем с кода, который нам поможет разобраться с API:</p>
<pre class="brush: jscript; title: ; notranslate">
(function($, window, undefined){
    $.extend($.expr[':'], {
        /**
         * @param element DOM элемент
         * @param i порядковый номер элемента
         * @param match объект матчинга регулярного выражения (ищите PSEUDO в сорцах)
         * @param elements массив всех найденных DOM элементов
         */
        test: function(element, i, match, elements) {
            return true; // элемент подходит нам
            return false; // или нет
        }
    })
})(jQuery, window);
</pre>
<p>Приведенный код конечно не очень наглядный, поэтому расширю его <a href="http://anton.shevchuk.name/wp-demo/jquery-tutorials-8/basic.html">рабочими примерами</a>:</p>
<pre class="brush: jscript; title: ; notranslate">
test: function(element, i, match, elements) {
    // мы можем создавать проверки на основе правил, которые относятся к DOM элементу
    // нам нужны лишь параграфы - $(&quot;p:test&quot;)
    if (element.tagName.toUpperCase() != 'P') return false;

    // ищем по порядковому номеру
    // $(&quot;p:test(4)&quot;)
    if (parseInt(match[3]) == (i+1)) {
        return true;
    } else
    // чёт / нечет
    // $(&quot;p:test(even)&quot;) или $(&quot;p:test(odd)&quot;)
    if (match[3] == &quot;even&quot; &amp;&amp; (i%2==0)) {
        return true;
    } else if (match[3] == &quot;odd&quot; &amp;&amp; (i%2==1)) {
        return true;
    } else
    // первый/последний
    // $(&quot;p:test(first)&quot;) или $(&quot;p:test(last)&quot;)
    if (match[3] == &quot;first&quot; &amp;&amp; (i==0)) {
        return true;
    } else if (match[3] == &quot;last&quot; &amp;&amp; (i==(elements.length-1))) {
        return true;
    }
    return false;
}
</pre>
<p>Примеры использования:</p>
<pre class="brush: jscript; title: ; notranslate">
$(&quot;p:test&quot;);
$(&quot;p:test(2)&quot;);

// чёт/нечет
$(&quot;p:test(odd)&quot;);
$(&quot;p:test(even)&quot;);

// первый/последний
$(&quot;p:test(first)&quot;);
$(&quot;p:test(last)&quot;);
</pre>
<p>Вот таким не хитрым способом можно расширять фильтры, если у вас есть еще примеры возможных плагинов, прошу &mdash; комментируйте.</p>
<h3>Пример plugin&#8217;а</h3>
<p>Ну от тривиальных абстрактных задач к чему-то более конкретному &mdash; есть несложная задачка: &laquo;выделить ссылки в тексте в зависимости от типа: внешняя, внутренняя, якорь&raquo;, ну и пример текста:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;p&gt;
&lt;a name=&quot;p1&quot;&gt;Lorem ipsum dolor sit amet&lt;/a&gt;, consectetur adipiscing elit. Ut lacinia quam
nec enim scelerisque porta. In ut lorem ipsum. Proin iaculis viverra rutrum. Maecenas quis
ante enim. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia
Curae; Vestibulum luctus tristique feugiat. Morbi dictum est dolor,
&lt;a href=&quot;http://anton.shevchuk.name&quot;&gt;at condimentum nisl&lt;/a&gt;. Ut id nunc augue, at luctus
enim. Phasellus urna nunc, aliquam sit amet rutrum ac, imperdiet in nunc. Cras mattis massa
et est sodales ac auctor mi sagittis. Fusce elementum ultrices nunc, eu scelerisque massa
sodales quis. Aliquam bibendum accumsan nibh ut blandit.
&lt;/p&gt;
&lt;p&gt;
&lt;a href=&quot;index.html&quot;&gt;Praesent venenatis dictum ante&lt;/a&gt;, ...
&lt;/p&gt;
&lt;p&gt;
Aliquam erat volutpat. ... &lt;a href=&quot;http://google.com&quot;&gt;Curabitur congue enim&lt;/a&gt; ... &lt;a href=&quot;#p1&quot;&gt;Duis lectus tellus&lt;/a&gt;, gravida non commodo eu, dictum a tellus. Praesent a nibh vel nisl sodales tincidunt at ac leo.
&lt;/p&gt;
</pre>
<p>Для решения лучше всего подошли бы фильтры для селекторов:</p>
<pre class="brush: jscript; title: ; notranslate">
$(&quot;a:internal&quot;);
$(&quot;a:anchor&quot;);
$(&quot;a:external&quot;);
</pre>
<p>Поскольку из коробки данный функционал не доступен, мы <a href="http://anton.shevchuk.name/wp-demo/jquery-tutorials-8/filter.html">напишем его сами</a>, для этого нам понадобится не так уж и много:</p>
<pre class="brush: jscript; title: ; notranslate">
$.extend($.expr[':'], {
    /**
     * Пример определения внешней ссылки
     * остальные фильтры найдете на странице примеров
     *
     * @param element нам понадобится лишь DOM Element
     */
    external: function(element) {
        // у нас ссылка?
        if (element.tagName.toUpperCase() != 'A') return false;
        // есть ли атрибут href
        if (element.getAttribute('href')) {
            // отсекаем ненужное
            if ((element.getAttribute('href').indexOf('/') === 0)             // внутренняя ссылка
                || (element.getAttribute('href').indexOf('#') === 0)        // якорь
                || (element.getAttribute('href').indexOf(window.location.hostname) === 7) // наш домен по http://...
                || (element.getAttribute('href').indexOf(window.location.hostname) === 8) // наш домен по https://...
                    ) {
                return false;
            } else {
                // да, мы нашли правильные ссылки
                return true;
            }
        } else {
            return false;
        }
    }
})
</pre>
<blockquote><p>Как вы могли заметить в статье я всегда, нет не так &mdash; <strong>ВСЕГДА</strong> использую фильтр вместе с HTML тэгом который я ищу &mdash; <strong>$(&#8220;tag:filter&#8221;)</strong>. Это один из пунктов оптимизации работы с фильтрами jQuery, иначе ваш фильтр будет обрабатывать все DOM элементы на странице, а это может очень сильно сказаться на производительности. Если же у вас несколько тегов, то пишите так &mdash; <strong>$(&#8220;tag1:filter, tag2:filter, tag3:filter&#8221;)</strong></p></blockquote>
<h3>Материалы</h3>
<p>Тут всё грустно &mdash; нашел лишь <a href="http://github.com/jeresig/sizzle/wiki">скудную официальную документацию</a>, если есть чем поделиться, ну вы поняли&#8230;</p>
<h3>Цикл статей</h3>
<ol>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners/">jQuery для начинающих</a></li>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-2/">jQuery для начинающих. Часть 2. JavaScript Меню</a></li>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-ajax/">jQuery для начинающих. Часть 3. AJAX</a></li>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-selectors/">jQuery для начинающих. Часть 4. Селекторы</a></li>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-effects/">jQuery для начинающих. Часть 5. Эффекты</a></li>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-events/">jQuery для начинающих. Часть 6. События</a></li>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-write-plugins/">jQuery для начинающих. Часть 7. Пишем плагины</a></li>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-filter-extend/">jQuery для начинающих. Часть 8. Расширяем фильтры</a></li>
</ol>
<h3  class="related_post_title">Другие посты на эту тему</h3><ul class="related_post"><li><a href="http://anton.shevchuk.name/php/php-jquery-cookbook/" title="PHP jQuery Cookbook">PHP jQuery Cookbook</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-write-animation-plugins/" title="jQuery для начинающих. Часть 9. Пишем плагины анимации">jQuery для начинающих. Часть 9. Пишем плагины анимации</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-write-plugins/" title="jQuery для начинающих. Часть 7. Пишем плагины">jQuery для начинающих. Часть 7. Пишем плагины</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-events/" title="jQuery для начинающих. Часть 6. События">jQuery для начинающих. Часть 6. События</a></li><li><a href="http://anton.shevchuk.name/javascript/a-sexy-images-jquery-plugin/" title="(a) Sexy Images jQuery плагин">(a) Sexy Images jQuery плагин</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-effects/" title="jQuery для начинающих. Часть 5. Эффекты">jQuery для начинающих. Часть 5. Эффекты</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-iphone-ui/" title="jQuery iPhone UI">jQuery iPhone UI</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-datagrid-plugins/" title="10 jQuery Datagrid плагинов">10 jQuery Datagrid плагинов</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://anton.shevchuk.name/javascript/jquery-for-beginners-filter-extend/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Анонс мастер-классов по JavaScript&#8217;у</title>
		<link>http://anton.shevchuk.name/javascript/javascript-master-class-feedback/</link>
		<comments>http://anton.shevchuk.name/javascript/javascript-master-class-feedback/#comments</comments>
		<pubDate>Mon, 11 Oct 2010 10:01:25 +0000</pubDate>
		<dc:creator>Anton Shevchuk</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[announcement]]></category>

		<guid isPermaLink="false">http://anton.shevchuk.name/?p=1565</guid>
		<description><![CDATA[Ух, побывал я на прошедшем мастер классе в Харькове. Было это еще летом, а фидбек пишу только сейчас, да еще и совмещаю с анонсом новых, т.е. убиваю двух зайцев одним махом. Для начала о датах предстоящих мастер-классов: Днепропетровск &#8212; 23-24 октября Одесса &#8212; 30-31 октября Киев &#8212; 4-5 декабря Харьков &#8212; 11-12 декабря Лично мое [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://anton.shevchuk.name/wp-content/uploads/2010/06/javascript.jpg" alt="" title="JavaScript" width="400" height="400" class="aligncenter size-full wp-image-1302" /></p>
<p>Ух, побывал я на <a href="http://anton.shevchuk.name/javascript/javascript-master-class/">прошедшем мастер классе в Харькове</a>. Было это еще летом, а фидбек пишу только сейчас, да еще и совмещаю с анонсом новых, т.е. убиваю двух зайцев одним махом.</p>
<p><span id="more-1565"></span></p>
<p>Для начала о датах <a href="http://javascript.ru/mk">предстоящих мастер-классов</a>:</p>
<ul>
<li>Днепропетровск &mdash; 23-24 октября</li>
<li>Одесса &mdash; 30-31 октября</li>
<li>Киев &mdash; 4-5 декабря </li>
<li>Харьков &mdash; 11-12 декабря</li>
</ul>
<blockquote><p>Лично мое мнение, если возвращаясь с мастер-класса/тренинга/конференции вы осознаете, что хотя бы 10% услышанной информации были для вас в новинку &mdash; значит вы не зря потратили время и деньги. Так же, неоспоримым плюсом является повышение самомотивации и желание свернуть горы ;)</p></blockquote>
<p>Ну, а теперь непосредственно о самих мастер-классах, расскажу насколько они были полезны лично мне:</p>
<h3>Профессиональное javascript-программирование</h3>
<p>С данного мастер класса я унёс несколько полезных инструментов, слегка подкачал свои знания по ООП применительно к JS, в целом был доволен. Если же подходить с точки зрения необходимости данного мастер класса лично для вас, то советую ответить на следующие вопросы:</p>
<ul>
<li>Вы знаете как дебажить javascript? А в IE?</li>
<li>Вы понимаете почему JS называют прототип-ориентированным языком программирования?</li>
<li>Вы знаете что такое &laquo;замыкания&raquo;?</li>
<li>Вы применяли шаблоны проектирования в js приложениях?</li>
</ul>
<p>Если ответили &laquo;нет&raquo; хоть раз &mdash; то идите.</p>
<h3>Сложный AJAX и COMET: тонкости Web 2.0</h3>
<p>Тут для меня всё было в новинку, так как в живую с COMET работать не приходилось, и лишь изредка проскакивали заметки в моем RSS ридере. Если вы до этого не работали с COMET, то обязательно сходите &mdash; если имеете опыт разработки, ничего нового, я думаю, вы не услышите.</p>
<h3>Мастер-класс по клиентской оптимизации</h3>
<p>О, это самый полезный мастер-класс из представленных, количество новой и полезной информации было много. Нужен ли он вам? На этот вопрос думаю будет утвердительный ответ, ведь мы так привыкли к оптимизации серверной части, что совсем забываем о том, что между клиентом и сервером ни одна кирпичная перегородка, а тысячи километров и десятки роутеров.</p>
<h3>Секреты jQuery</h3>
<p>Наверное лишь этот мастер класс прошел для меня без сюрпризов, хотя настолько глубоко в дебри Sizzle я не вникал. И да, если вы не в курсе, что есть Sizzle &mdash; то этот мастер класс для вас будет полезен.</p>
<h3  class="related_post_title">Другие посты на эту тему</h3><ul class="related_post"><li><a href="http://anton.shevchuk.name/php/zend-framework-day/" title="Zend Framework Day">Zend Framework Day</a></li><li><a href="http://anton.shevchuk.name/html-and-css/master-klass-yuriy-akella-artyukh/" title="Мастер-класс Юры Артюха">Мастер-класс Юры Артюха</a></li><li><a href="http://anton.shevchuk.name/internet/sphinx-master-class/" title="Мастер-класс по Sphinx">Мастер-класс по Sphinx</a></li><li><a href="http://anton.shevchuk.name/php/zfconf-2011-piter/" title="Конференция ZFConf 2011">Конференция ZFConf 2011</a></li><li><a href="http://anton.shevchuk.name/php/zfconf-ukraine/" title="ZFConf Ukraine">ZFConf Ukraine</a></li><li><a href="http://anton.shevchuk.name/javascript/javascript-master-class/" title="Мастер-классы по JavaScript&#8217;у">Мастер-классы по JavaScript&#8217;у</a></li><li><a href="http://anton.shevchuk.name/internet/pdfeed-rss-to-pdf-generator/" title="PDFEED &#8211; или RSS2PDF в новом обличье">PDFEED &#8211; или RSS2PDF в новом обличье</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://anton.shevchuk.name/javascript/javascript-master-class-feedback/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>jQuery для начинающих. Часть 7. Пишем плагины</title>
		<link>http://anton.shevchuk.name/javascript/jquery-for-beginners-write-plugins/</link>
		<comments>http://anton.shevchuk.name/javascript/jquery-for-beginners-write-plugins/#comments</comments>
		<pubDate>Fri, 08 Oct 2010 06:39:39 +0000</pubDate>
		<dc:creator>Anton Shevchuk</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://anton.shevchuk.name/?p=1044</guid>
		<description><![CDATA[Если вы читали предыдущие статьи из этой серии, то вы наверное уже пробовали разрабатывать свой плагин, если нет, то можем начать вместе&#8230; Для начала вспомним, для чего нам нужны плагины? Мой ответ &#8212; создание повторно используемого кода, и да &#8212; с удобным интерфейсом. Давайте напишем такой код, вот простая задачка: &#171;По клику на параграф, текст [...]]]></description>
			<content:encoded><![CDATA[<p align="center"><img src="http://anton.shevchuk.name/wp-content/uploads/2008/03/jquery-logo.gif" alt="jQuery Logo" title="jQuery Logo" width="168" height="146" class="aligncenter size-full wp-image-167" /></p>
<p>Если вы читали предыдущие статьи из этой серии, то вы наверное уже пробовали разрабатывать свой плагин, если нет, то можем начать вместе&#8230;<br />
<span id="more-1044"></span></p>
<p>Для начала вспомним, для чего нам нужны плагины? Мой ответ &mdash; создание повторно используемого кода, и да &mdash; с удобным интерфейсом. Давайте напишем такой код, вот простая задачка: &laquo;По клику на параграф, текст должен измениться на красный&raquo;</p>
<h3>Javascript и даже не jQuery</h3>
<p>Дабы не забывать истоков &mdash; начнем с <a href="http://anton.shevchuk.name/wp-demo/jquery-tutorials-7/native.html">реализации на нативном javascript&#8217;е</a>:</p>
<pre class="brush: jscript; title: ; notranslate">
var loader = function () {
    // находим все параграфы
    var para = document.getElementsByTagName('P');
    // перебираем все, и вешаем обработчик
    for (var i=0,size=para.length;i&lt;size;i++) {
        // обработчик
        para[i].onclick = function() {
             this.style.color = &quot;#FF0000&quot;;
        }
    }
}
// естественно, весь код должен работать после загрузки всей страницы
document.addEventListener(&quot;DOMContentLoaded&quot;, loader, false);
</pre>
<blockquote><p>Данный код не является кроссбраузерным, и написан с целью лишний раз подчеркнуть удобство использования фреймворка</p></blockquote>
<h3>jQuery, но еще не плагин</h3>
<p>Теперь можно этот код упростить, подключаем jQuery и получаем <a href="http://anton.shevchuk.name/wp-demo/jquery-tutorials-7/native2.html">следующий вариант</a>:</p>
<pre class="brush: jscript; title: ; notranslate">
$(document).ready(function(){
    $('p').click(function(){
        $(this).css('color', '#ff0000');
    })
});
</pre>
<h3>Таки jQuery плагин</h3>
<p>С поставленной задачей мы справились, но где тут повторное использование кода? Или если нам надо не в красный, а в зеленый перекрасить? Вот тут начинается самое интересное, чтобы написать простой плагин достаточно расширить объект <em>$.fn</em>:</p>
<pre class="brush: jscript; title: ; notranslate">
$.fn.mySimplePlugin = function () {
    $(this).click(function(){
        $(this).css('color', '#ff0000');
    })
}
</pre>
<p>Если же писать более грамотно, то нам необходимо ограничить переменную <em>$</em> только нашим плагином, а так же возвращать <em>this</em>, чтобы можно было использовать цепочки вызовов (т.н. chaining) , делается это следующим образом: </p>
<pre class="brush: jscript; title: ; notranslate">
(function($) {
  $.fn.mySimplePlugin = function(){
     // код плагина ...
     return this;
  };
})(jQuery);
</pre>
<blockquote><p>Внесу небольшое пояснение о происходящем, код <em>(function($){&#8230;})(jQuery)</em> создает анонимную функцию, и тут же вызывает ее, передавая в качестве параметра объект jQuery, таким образом внутри анонимной функции мы можем использовать алиас <em>$</em> не боясь за конфликты с другими библиотеками &mdash; так как теперь <em>$</em> находится лишь в области видимости нашей функции, и мы имеем полный контроль над ней</p></blockquote>
<p>Добавим опцию по выбору цвета и получим <a href="http://anton.shevchuk.name/wp-demo/jquery-tutorials-7/simple.html">рабочий плагин</a>:</p>
<pre class="brush: jscript; title: ; notranslate">
(function($) {
    // значение по умолчанию - ЗЕЛЁНЫЙ
    var defaults = { color:'green' };

    // актуальные настройки, глобальные
    var options;

    $.fn.mySimplePlugin = function(params){
        // при многократном вызове функции настройки будут сохранятся, и замещаться при необходимости
        options = $.extend({}, defaults, options, params);

        $(this).click(function(){
            $(this).css('color', options.color);
        });

        return this;
    };
})(jQuery);
</pre>
<p>Вызов:</p>
<pre class="brush: jscript; title: ; notranslate">
// первый вызов
$('p:first,p:last').mySimplePlugin();
// второй вызов
$('p:eq(1)').mySimplePlugin({ color: 'red' });
</pre>
<p>В результате работы данного плагина, каждый клик будет изменять цвет параграфа на красный, т.к. мы используем глобальную переменную для хранения настроек, то второй вызов плагина изменят значение для всех элементов. Можно внести небольшие изменения, и разделить настройки для каждого вызова (<a href="http://anton.shevchuk.name/wp-demo/jquery-tutorials-7/simple2.html">пример</a>):</p>
<pre class="brush: jscript; title: ; notranslate">
// актуальные настройки, будут индивидуальными при каждом запуске
var options = $.extend({}, defaults, params);
</pre>
<h3>Работаем с коллекциями объектов</h3>
<p>Тут все просто, достаточно запомнить &mdash; <em>this</em> содержит jQuery объект с коллекцией всех элементов, т.е. :</p>
<pre class="brush: jscript; title: ; notranslate">
$.fn.mySimplePlugin = function(){
     console.log(this); // jQuery
     console.log(this.length); // число элементов
     return this;
};
</pre>
<p>Если мы хотим обрабатывать каждый элемент то соорудим следующую конструкцию:</p>
<pre class="brush: jscript; title: ; notranslate">
// необходимо обработать каждый элемент в коллекции
return this.each(function(){
    $(this).click(function(){
        $(this).css('color', options.color);
    });
});

// предыдущий вариант немного избыточен,
// т.к. внутри функции click и так есть перебор элементов
return this.click(function(){
        $(this).css('color', options.color);
    });
</pre>
<blockquote><p>Опять же напомню, если ваш плагин не должен что-то возвращать по вашей задумке &mdash; возвращайте <em>this</em> &mdash; цепочки вызовов в jQuery это часть магии, не стоит её ломать</p></blockquote>
<h3>Публичные методы</h3>
<p>Так, у нас написан крутой плагин, надо бы ему еще докрутить функционала, пусть цвет регулируется несколькими кнопками на сайте. Для этого нам понадобится некий метод &laquo;color&raquo;, который и будет в ответе за всё. Сейчас приведу <a href="http://anton.shevchuk.name/wp-demo/jquery-tutorials-7/extend.html">пример кода готового плагина</a> &mdash; будем курить вместе:</p>
<pre class="brush: jscript; title: ; notranslate">
// значение по умолчанию
var defaults = { color:'green' };

// наши публичные методы
var methods = {
    // инициализация плагина
    init:function(params) {
        // актуальные настройки, будут индивидуальными при каждом запуске
        var options = $.extend({}, defaults, params);

        return this.click(function(){
           $(this).css('color', options.color);
        });
    },
    // изменение цвета
    color:function(color) {
        $(this).css('color', color);
    },
    // сброс цвета
    reset:function() {
        $(this).css('color', 'black');
    }
};

$.fn.mySimplePlugin = function(method){

    // немного магии
    if ( methods[method] ) {
        // если запрашиваемый метод существует, мы его вызываем
        // все параметры, кроме имени метода прийдут в метод
        // this так же перекочует в метод
        return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
    } else if ( typeof method === 'object' || ! method ) {
        // если первым параметром идет объект, либо совсем пусто
        // выполняем метод init
        return methods.init.apply( this, arguments );
    } else {
        // если ничего не получилось
        $.error( 'Метод &quot;' +  method + '&quot; не найден в плагине jQuery.mySimplePlugin' );
    }
};
</pre>
<p>Теперь еще небольшой пример использование данных методов:</p>
<pre class="brush: jscript; title: ; notranslate">
// вызов без параметров - будет вызван init
$('p').mySimplePlugin();
// вызов метода color и передача цвета в качестве параметров
$('p').mySimplePlugin('color', '#FFFF00');
// вызов метода reset
$('p').mySimplePlugin('reset');
</pre>
<blockquote><p>Для понимания данного кусочка кода, вы должны разобраться лишь с переменной <a href="http://www.seifi.org/javascript/javascript-arguments.html">arguments</a>, и с методом <a href="http://odetocode.com/Blogs/scott/archive/2007/07/05/function-apply-and-function-call-in-javascript.aspx">apply</a> (тут им целые статьи посвятили &mdash; дерзайте)</p></blockquote>
<h3>О обработчиках событий</h3>
<p>Если ваш плагин вешает какой-либо обработчик, то лучше всего (читай всегда) данный обработчик повесить в своём собственном namespace:</p>
<pre class="brush: jscript; title: ; notranslate">
return this.bind(&quot;click.mySimplePlugin&quot;,function(){
    $(this).css('color', options.color);
});
</pre>
<p>Данный финт позволит в любой момент убрать все ваши обработчики, или вызвать только ваш, что очень удобно:</p>
<pre class="brush: jscript; title: ; notranslate">
// вызовем лишь наш обработчик
$('p').trigger(&quot;click.mySimplePlugin&quot;);

// убираем все наши обработчики
$('p').unbind(&quot;.mySimplePlugin&quot;);
</pre>
<h3>Использование data</h3>
<p>Если по какой-то причине вы еще не знакомы с <a href="http://api.jquery.com/data/">data</a> &mdash; то советую прочитать и усвоить незамедлительно. Если же в двух словах &mdash; это реестр данных, и все данные привязанные к какому-либо элементу лучше хранить в нем, это же правило касается и плагинов. Если вам надо сохранить состояние плагина &mdash; используйте data, если необходим кеш &mdash; используйте data, если вам необходимо сохранить &#8230; ну думаю понятно. Приведу еще примерчик связанный с инициализацией:</p>
<pre class="brush: jscript; title: ; notranslate">
// функция init
function() {
  var init = $(this).data('mySimplePlugin');

  if (init) {
    return this;
  } else {
    $(this).data('mySimplePlugin', true);
    return this.bind(&quot;click.mySimplePlugin&quot;,function(){
       $(this).css('color', options.color);
    });
  }
}
</pre>
<h3>Источники</h3>
<ul>
<li><a href="http://docs.jquery.com/Plugins/Authoring">Plugins/Authoring</a></li>
<li><a href="http://wintoni.us/post/123029056/jquery-plugin-patterns">jQuery Plugin Patterns</a></li>
<li><a href="http://www.learningjquery.com/2007/10/a-plugin-development-pattern">A Plugin Development Pattern</a></li>
<li><a href="http://aext.net/2010/02/learn-jquery-first-jquery-plugin-bubbleup/">Learning jQuery: Your First jQuery Plugin, &#8220;BubbleUP&#8221;</a></li>
</ul>
<h3>Цикл статей</h3>
<ol>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners/">jQuery для начинающих</a></li>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-2/">jQuery для начинающих. Часть 2. JavaScript Меню</a></li>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-ajax/">jQuery для начинающих. Часть 3. AJAX</a></li>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-selectors/">jQuery для начинающих. Часть 4. Селекторы</a></li>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-effects/">jQuery для начинающих. Часть 5. Эффекты</a></li>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-events/">jQuery для начинающих. Часть 6. События</a></li>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-write-plugins/"><strong>jQuery для начинающих. Часть 7. Пишем плагины</strong></a></li>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-filter-extend/">jQuery для начинающих. Часть 8. Расширяем фильтры</a></li>
</ol>
<h3  class="related_post_title">Другие посты на эту тему</h3><ul class="related_post"><li><a href="http://anton.shevchuk.name/php/php-jquery-cookbook/" title="PHP jQuery Cookbook">PHP jQuery Cookbook</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-write-animation-plugins/" title="jQuery для начинающих. Часть 9. Пишем плагины анимации">jQuery для начинающих. Часть 9. Пишем плагины анимации</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-filter-extend/" title="jQuery для начинающих. Часть 8. Расширяем фильтры">jQuery для начинающих. Часть 8. Расширяем фильтры</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-events/" title="jQuery для начинающих. Часть 6. События">jQuery для начинающих. Часть 6. События</a></li><li><a href="http://anton.shevchuk.name/javascript/a-sexy-images-jquery-plugin/" title="(a) Sexy Images jQuery плагин">(a) Sexy Images jQuery плагин</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-effects/" title="jQuery для начинающих. Часть 5. Эффекты">jQuery для начинающих. Часть 5. Эффекты</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-iphone-ui/" title="jQuery iPhone UI">jQuery iPhone UI</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-datagrid-plugins/" title="10 jQuery Datagrid плагинов">10 jQuery Datagrid плагинов</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://anton.shevchuk.name/javascript/jquery-for-beginners-write-plugins/feed/</wfw:commentRss>
		<slash:comments>19</slash:comments>
		</item>
		<item>
		<title>Мастер-классы по JavaScript&#8217;у</title>
		<link>http://anton.shevchuk.name/javascript/javascript-master-class/</link>
		<comments>http://anton.shevchuk.name/javascript/javascript-master-class/#comments</comments>
		<pubDate>Fri, 04 Jun 2010 07:59:47 +0000</pubDate>
		<dc:creator>Anton Shevchuk</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[announcement]]></category>

		<guid isPermaLink="false">http://anton.shevchuk.name/?p=1296</guid>
		<description><![CDATA[В ближайшем времени, в Харькове и Киеве, а также Екатеринбурге и Москве пройдут мастер-классы по JavaScript&#8217;у. Ведущий &#8212; Илья Кантор, создатель http://javascript.ru и http://algolist.manual.ru, в прошлом один из разработчиков фреймворка Dojo Toolkit, человек с большим опытом в создании сложных javascript-приложений. Темы Профессиональное javascript-программирование Сложный AJAX и COMET: тонкости Web 2.0 Мастер-класс по клиентской оптимизации Секреты [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://anton.shevchuk.name/wp-content/uploads/2010/06/javascript.jpg" alt="" title="JavaScript" width="400" height="400" class="aligncenter size-full wp-image-1302" /></p>
<p>В ближайшем времени, в Харькове и Киеве, а также Екатеринбурге и Москве пройдут мастер-классы по JavaScript&#8217;у. Ведущий &mdash; Илья Кантор, создатель <a href="http://javascript.ru">http://javascript.ru</a> и <a href="http://algolist.manual.ru">http://algolist.manual.ru</a>, в прошлом один из разработчиков фреймворка Dojo Toolkit, человек с большим опытом в создании сложных javascript-приложений.<br />
<span id="more-1296"></span></p>
<h3>Темы</h3>
<ul>
<li>Профессиональное javascript-программирование</li>
<li>Сложный AJAX и COMET: тонкости Web 2.0</li>
<li>Мастер-класс по клиентской оптимизации</li>
<li>Секреты jQuery</li>
</ul>
<h3>Расписание</h3>
<ul>
<li>Харьков &mdash; 19 и 20 июня</li>
<li>Екатеринбург &mdash; 26 и 27 июня</li>
<li>Москва &mdash; 17 и 18 июля</li>
<li>Киев &mdash; 24 и 25 июля</li>
</ul>
<p>Мероприятия платные, но стоимость не кусается. Узнать больше можете на странице <a href="http://javascript.ru/mk">http://javascript.ru/mk</a>. И да, коль накладок не будет &mdash; встретимся в Харькове ;)</p>
<h3  class="related_post_title">Другие посты на эту тему</h3><ul class="related_post"><li><a href="http://anton.shevchuk.name/php/zend-framework-day/" title="Zend Framework Day">Zend Framework Day</a></li><li><a href="http://anton.shevchuk.name/html-and-css/master-klass-yuriy-akella-artyukh/" title="Мастер-класс Юры Артюха">Мастер-класс Юры Артюха</a></li><li><a href="http://anton.shevchuk.name/internet/sphinx-master-class/" title="Мастер-класс по Sphinx">Мастер-класс по Sphinx</a></li><li><a href="http://anton.shevchuk.name/php/zfconf-2011-piter/" title="Конференция ZFConf 2011">Конференция ZFConf 2011</a></li><li><a href="http://anton.shevchuk.name/php/zfconf-ukraine/" title="ZFConf Ukraine">ZFConf Ukraine</a></li><li><a href="http://anton.shevchuk.name/javascript/javascript-master-class-feedback/" title="Анонс мастер-классов по JavaScript&#8217;у">Анонс мастер-классов по JavaScript&#8217;у</a></li><li><a href="http://anton.shevchuk.name/internet/pdfeed-rss-to-pdf-generator/" title="PDFEED &#8211; или RSS2PDF в новом обличье">PDFEED &#8211; или RSS2PDF в новом обличье</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://anton.shevchuk.name/javascript/javascript-master-class/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>jQuery для начинающих. Часть 6. События</title>
		<link>http://anton.shevchuk.name/javascript/jquery-for-beginners-events/</link>
		<comments>http://anton.shevchuk.name/javascript/jquery-for-beginners-events/#comments</comments>
		<pubDate>Wed, 02 Jun 2010 14:54:03 +0000</pubDate>
		<dc:creator>Anton Shevchuk</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://anton.shevchuk.name/?p=552</guid>
		<description><![CDATA[Прежде чем приступить к прочтению данной статьи, стоит определиться, что из себя представляют события &#8212; для web-странички, событиями будут являться любые действия пользователя &#8211; это и ввод данных с клавиатуры, и передвижения мышки, и конечно же &#8220;клики&#8221; &#8211; всё это и есть события. К событиям можно еще отнести события создаваемые скриптами, т.н. триггеры. Базовые события [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://anton.shevchuk.name/wp-content/uploads/2008/03/jquery-logo.gif" alt="" title="jQuery Logo" width="168" height="146" class="aligncenter size-medium wp-image-167" /></p>
<p>Прежде чем приступить к прочтению данной статьи, стоит определиться, что из себя представляют события &mdash; для web-странички, событиями будут являться любые действия пользователя &#8211; это и ввод данных с клавиатуры, и передвижения мышки, и конечно же &#8220;клики&#8221; &#8211; всё это и есть события. К событиям можно еще отнести события создаваемые скриптами, т.н. триггеры.<br />
<span id="more-552"></span></p>
<h3>Базовые события</h3>
<p>Начнем с азов. <a href="http://docs.jquery.com/Events">jQuery работает</a> практически со всеми событиями в JavaScript&#8217;е, приведу список функции с небольшими пояснениями:</p>
<ul>
<li><strong>change</strong> &mdash; измение значения элемента (значение, при потери фокуса, элемента отличается от изначального, при получении фокуса) </li>
<li><strong>click</strong> &mdash; клик по элементу (порядок событий &mdash; mousedown, mouseup, click) </li>
<li><strong>dblclick</strong> &mdash; двойной клик по элементу </li>
<li><strong>resize</strong> &mdash; изменение размеров элементов </li>
<li><strong>scroll</strong> &#8211; скроллинг элемента </li>
<li><strong>select</strong> &mdash; выбор текста (актуален только для input[type=text] и textarea)</li>
<li><strong>submit</strong> &mdash; отправка формы </li>
<li><strong>focus</strong> &mdash; фокус на элементе &#8211; актуально для input[type=text], но в современных браузерах работает и с другими элементами </li>
<li><strong>blur</strong> &mdash; фокус ушел с элемента &#8211; актуально для input[type=text] &mdash; срабатывает при клике по другому элементу на странице или по событию клавиатуры (к примеру переключение по tab&#8217;у) </li>
<li><strong>focusin</strong> &mdash; фокус на элементе, данное событие срабатывает на предке элемента, для которого произошло событие focus </li>
<li><strong>focusout</strong> &mdash; фокус ушел с элемента, данное событие срабатывает на предке элемента, для которого произошло событие blur </li>
<li><strong>keydown</strong> &mdash; нажатие клавиши на клавиатуре </li>
<li><strong>keypress</strong> &mdash; нажатие клавиши на клавиатуре (порядок событий &mdash; keydown, keypress, keyup) </li>
<li><strong>keyup</strong> &mdash; отжатие клавиши на клавиатуре </li>
<li><strong>load</strong> &mdash; загрузка элемента (img) </li>
<li><strong>unload</strong> &mdash; выгрузка элемента (window) </li>
<li><strong>mousedown</strong> &mdash; нажатие клавиши мыши </li>
<li><strong>mouseup</strong> &mdash; отжатие клавиши мыши </li>
<li><strong>mousemove</strong> &mdash; движение курсора </li>
<li><strong>mouseenter</strong> &mdash; наведение курсора на элемент, не срабатывает при переходе фокуса на дочерние элементы </li>
<li><strong>mouseleave</strong> &mdash; вывод курсора из элемента, не срабатывает при переходе фокуса на дочерние элементы </li>
<li><strong>mouseover</strong> &mdash; наведение курсора на элемент </li>
<li><strong>mouseout</strong> &mdash; вывод курсора из элемента</li>
</ul>
<p>Опробовать события можно на примере с <a href="http://anton.shevchuk.name/wp-demo/jquery-tutorials-6/mouse.html">событиями мышки</a> и <a href="http://anton.shevchuk.name/wp-demo/jquery-tutorials-6/form.html">элементами формы</a>.</p>
<p>Большинство из перечисленных событий можно имитировать непосредственно из самого скрипта: </p>
<pre class="brush: xml; title: ; notranslate">
&lt;script lang=&quot;javascript&quot;&gt; $('#menu li').click(); &lt;/script&gt;
</pre>
<h3>Триггеры</h3>
<p>Для работы с триггерами в jQuery предопределены следующие функции: </p>
<ul>
<li><strong>bind</strong> (type, data, fnc) &mdash; добавляет обработчик событий</li>
<li><strong>one</strong> (type, data, fnc) &mdash; добавляет обработчик событий, который сработает лишь раз</li>
<li><strong>trigger</strong> (event, data) &mdash; вызывает обработчики событий</li>
<li><strong>triggerHandler</strong>( event, data) &mdash; вызывает обработчики событий, без вызова события браузера</li>
<li><strong>unbind</strong> (type, fnc) &mdash; удаляет все обработчики событий с элемента</li>
</ul>
<pre class="brush: jscript; title: ; notranslate">
$('.class').bind('click', function(){
    // что-то делаем
});

// вызываем обработчик
$('.class').trigger('click');

// отключаем обработчик
$('.class').unbind('click');
</pre>
<p>Можно повесить триггер почти на любой объект: </p>
<pre class="brush: jscript; title: ; notranslate">
var obj = {
    test:function() {
        console.log('obj.test');
    }
}

$(obj).bind('someEvent', function(){
   console.log('obj.someEvent');
   this.test();
});

$(obj).trigger('someEvent');
</pre>
<h3>Пространство имен</h3>
<p>Обычно, когда мы хотим создать/удалить свой обработчик событий, мы пишем следующий код (чуть выше я об этом уже писал): </p>
<pre class="brush: jscript; title: ; notranslate">
// создаем свой обработчик
$('.class').bind('click', function(){
    // что-то делаем
});
// удаляем все обработчики
$('.class').unbind();
</pre>
<p>Но как всегда, есть ситуации когда нам необходимо отключить не все обработчики (как пример, надо отключить обработку какого-то контрола определенным плагином), в этом случае нам на помощь приходят пространства имен, использовать их достаточно легко: </p>
<pre class="brush: jscript; title: ; notranslate">
// создаем обработчик
$('.class').bind('click.namespace', function(){
    // что-то делаем
});

// вызываем обработчик
$('.class').trigger('click.namespace');

// удаляем все обработчики в данном пространстве имен
$('.class').unbind('click.namespace');
</pre>
<p>Еще примерчик, вешаем обработчик, который выводит текст в консоль. </p>
<pre class="brush: jscript; title: ; notranslate">
$('.class').bind('click.namespace', function(){
    console.log('bang');
});
$('.class').trigger('click.namespace'); // вызываем событие, наш обработчик сработает
$('.class').trigger('click');           // тоже работает
$('.class').trigger('click.other');     // событие из другого пространства имен, наш обработчик не будет вызван
</pre>
<p>Так же, есть поддержка нескольких пространств имен (с версии 1.3, если быть точным): </p>
<pre class="brush: jscript; title: ; notranslate">
$('.class').bind('click.a.b', function(){
    // для пространств a и b
});
$('.class').trigger('click.a'); // вызываем обработчик из пространства a
$('.class').unbind('click.b');  // отменяем обработчик для пространства b
</pre>
<p>Можно одним махом удалить все обработчики с определенного пространства имен: </p>
<pre class="brush: jscript; title: ; notranslate">
$('.class').bind('click.namespace', function(){}); // обработчик клика
$('.class').bind('blur.namespace', function(){});  // обработчик фокус
$('.class').unbind('.namespace'); // передумали, и все отменили
</pre>
<ul>
<li><a href="http://anton.shevchuk.name/wp-demo/jquery-tutorials-6/triggers.html">Пример использование триггеров и пространства имен</a></li>
<li><a href="http://docs.jquery.com/Namespaced_Events">Официальная документация по пространствам имен</a></li>
</ul>
<h3>Работа с live</h3>
<p>Очень часто при генерации страниц AJAX&#8217;ом возникает необходимость повесить обработчики событий на новые элементы в DOMе, для этой цели служит метод <a href="http://docs.jquery.com/Events/live">live</a> (первый параметр &mdash; имя события, второй &mdash; обработчик). </p>
<p>Приведу пример &mdash; есть следующая задача:</p>
<ol>
<li>все внутренние ссылки на страницы должны подгружать контент AJAXом в элемент с id=content </li>
<li>правило №1 верно для ссылок из подгруженного контента</li>
</ol>
<p>Реализовать первое требование достаточно просто: </p>
<pre class="brush: jscript; title: ; notranslate">
$('a[href^=/]').click(function(event){
    $('#content').load($(this).attr('href'));
    return false; // эквивалентно вызову event.preventDefault(); и event.stopPropagation();
});
</pre>
<p>Выполнить второе правило тоже легко, достаточно слегка модифицировать предыдущий пример: </p>
<pre class="brush: jscript; title: ; notranslate">
$('a[href^=/]').live('click', function(event){
    $('#content').load($(this).attr('href'));
    event.preventDefault(); // отменяем действие по умолчанию, но не трогаем bubbling - чтобы не мешать другим обработчикам
});
</pre>
<blockquote><p>
<strong>Поддерживаемые события</strong>: click, dblclick,  mousedown, mouseup, mousemove, mouseover, mouseout, keydown, keypress, keyup, change, submit, focusin, focusout, mouseenter,  mouseleave<br />
<strong>Нет поддержки</strong>: blur, focus, change, submit
</p></blockquote>
<p>В противовес функции <a href="http://docs.jquery.com/Events/live">live</a> существует функция <a href="http://docs.jquery.com/Events/die">die</a> &mdash; она убирает отслеживание событий для вновь появившихся элементов.</p>
<p>Хотелось бы отдельно остановиться на принципе работы метода <em>live</em> &mdash; данный метод работает благодаря т.н. &laquo;всплытию&raquo; событий в DOMе &mdash; т.е. когда вы кликаете на любом элементе на странице, то в первую очередь событие сработает на этом элементе, если обработчика события нет, или он не говорит, что дальше &laquo;всплывать&raquo; не надо (сказать он это может так <em>event.stopPropagation()</em>), то событие идет вверх по DOM дереву и инициализирует обработчики у родительского элемента, и так далее пока не достигнет корня &mdash; document&#8217;a. Таким образом метод <em>live</em> вешает обработчик событий на document, и затем анализирует каждое событие на предмет наличия элементов описанных вами. Это конечно не самый быстрый способ, но его можно ускорить используя метод <a href="http://api.jquery.com/delegate/">delegate</a>, его отличие от live только в том, что вы указываете элемент на который вешаете обработчик &laquo;live&raquo; событий, таким образом пример выше можно записать так:</p>
<pre class="brush: jscript; title: ; notranslate">
$('#content').delegate('a[href^=/]', 'click', function(event){
    $('#content').load($(this).attr('href'));
    event.preventDefault();
});
</pre>
<h3>Touch события</h3>
<p>Смартфоны с большим сенсорным экраном &mdash; это уже не редкость, вот и web-индустрия начинает адаптироваться под пользователей того же iPhone, если же вам понадобилось подобная адаптация, то вам пригодится следующая информация: </p>
<p>События в JavaScript&#8217;е: </p>
<ul>
<li><strong>touchstart</strong> &mdash; аналогично mousedown</li>
<li><strong>touchmove</strong> &mdash; mousemove </li>
<li><strong>touchend</strong> &mdash; mouseup </li>
<li><strong>touchcancel</strong> &mdash; аналогии нет</li>
</ul>
<p>О том как с ними работать, можно подчерпнуть из статей <a href="http://rossboucher.com/2008/08/19/iphone-touch-events-in-javascript/">iPhone Touch Events in JavaScript</a> и <a href="http://www.sitepen.com/blog/2008/07/10/touching-and-gesturing-on-the-iphone/">Touching and Gesturing on the iPhone</a></p>
<p>Если же вернутся к CSS, то для того же iPhone подключение внешнего CSS файла будет выглядеть следующим образом: </p>
<pre class="brush: xml; title: ; notranslate">
&lt;link rel=&quot;stylesheet&quot; href=&quot;iphone.css&quot; type='text/css' media='only screen and (max-device-width: 480px)' /&gt;
</pre>
<p>Или в самом CSS: </p>
<pre class="brush: css; title: ; notranslate">
@media only screen and (max-device-width: 480px) {
  .sidebar {
    display: none;
  }
}
</pre>
<p>Подробнее читайте в статье <a href="http://www.sitepoint.com/article/iphone-development-12-tips/">iPhone Development: 12 Tips To Get You Started</a> (<a href="http://interpretor.ru/iphone_dev">перевод был тут</a>)</p>
<h3>Цикл статей</h3>
<ol>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners/">jQuery для начинающих</a></li>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-2/">jQuery для начинающих. Часть 2. JavaScript Меню</a></li>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-ajax/">jQuery для начинающих. Часть 3. AJAX</a></li>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-selectors/">jQuery для начинающих. Часть 4. Селекторы</a></li>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-effects/">jQuery для начинающих. Часть 5. Эффекты</a></li>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-events/">jQuery для начинающих. Часть 6. События</a></li>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-write-plugins/">jQuery для начинающих. Часть 7. Пишем плагины</a></li>
<li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-filter-extend/">jQuery для начинающих. Часть 8. Расширяем фильтры</a></li>
</ol>
<h3  class="related_post_title">Другие посты на эту тему</h3><ul class="related_post"><li><a href="http://anton.shevchuk.name/php/php-jquery-cookbook/" title="PHP jQuery Cookbook">PHP jQuery Cookbook</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-write-animation-plugins/" title="jQuery для начинающих. Часть 9. Пишем плагины анимации">jQuery для начинающих. Часть 9. Пишем плагины анимации</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-filter-extend/" title="jQuery для начинающих. Часть 8. Расширяем фильтры">jQuery для начинающих. Часть 8. Расширяем фильтры</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-write-plugins/" title="jQuery для начинающих. Часть 7. Пишем плагины">jQuery для начинающих. Часть 7. Пишем плагины</a></li><li><a href="http://anton.shevchuk.name/javascript/a-sexy-images-jquery-plugin/" title="(a) Sexy Images jQuery плагин">(a) Sexy Images jQuery плагин</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-for-beginners-effects/" title="jQuery для начинающих. Часть 5. Эффекты">jQuery для начинающих. Часть 5. Эффекты</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-iphone-ui/" title="jQuery iPhone UI">jQuery iPhone UI</a></li><li><a href="http://anton.shevchuk.name/javascript/jquery-datagrid-plugins/" title="10 jQuery Datagrid плагинов">10 jQuery Datagrid плагинов</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://anton.shevchuk.name/javascript/jquery-for-beginners-events/feed/</wfw:commentRss>
		<slash:comments>33</slash:comments>
		</item>
		<item>
		<title>Сравниваем производительность JavaScript фреймворков</title>
		<link>http://anton.shevchuk.name/javascript/compare-perfomance-javascript-frameworks/</link>
		<comments>http://anton.shevchuk.name/javascript/compare-perfomance-javascript-frameworks/#comments</comments>
		<pubDate>Mon, 31 May 2010 09:06:05 +0000</pubDate>
		<dc:creator>Anton Shevchuk</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Perfomance]]></category>

		<guid isPermaLink="false">http://anton.shevchuk.name/?p=1252</guid>
		<description><![CDATA[Давненько я не сравнивал производительность JavaScript фреймворков, пора это исправить, и обновить данные двухгодичной давности. В качестве инструмента я взял тест SlickSpeed и установил на свой хостинг, в списке испытуемых оказались следующие фреймворки: Dojo 1.4.3 JQuery 1.4.2 MooTools 1.2.4 Prototype 1.6.1 и 1.7.2 ExtJS Core 3.1 YUI 3.1.1 А так же браузеры, установленные на моей [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://anton.shevchuk.name/wp-content/uploads/2008/11/speedometer.jpg" alt="" title="Speedometer" width="350" height="284" class="aligncenter size-full wp-image-648" /><br />
Давненько я не сравнивал производительность JavaScript фреймворков, пора это исправить, и обновить <a href="http://anton.shevchuk.name/javascript/perfomance-javascript-frameworks/">данные двухгодичной давности</a>.<br />
<span id="more-1252"></span><br />
В качестве инструмента я взял тест SlickSpeed и <a href="http://slickspeed.hohli.com/">установил на свой хостинг</a>, в списке испытуемых оказались следующие фреймворки:</p>
<ul>
<li><a href="http://dojotoolkit.org/">Dojo 1.4.3</a></li>
<li><a href="http://jquery.com/">JQuery 1.4.2</a></li>
<li><a href="http://mootools.net/">MooTools 1.2.4</a></li>
<li><a href="http://www.prototypejs.org/">Prototype 1.6.1 и 1.7.2</a></li>
<li><a href="http://www.extjs.com/products/core/">ExtJS Core 3.1</a></li>
<li><a href="http://developer.yahoo.com/yui/3/">YUI 3.1.1</a></li>
</ul>
<p>А так же браузеры, установленные на моей системе:</p>
<ul>
<li>Mozilla Firefox 3.6.3</li>
<li>Opera 10.53</li>
<li>Google Chrome 5.0.375.55 beta</li>
<li>Safari 4.0.3</li>
<li>Internet Explorer 8.0</li>
</ul>
<h3>Результаты тестирования</h3>
<p>Все тесты проводил 5 раз &mdash; для достоверности (т.е. с учетом алгоритма тестирование, результат &mdash; среднее значение от 30 запусков). Все расширения браузеров были отключены. Результаты приведены в миллисекундах.</p>
<table cellspacing="1" class="inline">
<tr>
<th width="160px"></th>
<th width="80px">Dojo 1.4.3</th>
<th width="80px">jQuery 1.4.2</th>
<th width="80px">MooTools 1.2.4</th>
<th width="80px">Prototype 1.6.1</th>
<th width="80px">Prototype 1.7.2</th>
<th width="80px">ExtJS Core 3.1</th>
<th width="80px">YUI 3.1.1</th>
<th width="80px">Avg</th>
</tr>
<tr>
<th>Firefox 3.6.3</th>
<td align="center">62,6</td>
<td align="center">52,6</td>
<td align="center">89,4</td>
<td align="center">68,4</td>
<td align="center">42,8</td>
<td align="center">109,2</td>
<td align="center">54,6</td>
<th>68,51</th>
</tr>
<tr>
<th>Opera 10.53</th>
<td align="center">37,2</td>
<td align="center">33,2</td>
<td align="center">61,6</td>
<td align="center">45,2</td>
<td align="center">34,2</td>
<td align="center">70,2</td>
<td align="center">35,6</td>
<th>45,31</th>
</tr>
<tr>
<th>Google Chrome 5.0</th>
<td align="center">18,20</td>
<td align="center">13,60</td>
<td align="center">59,40</td>
<td align="center">24,60</td>
<td align="center">13,60</td>
<td align="center">45,00</td>
<td align="center">19,60</td>
<th>27,71</th>
</tr>
<tr>
<th>Safari 4.0.3</th>
<td align="center">19,00</td>
<td align="center">14,80</td>
<td align="center">56,40</td>
<td align="center">20,20</td>
<td align="center">13,00</td>
<td align="center">64,80</td>
<td align="center">18,40</td>
<th>29,51</th>
</tr>
<tr>
<th>Internet Explorer 8.0</th>
<td align="center">106,40</td>
<td align="center">97,00</td>
<td align="center">371,80</td>
<td align="center">559,40</td>
<td align="center">135,80</td>
<td align="center">276,60</td>
<td align="center">518,40</td>
<th>295,06</th>
</tr>
<tr>
<th>Avg</th>
<th>48,68</th>
<th>42,24</th>
<th>127,72</th>
<th>143,56</th>
<th>47,88</th>
<th>113,16</th>
<th>129,32</th>
<th></th>
</tr>
</table>
<p>Сводный график полученных данных:<br />
<img src="http://chart.apis.google.com/chart?cht=bvg&#038;chs=700x400&#038;chd=s:EDGFDHE,CBGCBFC,GFJHELF,LKm5Oc1,CBGCBHC&#038;chdl=Opera%2010.53|Google%20Chrome%205.0|Firefox%203.6.3|IE%208.0|Safari%204.0.3&#038;chco=fa0000,7a7aff,ff9900,0000ff,8a8a8a,CCFFFF,CCFF00,CCCCCC&#038;chxt=y,x&#038;chxl=1:|Dojo|jQuery|MooTools|Prototype%201.6.1|Prototype%201.7.2|ExtJS%20Core|YUI&#038;chxs=0,00066e,10,-1|1,001c69,10,0&#038;chxr=0,0,600&#038;chf=bg,ls,90,FFFFFF,0.1,FFFFFF,0.1|c,s,FFFFFF&#038;chbh=10,2,20&#038;chg=14.3,10,6,2,0,0&#038;chtt=JavaScript%20Framework%20Perfomance&#038;chts=000000,12&#038;max=600&#038;agent=hohli.com" alt="Chart build on http://charts.hohli.com" class="aligncenter"/></p>
<p>Отбросим IE, а то он сильно портит картину:<br />
<img src="http://chart.apis.google.com/chart?cht=bvg&#038;chs=700x400&#038;chd=s:WRhbRmW,LGhLGbL,hbxmW8b,LGhLGmL&#038;chdl=Opera%2010.53|Google%20Chrome%205.0|Firefox%203.6.3|Safari%204.0.3&#038;chco=fa0000,7a7aff,ff9900,8a8a8a,CCFFFF,CCFF00,CCCCCC&#038;chxt=y,x&#038;chxl=1:|Dojo|jQuery|MooTools|Prototype%201.6.1|Prototype%201.7.2|ExtJS%20Core|YUI&#038;chxs=0,00066e,10,-1|1,001c69,10,0&#038;chxr=0,0,600&#038;chf=bg,ls,90,FFFFFF,0.1,FFFFFF,0.1|c,s,FFFFFF&#038;chbh=14,1,20&#038;chg=14.3,10,6,2,0,0&#038;chtt=JavaScript%20Framework%20Perfomance&#038;chts=000000,12&#038;max=110&#038;agent=hohli.com" alt="Chart build on http://charts.hohli.com" class="aligncenter" /></p>
<h3>Выводы</h3>
<p>Самым быстрым фреймворком оказывается jQuery:</p>
<ol>
<li>jQuery 1.4.2 &mdash; 42,24 мс</li>
<li>Prototype 1.7.2 &mdash; 47,88 мс</li>
<li>Dojo 1.4.3 &mdash; 48,68 мс</li>
<li>ExtJs Core 3.1.0 &mdash; 113,16 мс</li>
<li>MooTools 1.2.4 &mdash; 127,72 мс</li>
<li>YUI 3.1.1 &mdash; 129,32 мс</li>
<li>Prototype 1.6.1 &mdash; 143,56 мс</li>
</ol>
<p>И в &laquo;медальном зачете&raquo; картина та же &mdash; Prototype совсем чуть-чуть отстает:</p>
<table cellspacing="1" class="inline">
<tr>
<th width="160px"></th>
<th width="80px">Dojo 1.4.3</th>
<th width="80px">jQuery 1.4.2</th>
<th width="80px">MooTools 1.2.4</th>
<th width="80px">Prototype 1.6.1</th>
<th width="80px">Prototype 1.7.2</th>
<th width="80px">ExtJS Core 3.1</th>
<th width="80px">YUI 3.1.1</th>
</tr>
<tr>
<th>Firefox 3.6.3</th>
<td align="center">4</td>
<td align="center">2</td>
<td align="center">6</td>
<td align="center">5</td>
<td align="center">1</td>
<td align="center">7</td>
<td align="center">3</td>
</tr>
<tr>
<th>Opera 10.53</th>
<td align="center">4</td>
<td align="center">1</td>
<td align="center">6</td>
<td align="center">5</td>
<td align="center">2</td>
<td align="center">7</td>
<td align="center">3</td>
</tr>
<tr>
<th>Chrome 5.0</th>
<td align="center">3</td>
<td align="center">1</td>
<td align="center">7</td>
<td align="center">5</td>
<td align="center">1</td>
<td align="center">6</td>
<td align="center">4</td>
</tr>
<tr>
<th>Safari 4.0.3</th>
<td align="center">4</td>
<td align="center">2</td>
<td align="center">6</td>
<td align="center">5</td>
<td align="center">1</td>
<td align="center">7</td>
<td align="center">3</td>
</tr>
<tr>
<th>IE 8.0</th>
<td align="center">2</td>
<td align="center">1</td>
<td align="center">5</td>
<td align="center">7</td>
<td align="center">3</td>
<td align="center">4</td>
<td align="center">6</td>
</tr>
<tr>
<th>Total</th>
<th>17</th>
<th>7</th>
<th>30</th>
<th>27</th>
<th>8</th>
<th>31</th>
<th>19</th>
</tr>
</table>
<p>А среди самых быстрых браузеров &mdash; Google Chrome, на пятки наступает Safari:</p>
<ol>
<li>Google Chrome 5.0 &mdash; 27,71 мс</li>
<li>Safari 4.0.3 &mdash; 29,51 мс</li>
<li>Opera 10.53 &mdash; 45,31 мс</li>
<li>Mozilla Firefox 3.6.3 &mdash; 68,51 мс</li>
<li>Internet Explorer 8.0 &mdash; 295,06 мс</li>
</ol>
<p>А в &laquo;медальном зачете&raquo; они обменялись местами:</p>
<table cellspacing="1" class="inline">
<tr>
<th width="160px"></th>
<th width="80px">Dojo</th>
<th width="80px">jQuery</th>
<th width="80px">MooTools</th>
<th width="80px">Prototype</th>
<th width="80px">Prototype</th>
<th width="80px">ExtJS Core</th>
<th width="80px">YUI</th>
<th width="80px">Total</th>
</tr>
<tr>
<th>Firefox 3.6.3</th>
<td align="center">4</td>
<td align="center">4</td>
<td align="center">4</td>
<td align="center">4</td>
<td align="center">4</td>
<td align="center">4</td>
<td align="center">4</td>
<th>28</th>
</tr>
<tr>
<th>Opera 10.53</th>
<td align="center">3</td>
<td align="center">3</td>
<td align="center">3</td>
<td align="center">3</td>
<td align="center">3</td>
<td align="center">3</td>
<td align="center">3</td>
<th>21</th>
</tr>
<tr>
<th>Chrome 5.0</th>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">2</td>
<td align="center">2</td>
<td align="center">2</td>
<td align="center">1</td>
<td align="center">2</td>
<th>11</th>
</tr>
<tr>
<th>Safari 4.0.3</th>
<td align="center">2</td>
<td align="center">2</td>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">2</td>
<td align="center">1</td>
<th>10</th>
</tr>
<tr>
<th>IE 8.0</th>
<td align="center">5</td>
<td align="center">5</td>
<td align="center">5</td>
<td align="center">5</td>
<td align="center">5</td>
<td align="center">5</td>
<td align="center">5</td>
<th>35</th>
</tr>
<tr>
</table>
<p>P.S. Если хотите &#8211; добавлю еще фреймворки для сравнения, оставляйте ссылку и название функции для тестирования&#8230;</p>
<h3  class="related_post_title">Другие посты на эту тему</h3><ul class="related_post"><li><a href="http://anton.shevchuk.name/javascript/perfomance-javascript-frameworks/" title="Производительность JavaScript фреймворков">Производительность JavaScript фреймворков</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://anton.shevchuk.name/javascript/compare-perfomance-javascript-frameworks/feed/</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
	</channel>
</rss>

