<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Web Energy]]></title><description><![CDATA[Блог о современных технологиях в веб-разработке]]></description><link>http://simonenko.su</link><image><url>http://simonenko.su/i/logo.jpg</url><title>Web Energy</title><link>http://simonenko.su</link></image><generator>Web Energy</generator><lastBuildDate>Thu, 09 Apr 2026 06:36:21 GMT</lastBuildDate><atom:link href="http://simonenko.su/rss" rel="self" type="application/rss+xml"/><author><![CDATA[Алексей Симоненко]]></author><item><title><![CDATA[Плавное проявление, мгновенное затухание]]></title><description><![CDATA[<div></div><p>Илья Бирман вчера <a href="http://ilyabirman.ru/meanwhile/all/hover-fade/" target="_blank" rel="nofollow">написал</a>:</p>
<blockquote>
<div>Важная штука тут в том, чтобы делать плавным только затухание — тогда эффект получится действительно приятным.</div>
</blockquote>
<p>Плавное изменение свойств это здорово, но оно не должно мешать работе с сайтом. Илья предлагает делать плавным только затухание, вот его аргументы:</p>
<blockquote>
<div>Исходный смысл подсветки — обратная связь, ощущение, что всё работает, реагирует, не зависло и не заглючило. Если подсветка срабатывает не мгновенно, а лениво, это сразу воспринимается как тупняк, а не как приятный эффект. Точно так же бесит, когда некоторые светильники не сразу включаются, когда жмёшь кнопку, а плавно.</div>
</blockquote>

<p>Мне кажется, что вместо плавного затухания следует делать плавное проявление. Плавное затухание плохо потому, что когда я перехожу на новый элемент, я меняю свой <strong>фокус внимания</strong>. Если оставить плавное затухание, то будет ощущение, что предыдущий элемент меня не отпускает. Это отвлекает.</p>
<p>За примером из жизни далеко ходить не приходится. Дома у меня телевизор с подсветкой. После выключения телевизора подсветка горит ещё пять секунд и плавно затухает. А ведь я выключил телевизор. Я не хочу никаких плавных действий. Я хочу, чтобы действие произошло. Прямо сейчас.</p>
<p>Однако стоит отметить, что аргумент с «тупняком» тоже правильный. И я советую делать плавное проявление с минимально возможным временем анимации. Например, 0.1−0.2 секунды.</p>]]></description><link>http://simonenko.su/71249012903/hover-fade</link><guid isPermaLink="true">http://simonenko.su/71249012903/hover-fade</guid><dc:creator><![CDATA[Алексей Симоненко]]></dc:creator><pubDate>Fri, 27 Dec 2013 00:01:00 GMT</pubDate></item><item><title><![CDATA[Пульс твиттера о веб-разработке]]></title><description><![CDATA[<div></div><p><img align="left" alt="Пульс твиттера о веб-разработке" height="216" src="http://simonenko.su/i/u/pulse.png" title="Пульс твиттера о веб-разработке" width="216" itemprop="image"> Наверное, уже <a href="http://simonenko.su/20775191979/nodejs-twitter-stream-websockets">прошёл год</a> с тех пор, как я написал скрипт для трансляции сообщений из Twitter. Изначально я это делал для маркетинговой конференции <a href="http://digitaleconf.ru/twitter">Digitale</a>, но востребованность трансляции оказалась намного выше. И в качестве очередного примера я добавил трансляцию сообщений о веб-разработке. Но об этом чуть ниже. Потому что начать я хотел бы с грустного.</p>

<h2>Twitter API 1.1</h2>
<p><a href="http://dev.twitter.com/blog/api-v1-is-retired" target="_blank" rel="nofollow">Twitter закрыл первую версию своего API</a>. Все запросы к старой версии будут возвращать статус HTTP 410 Gone. Главное изменение заключается в авторизации. Теперь работает только OAuth авторизация. Ну что же, давайте переделаем нашу трансляцию.</p>
<p class="notice">Я не хочу ничего переделывать, <a href="http://simonenko.su/tools/twitter/">я хочу смотреть демонстрацию</a>.</p>
<h3>Twitter Streaming API</h3>
<p>Для начала, выделим код работы с <a href="http://dev.twitter.com/docs/streaming-apis" target="_blank" rel="nofollow">Twitter Streaming API</a> в отдельную функцию.</p>
<pre><code class="coffeescript">http = <span class="hljs-built_in">require</span> <span class="hljs-string">'request'</span>

<span class="hljs-built_in">module</span>.<span class="hljs-function"><span class="hljs-title">exports</span> = <span class="hljs-params">(track, oauth, fn)</span> -&gt;</span>
  params =
    <span class="hljs-attribute">url</span>: <span class="hljs-string">'https://stream.twitter.com/1.1/statuses/filter.json'</span>
    <span class="hljs-attribute">oauth</span>: oauth
    <span class="hljs-attribute">form</span>:
      <span class="hljs-attribute">include_entities</span>: <span class="hljs-literal">true</span>
      <span class="hljs-attribute">track</span>: track

  request = http.post params, <span class="hljs-function"><span class="hljs-params">(error)</span> -&gt;</span>
    fn error, <span class="hljs-literal">false</span> <span class="hljs-keyword">if</span> error

  request.<span class="hljs-literal">on</span> <span class="hljs-string">'data'</span>, <span class="hljs-function"><span class="hljs-params">(buffer)</span> -&gt;</span>
    <span class="hljs-keyword">try</span>
      tweet = JSON.parse buffer.toString()
    <span class="hljs-keyword">catch</span> error
      tweet = <span class="hljs-literal">false</span>

    <span class="hljs-keyword">if</span> tweet
      data =
        <span class="hljs-attribute">id</span>: tweet.id_str
        <span class="hljs-attribute">link</span>: <span class="hljs-string">"http://twitter.com/<span class="hljs-subst">#{tweet.user.screen_name}</span>"</span>
        <span class="hljs-attribute">avatar</span>: tweet.user.profile_image_url
        <span class="hljs-attribute">login</span>: tweet.user.screen_name
        <span class="hljs-attribute">name</span>: tweet.user.name <span class="hljs-keyword">or</span> tweet.user.screen_name
        <span class="hljs-attribute">text</span>: tweet.text

      fn <span class="hljs-literal">null</span>, data</code></pre>
<p>Как видите, я решил использовать <a href="http://github.com/mikeal/request" target="_blank" rel="nofollow">request</a> модуль. С ним будет проще проходить OAuth авторизацию и разбирать ответ сервера. Вызов функции будет выглядеть так.</p>
<pre><code class="coffeescript">oauth =
  <span class="hljs-attribute">consumer_key</span>: <span class="hljs-string">'consumer key'</span>
  <span class="hljs-attribute">consumer_secret</span>: <span class="hljs-string">'consumer secret'</span>
  <span class="hljs-attribute">token</span>: <span class="hljs-string">'oauth token'</span>
  <span class="hljs-attribute">token_secret</span>: <span class="hljs-string">'token secret'</span>
 
filter = <span class="hljs-built_in">require</span> <span class="hljs-string">'./twitter/filter'</span>
filter <span class="hljs-string">'html5,css3'</span>, oauth, <span class="hljs-function"><span class="hljs-params">(error, tweet)</span> -&gt;</span>
  <span class="hljs-built_in">console</span>.log tweet <span class="hljs-keyword">unless</span> error</code></pre>
<p>Остаётся только передавать все новые сообщения клиентам. Например, это можно сделать с помощью <a href="http://socket.io" target="_blank" rel="nofollow">socket.io</a>.</p>
<pre><code class="coffeescript">clients = []

io = <span class="hljs-built_in">require</span>(<span class="hljs-string">'socket.io'</span>).listen(<span class="hljs-number">8080</span>)
io.sockets.<span class="hljs-literal">on</span> <span class="hljs-string">'connection'</span>, <span class="hljs-function"><span class="hljs-params">(socket)</span> -&gt;</span>
  clients.push socket

oauth =
  <span class="hljs-attribute">consumer_key</span>: <span class="hljs-string">'consumer key'</span>
  <span class="hljs-attribute">consumer_secret</span>: <span class="hljs-string">'consumer secret'</span>
  <span class="hljs-attribute">token</span>: <span class="hljs-string">'oauth token'</span>
  <span class="hljs-attribute">token_secret</span>: <span class="hljs-string">'token secret'</span>

<span class="hljs-function"><span class="hljs-title">send</span> = <span class="hljs-params">(tweet)</span> -&gt;</span>
  client.send JSON.stringify tweet <span class="hljs-keyword">for</span> client <span class="hljs-keyword">in</span> clients

filter = <span class="hljs-built_in">require</span> <span class="hljs-string">'./twitter/filter'</span>
filter <span class="hljs-string">'html5,css3'</span>, oauth, <span class="hljs-function"><span class="hljs-params">(error, tweet)</span> -&gt;</span>
  send tweet <span class="hljs-keyword">unless</span> error</code></pre>
<h3>OAuth авторизация для Twitter API</h3>
<p>Осталось получить OAuth ключи и можно запускать. Я думал, что этот процесс достаточно простой, но вчера увидел <a href="http://twitter.com/miripiruni/status/346731801508732929" target="_blank" rel="nofollow">сообщение от @miripiruni</a> и решил подробнее остановиться на этой задаче. Так как трансляция будет работать на серверной стороне, то нам будет достаточно девелоперских ключей.</p>
<figure><a class="link" href="http://simonenko.su/i/u/new-oauth-app.jpg" target="_blank"><img alt="Новое OAuth приложение для Twitter" height="421" src="http://simonenko.su/i/u/new-oauth-app-thumb.jpg" title="Новое OAuth приложение для Twitter" width="500" itemprop="image"></a> <figcaption>Заходим на сайт для разработчиков и <a href="http://dev.twitter.com/apps/new" target="_blank" rel="nofollow">создаём новое приложение</a>.</figcaption></figure>
<figure><a class="link" href="http://simonenko.su/i/u/consumer-key.jpg" target="_blank"><img alt="OAuth ключи для Twitter" height="421" src="http://simonenko.su/i/u/consumer-key-thumb.jpg" title="OAuth ключи для Twitter" width="500" itemprop="image"></a> <figcaption>Получаем «consumer key» и «consumer secret» ключи.</figcaption></figure>
<figure><a class="link" href="http://simonenko.su/i/u/access-token.jpg" target="_blank"><img alt="Access token для OAuth" height="421" src="http://simonenko.su/i/u/access-token-thumb.jpg" title="Access token для OAuth" width="500" itemprop="image"></a> <figcaption>Создаём девелоперский «access token» ключ.</figcaption></figure>
<p>Итак, у нас есть все четыре ключа: «consumer key», «consumer secret», «access token» и «access token secret».</p>
<h3>Вывод через socket.io</h3>
<p>Теперь дело за клиентской частью. Простой вариант вывода сообщений трансляции на страницу.</p>
<pre><code class="javascript"><span class="hljs-list">(<span class="hljs-title">function</span><span class="hljs-list">(<span class="hljs-title">window</span>, document)</span> <span class="hljs-collection">{
  'use strict'<span class="hljs-comment">;</span>

  var socket = window.io.connect<span class="hljs-list">(<span class="hljs-title">'http</span><span class="hljs-attribute">://127.0.0.1</span><span class="hljs-attribute">:8080'</span>)</span>,

  // Получаем шаблон
  template = document.getElementById<span class="hljs-list">(<span class="hljs-title">'twitter-template'</span>)</span>.innerHTML,

  // Контейнер со всеми сообщениями
  container = document.getElementById<span class="hljs-list">(<span class="hljs-title">'twitter-feed'</span>)</span>,

  messages = <span class="hljs-collection">[]</span><span class="hljs-comment">;</span>

  // Все новые сообщения добавляем в массив messages
  socket.on<span class="hljs-list">(<span class="hljs-title">'connect'</span>, function<span class="hljs-list">()</span> <span class="hljs-collection">{
    socket.on<span class="hljs-list">(<span class="hljs-title">'message'</span>, function<span class="hljs-list">(<span class="hljs-title">result</span>)</span> <span class="hljs-collection">{
      messages.push<span class="hljs-list">(<span class="hljs-title">JSON.parse</span><span class="hljs-list">(<span class="hljs-title">result</span>)</span>)</span><span class="hljs-comment">;</span>
    }</span>)</span><span class="hljs-comment">;</span>
  }</span>)</span><span class="hljs-comment">;</span>

  // Каждые<span class="hljs-number"> 2</span> секунды берём первое сообщение из массива и показываем
  window.setInterval<span class="hljs-list">(<span class="hljs-title">function</span><span class="hljs-list">()</span> <span class="hljs-collection">{
    if <span class="hljs-list">(<span class="hljs-title">messages.length</span> &gt;<span class="hljs-number"> 0</span>)</span> <span class="hljs-collection">{
      var message = messages.shift<span class="hljs-list">()</span>,

      // Получаем все сообщения в ленте
      tweets = container.querySelectorAll<span class="hljs-list">(<span class="hljs-title">'section'</span>)</span>,
      length = tweets.length,

      section = document.createElement<span class="hljs-list">(<span class="hljs-title">'section'</span>)</span><span class="hljs-comment">;</span>

      // Создаём HTML шаблон для нового сообщения
      section.innerHTML = mustache<span class="hljs-list">(<span class="hljs-title">template</span>, message)</span><span class="hljs-comment">;</span>

      if <span class="hljs-list">(<span class="hljs-title">length</span> &gt;<span class="hljs-number"> 0</span>)</span> <span class="hljs-collection">{
        container.insertBefore<span class="hljs-list">(<span class="hljs-title">section</span>, tweets<span class="hljs-collection">[0]</span>)</span><span class="hljs-comment">;</span>
      }</span> else <span class="hljs-collection">{
        container.appendChild<span class="hljs-list">(<span class="hljs-title">section</span>)</span><span class="hljs-comment">;</span>
      }</span>

      // Если в ленте больше<span class="hljs-number"> 20</span> сообщений, удаляем самое старое
      if <span class="hljs-list">(<span class="hljs-title">length</span> &gt;<span class="hljs-number"> 20</span>)</span> <span class="hljs-collection">{
        container.removeChild<span class="hljs-list">(<span class="hljs-title">tweets</span><span class="hljs-collection">[length-1]</span>)</span><span class="hljs-comment">;</span>
      }</span>
    }</span>
  }</span>,<span class="hljs-number"> 2000</span>)</span><span class="hljs-comment">;</span>
}</span>)</span><span class="hljs-list">(<span class="hljs-title">window</span>, document)</span><span class="hljs-comment">;</span></code></pre>
<p>В таком виде мы получаем работающую трансляцию всех сообщений по заданным хештегам.</p>
<h2>Пульс твиттера о веб-разработке</h2>
<p>Для демонстрации я решил подготовить немного улучшенный вариант. Он транслирует большинство сообщений о веб-разработке на русском и английском языке. Как правильно заметил <a href="http://twitter.com/nesterov_vit/status/339820296817242114" target="_blank" rel="nofollow">@nesterov_vit</a> — очень похоже на телевизор.</p>
<p>Теперь предлагаю остановиться подробнее на тех улучшениях, которые я добавил.</p>
<h3>Серверная часть</h3>
<p>Во-первых, хотелось убрать нежелательные сообщения из трансляции. Для этого я подготовил несколько правил:</p>
<ol><li>сообщения только на русском и английском языке;</li>
<li>сообщения от пользователей с нестандартными аватарами;</li>
<li>игнорировать ретвиты и ответы;</li>
<li>игнорировать заданный список аккаунтов;</li>
<li>игнорировать заданные слова в сообщении;</li>
<li>игнорировать сообщения с переизбытком хештегов.</li>
</ol><p>Все эти правила я выделил в отдельную функцию.</p>
<pre><code class="coffeescript">twitter = <span class="hljs-built_in">require</span> <span class="hljs-string">'twitter-text'</span>

options =
  <span class="hljs-comment"># список нежелательных аккаунтов (для тролей)</span>
  mute: [<span class="hljs-string">''</span>]
  <span class="hljs-comment"># список стоп-слов (для спама)</span>
  spam: [<span class="hljs-string">'купить javascript без смс'</span>]

module.exports = (tweet) -&gt;
  <span class="hljs-constant">return</span> <span class="hljs-constant">false</span> <span class="hljs-keyword">if</span> <span class="hljs-operator">not</span> tweet <span class="hljs-operator">or</span> <span class="hljs-operator">not</span> tweet.user <span class="hljs-operator">or</span> <span class="hljs-operator">not</span> tweet.<span class="hljs-keyword">text</span>

  <span class="hljs-comment"># Разрешены сообщения только на русском и английском</span>
  <span class="hljs-keyword">if</span> tweet.lang?
    <span class="hljs-constant">return</span> <span class="hljs-constant">false</span> <span class="hljs-keyword">if</span> tweet.lang <span class="hljs-operator">not</span> <span class="hljs-operator">in</span> [<span class="hljs-string">'en'</span>, <span class="hljs-string">'ru'</span>]
  <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> tweet.user.lang?
    <span class="hljs-constant">return</span> <span class="hljs-constant">false</span> <span class="hljs-keyword">if</span> tweet.user.lang <span class="hljs-operator">not</span> <span class="hljs-operator">in</span> [<span class="hljs-string">'en'</span>, <span class="hljs-string">'ru'</span>]

  <span class="hljs-comment"># Пропускаем нежелательные аккаунты</span>
  <span class="hljs-keyword">if</span> options.mute.<span class="hljs-built_in">length</span> &gt; <span class="hljs-number">0</span>
    account = <span class="hljs-string">"#{tweet.user.screen_name}"</span>.toLowerCase()
    <span class="hljs-constant">return</span> <span class="hljs-constant">false</span> <span class="hljs-keyword">for</span> mute <span class="hljs-operator">in</span> options.mute when account is mute

  <span class="hljs-comment"># Пропускаем пользователей с аватаром по-умолчанию ^_^</span>
  <span class="hljs-keyword">if</span> tweet.user.profile_image_url.indexOf(<span class="hljs-string">'default_profile_images'</span>) &gt; -<span class="hljs-number">1</span>
    <span class="hljs-constant">return</span> <span class="hljs-constant">false</span>

  <span class="hljs-keyword">text</span> = <span class="hljs-string">"#{tweet.text}"</span>

  <span class="hljs-comment"># Пропускаем старые ретвиты и ответы на сообщения</span>
  <span class="hljs-keyword">if</span> <span class="hljs-keyword">text</span>.indexOf(<span class="hljs-string">'RT '</span>) is <span class="hljs-number">0</span> <span class="hljs-operator">or</span> <span class="hljs-keyword">text</span>.indexOf(<span class="hljs-string">'@'</span>) is <span class="hljs-number">0</span> <span class="hljs-operator">or</span> <span class="hljs-keyword">text</span>.indexOf(<span class="hljs-string">'.@'</span>) is <span class="hljs-number">0</span>
    <span class="hljs-constant">return</span> <span class="hljs-constant">false</span>

  <span class="hljs-keyword">if</span> options.spam.<span class="hljs-built_in">length</span> &gt; <span class="hljs-number">0</span>
    <span class="hljs-comment"># Преобразуем сжатые ссылки в полный вид</span>
    <span class="hljs-keyword">if</span> tweet.entities.urls? <span class="hljs-operator">and</span> tweet.entities.urls.<span class="hljs-built_in">length</span> &gt; <span class="hljs-number">0</span>
      <span class="hljs-keyword">for</span> entity <span class="hljs-operator">in</span> tweet.entities.urls
        <span class="hljs-keyword">text</span> = <span class="hljs-keyword">text</span>.<span class="hljs-built_in">replace</span> entity.url, entity.expanded_url

    <span class="hljs-keyword">text</span> = <span class="hljs-keyword">text</span>.toLowerCase()

    <span class="hljs-comment"># Пропускаем твиты содержащие в себе стоп-слова</span>
    <span class="hljs-constant">return</span> <span class="hljs-constant">false</span> <span class="hljs-keyword">for</span> spam <span class="hljs-operator">in</span> options.spam when <span class="hljs-keyword">text</span>.indexOf(spam) isnt -<span class="hljs-number">1</span>

  <span class="hljs-comment"># Пропускаем твиты содержащие переизбыток хештегов ^_^</span>
  hashtags = twitter.extractHashtagsWithIndices <span class="hljs-keyword">text</span>
  <span class="hljs-constant">return</span> <span class="hljs-constant">false</span> <span class="hljs-keyword">if</span> hashtags <span class="hljs-operator">and</span> hashtags.<span class="hljs-built_in">length</span> &gt; <span class="hljs-number">4</span>

  <span class="hljs-constant">return</span> <span class="hljs-constant">true</span></code></pre>
<p>Во-вторых, можно поработать над текстом сообщений. Вот, что мы с ними сделаем:</p>
<ol><li>все сжатые ссылки преобразуем в нормальные;</li>
<li>все ссылки на картинки преобразуем в изображения;</li>
<li>заменим размер аватаров на максимально возможный.</li>
</ol><p>Опять же, этим будет заниматься отдельная функция.</p>
<pre><code class="coffeescript">twitter = <span class="hljs-built_in">require</span> <span class="hljs-string">'twitter-text'</span>

module.exports = (tweet) -&gt;
  <span class="hljs-comment"># Преобразуем только ссылки, хештеги и аккаунты оставляем текстом</span>
  <span class="hljs-keyword">text</span> = twitter.autoLinkUrlsCustom tweet.<span class="hljs-keyword">text</span>, target: <span class="hljs-string">'_blank'</span>

  <span class="hljs-comment"># Заменяем сжатые ссылки на нормальные</span>
  <span class="hljs-keyword">if</span> tweet.entities.urls? <span class="hljs-operator">and</span> tweet.entities.urls.<span class="hljs-built_in">length</span> &gt; <span class="hljs-number">0</span>
    <span class="hljs-keyword">for</span> entity <span class="hljs-operator">in</span> tweet.entities.urls
      <span class="hljs-keyword">text</span> = <span class="hljs-keyword">text</span>.<span class="hljs-built_in">replace</span> entity.url, entity.expanded_url
      <span class="hljs-keyword">text</span> = <span class="hljs-keyword">text</span>.<span class="hljs-built_in">replace</span> entity.url, entity.display_url

  <span class="hljs-comment"># Заменяем ссылки на картинки на сами изображения</span>
  tweet.media = <span class="hljs-constant">false</span>
  <span class="hljs-keyword">if</span> tweet.entities.media? <span class="hljs-operator">and</span> tweet.entities.media.<span class="hljs-built_in">length</span> &gt; <span class="hljs-number">0</span>
    <span class="hljs-keyword">for</span> entity <span class="hljs-operator">in</span> tweet.entities.media
      <span class="hljs-keyword">text</span> = <span class="hljs-keyword">text</span>.<span class="hljs-built_in">replace</span> entity.url, entity.expanded_url
      <span class="hljs-keyword">text</span> = <span class="hljs-keyword">text</span>.<span class="hljs-built_in">replace</span> entity.url, entity.display_url

      tweet.media =
        url: entity.media_url
        w: entity.sizes.small.w
        h: entity.sizes.small.h

  tweet.<span class="hljs-keyword">text</span> = <span class="hljs-keyword">text</span>

  <span class="hljs-comment"># Заменяем размеры аватаров на максимальные</span>
  image = tweet.user.profile_image_url
  pos = image.lastIndexOf <span class="hljs-string">'_'</span>
  tweet.user.profile_image_url = image.substring(<span class="hljs-number">0</span>, pos) + <span class="hljs-string">'_bigger'</span> + image.substring(pos + <span class="hljs-number">7</span>)

  tweet</code></pre>
<h3>Клиентская часть</h3>
<p>В этой части я постарался сконцентрироваться на удобстве.</p>
<p>Во-первых, я добавил статус подключения к серверу. Страница может быть открыта продолжительное время, и не всем будет понятно, работает трансляция или нет.</p>
<p>Во-вторых, из-за того, что Twitter Streaming API возвращает только текущие сообщения, то может произойти так, что новый пользователь долгое время ничего не увидит. Чтобы этого избежать, сразу показываю 5-10 сообщений из поиска. Для этого я использую <a href="http://dev.twitter.com/docs/api/1.1" target="_blank" rel="nofollow">Twitter REST API</a>. При этом новые сообщения из Streaming API будут поступать в очередь сразу за старыми.</p>
<p>В-третьих, время сообщения отображается, как время прошедшее с публикации. И, чтобы оно оставалось в актуальном состоянии, раз в минуту оно пересчитывается.</p>
<p>В-четвёртых, вся видимая лента хранится в <code>sessionStorage</code>. Это сделано, чтобы избежать случайных обновлений страницы и других похожих ситуаций. Таким образом информация всегда будет на своём месте.</p>
<p>Ну что, <a href="http://simonenko.su/tools/twitter/">посмотрим демонстрацию</a>?</p>
<h2>Анализ сообщений</h2>
<p>Было бы здорово к этому прикрутить анализ сообщений. И пытаться выявить наиболее интересные темы за день. Но это, видимо, не мой уровень. Может кто-то из Яндекса придёт и скажет, как это сделать, а?</p>]]></description><link>http://simonenko.su/53381781858/pulse-of-web-developments</link><guid isPermaLink="true">http://simonenko.su/53381781858/pulse-of-web-developments</guid><dc:creator><![CDATA[Алексей Симоненко]]></dc:creator><pubDate>Wed, 19 Jun 2013 15:30:00 GMT</pubDate></item><item><title><![CDATA[Будущее за веб-компонентами]]></title><description><![CDATA[<div><iframe width="560" height="315" src="https://www.youtube.com/embed/eJZx9c6YL8k?rel=0" frameborder="0" allowfullscreen></iframe></div><p>Выступление Эрика Бидельмана о веб-компонентах. Рассказывает о Templates, Shadow DOM и Observers.</p>

<p><a href="http://html5-demos.appspot.com/static/webcomponents/index.html" target="_blank" rel="nofollow">Слайды презентации.</a></p>]]></description><link>http://simonenko.su/44130951332/webcomponents-are-the-future</link><guid isPermaLink="true">http://simonenko.su/44130951332/webcomponents-are-the-future</guid><dc:creator><![CDATA[Алексей Симоненко]]></dc:creator><pubDate>Wed, 27 Feb 2013 08:41:57 GMT</pubDate></item><item><title><![CDATA[Конструктор социальных сниппетов]]></title><description><![CDATA[<div></div><p><img align="left" alt="Социальный сниппет" height="216" src="http://simonenko.su/i/u/og-snippet.png" title="Социальный сниппет" width="216" itemprop="image"> Я не знаю, есть ли такое понятие, как социальный сниппет, но если нет, то я бы его ввёл. Что же это такое, спросите вы. Это описание вашей страницы, которое будет использовано в социальной сети. Большинство социальных сервисов самостоятельно формирует описание. Но это не всегда хорошо. Есть огромное количество неудачных примеров. Я считаю, что лучше всего взять это под свой контроль.</p>

<p>В маркетинговом блоге «Ловим Сетью» я уже рассказывал, как сделать описание для <a href="http://lovim.net/2012/10/facebook-meta-tags/" target="_blank" rel="nofollow">Facebook</a> и <a href="http://lovim.net/2012/11/twitter-cards/" target="_blank" rel="nofollow">Twitter</a>. Но как оказалось, этого не достаточно. Я подготовил небольшой конструктор, который должен помочь в решении этой проблемы.</p>
<p><a href="http://simonenko.su/tools/snippet/">Посмотреть конструктор.</a></p>
<p>С его помощью, вы легко сможете подготовить описание для своей страницы. Такой сниппет будет работать во всех популярных социальных сетях: Facebook, Twitter, ВКонтакте и Google+. Дополнительно я постарался ответить на часто задаваемые вопросы о социальных сниппетах.</p>]]></description><link>http://simonenko.su/44055675806/social-snippets</link><guid isPermaLink="true">http://simonenko.su/44055675806/social-snippets</guid><dc:creator><![CDATA[Алексей Симоненко]]></dc:creator><pubDate>Tue, 26 Feb 2013 10:37:00 GMT</pubDate></item><item><title><![CDATA[Что мне дал 2012 год]]></title><description><![CDATA[<div></div><p>Подведу итоги работы блога за 2012 год. Год был очень насыщенным. Я выделил пять наиболее ярких и захвативших меня тем в прошедшем году. Но перед этим я хотел бы показать статистику блога. На графике сравнение 2011 (оранжевая линия) с 2012 (синяя линяя) годом по версии Google Analytics. Этот рост мотивирует к дальнейшему развитию сайта.</p>

<p><a class="link" href="http://simonenko.su/i/u/statistic-2012.jpg" target="_blank"><img alt="Сравнение аудитории 2011 с 2012 годом" height="107" src="http://simonenko.su/i/u/statistic-2012-thumb.jpg" title="Сравнение аудитории 2011 с 2012 годом" width="500" itemprop="image"></a></p>
<h2>Конференция по маркетингу Digitale</h2>
<p>За 2012 год мы с партнёрами организовали две больших конференции по маркетингу в Санкт-Петербурге. Это, конечно, был сумасшедший опыт. Мы старались делать конференции на очень высоком уровне: как само мероприятие, так и <a href="http://digitaleconf.ru">сайт конференции</a>. Для разработки этого сайта я использовал Node.js и MongoDB и остался ими очень доволен. Также было интересно организовать <a href="http://simonenko.su/20775191979/nodejs-twitter-stream-websockets">twitter трансляцию</a> во время проведения мероприятия. Именно с её помощью мы дважды вывели хэштег #dconf в тренды.</p>
<h2>Интеграция социальных сервисов</h2>
<p>В этому году было очень много проектов с интеграцией в разные социальные сервисы, в том числе и в рамках проведения конференции Digitale. Это были совершенно разные виды интеграций: сайт с социальной сетью, социальная сеть с сайтом; работа с API на стороне клиента и сервера. Пришлось изучить всё вдоль и поперёк. Этот опыт дал мне возможность чувствовать себя экспертом в этой области. Даже написал несколько коротких статей для блога «Ловим сетью» (за эту возможность спасибо Павлу Калугину).</p>
<ul><li><a href="http://lovim.net/2012/10/facebook-meta-tags/" target="_blank" rel="nofollow">Как подготовить сайт, чтобы ссылки на него в Facebook выглядели красиво</a></li>
<li><a href="http://lovim.net/2012/11/twitter-cards/" target="_blank" rel="nofollow">Twitter Cards: как добавить описание и изображение к ссылкам на ваш сайт в Twitter</a></li>
</ul><p>Попробовал поработать с виджетами для Facebook. В результате на этом сайте появился Facebook Reader.</p>
<p><img alt="Социальный виджет: Facebook Reader" height="62" src="http://simonenko.su/i/u/facebook-reader.jpg" title="Социальный виджет: Facebook Reader" width="300" itemprop="image"></p>
<p>Этот виджет автоматически публикует информацию о прочтении статьи в ваш профиль. Разумеется, с рядом настроек и возможностью отключить это безобразие. Я даже думал описать как делать подобные вещи, но со временем правила отображения таких активностей в Facebook изменились и моё желание поутихло. Если вам всё равно интересно почитать об этом, напишите в комментариях «пиши про facebook reader» — сделаю.</p>
<p>В итоге у меня есть экспертиза в направлении социальных интеграций. Если у вас есть вопросы по этой теме, можете выловить меня и задать их. Особенно, что касается социальных сетей: Facebook, Twitter, Google+, ВКонтакте, Instagram, Foursquare, Tumblr.</p>
<h2>Выступления и обучение</h2>
<p>Ещё 2012 год можно назвать годом первых публичных выступлений. Причём это был очень широкий спектр. Вместе с Serenity мы проводили семинары по интернет-маркетингу, где я больше рассказывал про автоматизацию. Были совместные семинары агентств Serenity и Nimax, в которых я уже скорее вдохновлял использовать Facebook для работы над брендом. А самым странным выступлением была презентация про викиномику. Часть презентаций с этих выступлений можно посмотреть на <a href="http://speakerdeck.com/simonenko" target="_blank" rel="nofollow">Speaker Deck</a>.</p>
<p>Вместе с этим я стал помогать в обучении студентов. Периодически у меня бывают лекции в ИТМО по технологиям разработки сайтов. Например, я читал лекцию об адаптивном веб-дизайне и рассказывал о Node.js. Надеюсь, в следующем году эта инициатива будет развиваться, так как компетенции преподавателей ВУЗов, к сожалению, не хватает. А ведь нам с вами нужны квалифицированные сотрудники.</p>
<h2>HTML Academy</h2>
<p><a class="link" href="http://htmlacademy.ru" data-chocolate="false"><img alt="HTML Academy  интерактивные online-курсы по HTML и CSS" height="250" src="http://simonenko.su/i/u/htmlacademy.jpg" title="HTML Academy  интерактивные online-курсы по HTML и CSS" width="250" itemprop="image"></a></p>
<p>Я стараюсь не говорить о будущих проектах, но про <a href="http://htmlacademy.ru">HTML Academy</a> хочется сказать пару слов. Александр Першин реализовал потрясающую идею обучения HTML/CSS в интерактивных курсах. Сейчас проект ещё очень сырой. Однако те отзывы, которые мы получаем по этому проекту, очень вдохновляют. Пользователям нравится и они c нетерпением ждут новых заданий.</p>
<p>На момент написания статьи в академии участвует 261 пользователь, при этом регистрация для прохождения курсов не обязательна. Они выполнили 7 810 заданий, при этом 907 заданий за последнюю неделю.</p>
<p>На конференциях Digitale нам с Александром очень понравилось выступление Игоря М. Намаконова об игроизации контента (gamification). А раз уж мы играем в World of Warcraft то решили, что достижениям в академии быть. Такой подход к прохождению курсов сильно увеличил мотивацию пользователей. Уже 1 268 достижений выполнено, при этом 117 достижений за последнюю неделю. Статистику по достижениям всегда можно <a href="http://htmlacademy.ru/achievments">посмотреть на сайте</a>.</p>
<p>В проекте участвует два с половиной человека и главная сложность на этот момент — контент. Мы рады любой помощи. А если у вас есть знакомый инвестор (или вы — он), всегда можно помочь материально.</p>
<h2>Обновление сайта</h2>
<p>Под завершение хочется отметить, что в этому году я наконец-таки сделал из сайта то, что хотел. Конечно не до конца: разделы «Проекты» и «Выступления» ждут своего времени. Я постарался сделать качественный сайт, применить все те знания, которые у меня появились за эти годы. Это не относится к дизайну, всё-таки я совсем не дизайнер. Думаю, что профессионалы в этой области меня бы поругали. Вместе с обновлением сайта я постарался изменить подход к написанию статей. Так, чтобы качество контента не отставало от сайта.</p>
<p><strong>В любом случае, главное это читатели. Спасибо вам за поддержку в 2012 году.</strong></p>]]></description><link>http://simonenko.su/40672294255/what-gave-me-up-2012</link><guid isPermaLink="true">http://simonenko.su/40672294255/what-gave-me-up-2012</guid><dc:creator><![CDATA[Алексей Симоненко]]></dc:creator><pubDate>Tue, 15 Jan 2013 07:25:00 GMT</pubDate></item><item><title><![CDATA[Улучшаем опыт взаимодействия с формами]]></title><description><![CDATA[<div></div><p><img align="left" alt="Улучшаем опыт взаимодействия с формами" height="177" src="http://simonenko.su/i/u/wireframe.jpg" title="Улучшаем опыт взаимодействия с формами" width="241" itemprop="image"> Часто меня спрашивают студенты: «Какой элемент сайта самый важный?», на что я им отвечаю — формы. Ведь с помощью форм пользователи совершают почти все конверсионные действия. Именно с этим элементом связано больше всего проблем. В этой статье я постараюсь рассказать, что можно улучшить при взаимодействии с формами. А заодно описать новые возможности работы с ними в браузерах.</p>
<p>Однако, сначала я бы хотел обозначить свою позицию по разработке таких форм. По-моему мнению, правильным подходом при разработке интерфейсов является подход прогрессивного улучшения.</p>

<figure><a class="link" href="http://simonenko.su/i/u/progressive-enhancement.jpg" target="_blank"><img alt="Пример прогрессивного улучшения" height="200" src="http://simonenko.su/i/u/progressive-enhancement-thumb.jpg" title="Пример прогрессивного улучшения" width="500" itemprop="image"></a> <figcaption>Отличная иллюстрация Progressive Enhancement из презентации <a href="http://twitter.com/asktwi" target="_blank" rel="nofollow">Анны Селезнёвой</a>.</figcaption></figure>
<p>Александр Першин подробно рассказывал об этом подходе в своей статье <a href="http://htmlacademy.ru/blog/7">Progressive Enhancement</a>. Если вы до сих пор не сталкивались с ним, то я вам крайне рекомендую прочитать.</p>
<p>Ещё один нюанс: я не считаю, что интерфейс должен работать и выглядеть одинаково во всех браузерах. Я твёрдо уверен в том, что не стоит мучить пользователей со старыми браузерами вашими многокилобайтными библиотеками — им и без этого плохо.</p>
<h2>Браузерная статистика рунета</h2>
<p>Так как я буду показывать подходы, использующие новые возможности браузеров, взглянем на текущую статистику рунета. Каждый сможет для себя определить степень готовности пользователей. Я разделил её на две категории: полная и частичная поддержка.</p>
<p>Все приёмы в этой статье оценят <strong>56,8%</strong> пользователей. В этой статистике присутствуют браузеры: IE 10, Firefox 11-17, Chrome 4-24, Safari 6, Opera 12.</p>
<p>Часть приёмов оценят <strong>80,1%</strong> пользователей. Сюда дополнительно включил поддержку: IE 8-9, Safari 4-5, Opera 10-11.</p>
<p>Мобильные браузеры в статистику не включал, хотя они бы дали ещё больший процент поддержки. Информацию брал из <a href="http://www.liveinternet.ru/stat/ru/browsers.html?slice=ru;period=month" target="_blank" rel="nofollow">LiveInternet со срезом по России</a>. В расчёт попал средний трафик за 3 месяца (октябрь-декабрь) 2012 года.</p>
<h2>Новые атрибуты форм в HTML5</h2>
<p class="notice"><strong>Внимание:</strong> примеры в этой статье не содержат jQuery кода. Весь JavaScript написан с помощью фреймворка <a href="http://vanilla-js.com" target="_blank" rel="nofollow">Vanilla JS</a>.</p>
<p>Для начала, напомню о новых атрибутах у полей формы, которые буду использовать: <code>required</code>, <code>autofocus</code>, <code>placeholder</code>.</p>
<ul><li><code>required</code> — обязательное поле для заполнения;</li>
<li><code>autofocus</code> — установка фокуса на поле при загрузке страницы;</li>
<li><code>placeholder</code> — описание поля.</li>
</ul><div class="support"><header>Поддержка новых атрибутов форм.</header><figure><img src="/i/explorer.png" width="40" height="40" alt="Поддержка в Internet Explorer версии 10" title="Поддержка в Internet Explorer версии 10"><figcaption>10</figcaption></figure><figure><img src="/i/firefox.png" width="40" height="40" alt="Поддержка в Firefox версии 4" title="Поддержка в Firefox версии 4"><figcaption>4</figcaption></figure><figure><img src="/i/chrome.png" width="40" height="40" alt="Поддержка в Chrome версии 6" title="Поддержка в Chrome версии 6"><figcaption>6</figcaption></figure><figure><img src="/i/safari.png" width="40" height="40" alt="Поддержка в Safari версии 5" title="Поддержка в Safari версии 5"><figcaption>5</figcaption></figure><figure><img src="/i/opera.png" width="40" height="40" alt="Поддержка в Opera версии 10" title="Поддержка в Opera версии 10"><figcaption>10</figcaption></figure></div>
<p>Вместе с этим появилось много новых типов полей: <code>date</code>, <code>email</code>, <code>number</code>, <code>range</code> и другие. Однако, самое безобидное из них (<code>email</code>), до сих пор используется с опаской. А ведь для того, чтобы оно заработало специальных действий не нужно. Браузеры, которые не знают этот тип полей, будут считать его текстовым.</p>
<p>Также появились дополнительные селекторы в CSS: <code>E:valid</code>, <code>E:invalid</code>, <code>E:required</code> — с помощью которых можно описывать стилевое оформление полей в разных ситуациях.</p>
<p><a href="http://simonenko.su/dev/improve-web-form/attributes.html">Демонстрация работы новых атрибутов полей и их оформления.</a></p>
<p>Такой подход, конечно, не будет работать в старых браузерах. Однако, так как мы прогрессивно улучшаем форму, нас это не должно сильно беспокоить. <strong>Форма остаётся работать даже в старых браузерах.</strong> В любом случае, проверка введённых данных должна происходить на серверной стороне. Возьмите себе за правило не доверять пользовательским данным и всегда полностью проверять их на сервере.</p>
<h2>Запись данных формы по мере ввода</h2>
<div class="support"><header>Поддержка <a href="http://caniuse.com/#feat=namevalue-storage"><code>localStorage</code></a>.</header><figure><img src="/i/explorer.png" width="40" height="40" alt="Поддержка в Internet Explorer версии 8" title="Поддержка в Internet Explorer версии 8"><figcaption>8</figcaption></figure><figure><img src="/i/firefox.png" width="40" height="40" alt="Поддержка в Firefox версии 3.5" title="Поддержка в Firefox версии 3.5"><figcaption>3.5</figcaption></figure><figure><img src="/i/chrome.png" width="40" height="40" alt="Поддержка в Chrome версии 4" title="Поддержка в Chrome версии 4"><figcaption>4</figcaption></figure><figure><img src="/i/safari.png" width="40" height="40" alt="Поддержка в Safari версии 4" title="Поддержка в Safari версии 4"><figcaption>4</figcaption></figure><figure><img src="/i/opera.png" width="40" height="40" alt="Поддержка в Opera версии 10.5" title="Поддержка в Opera версии 10.5"><figcaption>10.5</figcaption></figure></div>
<p>Одна из частых проблем заполнения форм — введённые данные теряются. Это может произойти по разным причинам: ошибка сайта, переход по ссылке, в конце концов, может отключиться интернет. Решить эту проблему можно по-разному, например, записывать данные в <code>localStorage</code> по мере ввода.</p>
<pre><code class="javascript"><span class="hljs-keyword">if</span> (window.localStorage) {
  <span class="hljs-keyword">var</span> elements = document.querySelectorAll(<span class="hljs-string">'[name]'</span>);

  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>, length = elements.length; i &lt; length; i++) {
    (<span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(element)</span> {</span>
      <span class="hljs-keyword">var</span> name = element.getAttribute(<span class="hljs-string">'name'</span>);

      element.value = localStorage.getItem(name) || <span class="hljs-string">''</span>;

      element.onkeyup = <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">()</span> {</span>
        localStorage.setItem(name, element.value);
      };
    })(elements[i]);
  }
}</code></pre>
<h2>Валидация формы и отправка данных аяксом</h2>
<p>Раз уж мы используем атрибуты <code>required</code>, то можно и валидацию сделать по-новому. В спецификации <a href="http://www.w3.org/TR/html5/" target="_blank" rel="nofollow">HTML5</a> для элемента формы добавлен метод <code>checkValidity()</code>. Он возвращает <code>true</code> или <code>false</code>. Стратегия работы формы будет очень простой: если проверка валидации даёт отрицательный результат — мы блокируем отправку формы, в обратном случае — разрешаем отправку.</p>
<pre><code class="javascript">submit<span class="hljs-preprocessor">.disabled</span> = !form<span class="hljs-preprocessor">.checkValidity</span>()<span class="hljs-comment">;</span></code></pre>
<div class="support"><header>Поддержка <a href="http://caniuse.com/#feat=xhr2"><code>FormData</code></a> из XHR2.</header><figure><img src="/i/explorer.png" width="40" height="40" alt="Поддержка в Internet Explorer версии 10" title="Поддержка в Internet Explorer версии 10"><figcaption>10</figcaption></figure><figure><img src="/i/firefox.png" width="40" height="40" alt="Поддержка в Firefox версии 4" title="Поддержка в Firefox версии 4"><figcaption>4</figcaption></figure><figure><img src="/i/chrome.png" width="40" height="40" alt="Поддержка в Chrome версии 7" title="Поддержка в Chrome версии 7"><figcaption>7</figcaption></figure><figure><img src="/i/safari.png" width="40" height="40" alt="Поддержка в Safari версии 5" title="Поддержка в Safari версии 5"><figcaption>5</figcaption></figure><figure><img src="/i/opera.png" width="40" height="40" alt="Поддержка в Opera версии 12" title="Поддержка в Opera версии 12"><figcaption>12</figcaption></figure></div>
<p>Теперь добавим возможность отправлять форму без перезагрузки, с помощью аякс. Со второй версией спецификации <a href="http://www.w3.org/TR/XMLHttpRequest/" target="_blank" rel="nofollow">XMLHttpRequest</a> мы получили много интересного. Например, мы можем больше не заниматься сбором данных для отправки формы, для этого есть объект <code>FormData</code>.</p>
<pre><code class="javascript">form.onsubmit = <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(event)</span> {</span>
  <span class="hljs-keyword">if</span> (window.FormData) {
    event.preventDefault();

    <span class="hljs-keyword">var</span> data = <span class="hljs-keyword">new</span> FormData(form);
    <span class="hljs-keyword">var</span> xhr = <span class="hljs-keyword">new</span> XMLHttpRequest();
    <span class="hljs-keyword">var</span> url = form.getAttribute(<span class="hljs-string">'action'</span>) + <span class="hljs-string">'?time='</span> + (<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>()).getTime();

    xhr.open(<span class="hljs-string">'post'</span>, url);
    xhr.onreadystatechange = <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">()</span> {</span>
      <span class="hljs-keyword">if</span> (xhr.readyState == <span class="hljs-number">4</span> &amp;&amp; xhr.status == <span class="hljs-number">200</span>) {
        <span class="hljs-comment">// server response: xhr.responseText</span>
      }
    };
    xhr.send(data);
  }
};</code></pre>
<p>При работе с асинхронными запросами следует помнить, что некоторые браузеры кэшируют результат. Например, это делает Internet Explorer, Mobile Safari (iOS 6) и другие. Чтобы избежать эту проблему, можно добавлять к адресу запроса текущее время.</p>
<p>Сейчас ответ от сервера мы получаем в текстовом виде (<code>xhr.responseText</code>), но со временем это изменится. Например, если мы точно знаем, что ответом будет JSON, мы можем получить JavaScript объект сразу.</p>
<pre><code class="javascript"><span class="hljs-keyword">var</span> xhr = <span class="hljs-keyword">new</span> XMLHttpRequest();

xhr.open(<span class="hljs-function"><span class="hljs-keyword">method</span>, <span class="hljs-title">url</span>);</span>
xhr.responseType = <span class="hljs-string">'json'</span>;

xhr.onreadystatechange = <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">()</span> <span class="hljs-comment">{
  if (xhr.readyState == 4 &amp;&amp; xhr.status == 200) {
    // server response: xhr.response
  }</span>
};</span>

xhr.send();</code></pre>
<p>Обратите внимание, что ответ сервера будет в свойстве <code>xhr.response</code>. А свойство <code>xhr.responseType</code> может принимать и другие значения, например: <code>arraybuffer</code>, <code>blob</code>, <code>document</code>.</p>
<p><a href="http://simonenko.su/dev/improve-web-form/localstorage.html">Демонстрация сохранения данных формы и отправки их с помощью XMLHttpRequest.</a></p>
<p>После успешной отправки формы я советую оставить контактную информацию в <code>localStorage</code>, а остальное очистить. Таким образом, если пользователь захочет ещё раз отправить форму, часть информации уже будет заполнена.</p>
<h2>Предварительный просмотр закачиваемых фотографий</h2>
<p>Плавно переходим на работу с файлами в формах. До недавнего времени почти никаких средств для работы с файлами не было. Но всё меняется. Начнём с простого — новые атрибуты для полей загрузки файлов.</p>
<ul><li><code>multiple</code> — позволяет выбирать несколько файлов;</li>
<li><code>accept</code> — даёт возможность указывать, какие файлы выбирать.</li>
</ul><p>Допустим, мы хотим к нашей форме добавить возможность закачивать несколько фотографий. Такое поле будет выглядеть как-то так.</p>
<pre><code class="html">&lt;input <span class="hljs-keyword">type</span>=<span class="hljs-string">"file"</span> name=<span class="hljs-string">"image"</span> accept=<span class="hljs-string">"image/*"</span> multiple&gt;</code></pre>
<p>Хочу напомнить: поле с такими атрибутами будет работать в старых браузерах. Ограничением будет:</p>
<ol><li>Только один файл;</li>
<li>Валидация файлов производится на серверной стороне.</li>
</ol><div class="support"><header>Поддержка <a href="http://caniuse.com/#feat=filereader"><code>FileReader</code></a> из File API.</header><figure><img src="/i/explorer.png" width="40" height="40" alt="Поддержка в Internet Explorer версии 10" title="Поддержка в Internet Explorer версии 10"><figcaption>10</figcaption></figure><figure><img src="/i/firefox.png" width="40" height="40" alt="Поддержка в Firefox версии 3.6" title="Поддержка в Firefox версии 3.6"><figcaption>3.6</figcaption></figure><figure><img src="/i/chrome.png" width="40" height="40" alt="Поддержка в Chrome версии 6" title="Поддержка в Chrome версии 6"><figcaption>6</figcaption></figure><figure><img src="/i/safari.png" width="40" height="40" alt="Поддержка в Safari версии 6" title="Поддержка в Safari версии 6"><figcaption>6</figcaption></figure><figure><img src="/i/opera.png" width="40" height="40" alt="Поддержка в Opera версии 11.10" title="Поддержка в Opera версии 11.10"><figcaption>11.10</figcaption></figure></div>
<p>Попробуем улучшить опыт взаимодействия с файлами. Раз мы ожидаем от пользователей добавления фотографий, логично сделать возможным предварительный просмотр. Для этого мы будем использовать объект <code>FileReader</code> из спецификации <a href="http://www.w3.org/TR/FileAPI/" target="_blank" rel="nofollow">File API</a>.</p>
<pre><code class="javascript">document.querySelector(<span class="hljs-string">'[type=file]'</span>).addEventListener(<span class="hljs-string">'change'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">()</span> {</span>
  [].forEach.call(<span class="hljs-keyword">this</span>.files, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(file)</span> {</span>
    <span class="hljs-keyword">if</span> (file.type.match(<span class="hljs-regexp">/image.*/</span>)) {
      <span class="hljs-keyword">var</span> reader = <span class="hljs-keyword">new</span> FileReader();

      reader.onload = <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(event)</span> {</span>
        <span class="hljs-keyword">var</span> img = document.createElement(<span class="hljs-string">'img'</span>);
        img.src = event.target.result;

        div.appendChild(img);

        queue.push({file: file, element: img});
      };

      reader.readAsDataURL(file);
    }
  });
}, <span class="hljs-literal">false</span>);</code></pre>
<p>Таким образом, все выбранные фотографии мы сразу же отображаем на сайте.</p>
<p><a class="link" href="http://simonenko.su/i/u/image-preview.jpg" target="_blank"><img alt="Предварительный просмотр фотографий (FileReader)" height="500" src="http://simonenko.su/i/u/image-preview-thumb.jpg" title="Предварительный просмотр фотографий (FileReader)" width="491" itemprop="image"></a></p>
<p>А для того, чтобы отправить их с помощью аякса, мы собираем их в массиве queue. Ранее в статье мы использовали объект <code>FormData</code>, сейчас мы просто добавим к нему список файлов.</p>
<pre><code class="javascript"><span class="hljs-keyword">var</span> data = <span class="hljs-keyword">new</span> FormData(form);

queue.<span class="hljs-keyword">forEach</span>(<span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(element)</span> {</span>
  data.append(<span class="hljs-string">'image'</span>, element.file);
});</code></pre>
<p>Только и всего, остальное остаётся таким же. Форма будет отправлена с файлами без перезагрузки.</p>
<p><a href="http://simonenko.su/dev/improve-web-form/ajax-upload.html">Демонстрация предварительного просмотра фотографий и отправки их с помощью аякс.</a></p>
<h2>Drag and drop файлов</h2>
<div class="support"><header>Поддержка <a href="http://caniuse.com/#feat=dragndrop" target="_blank" rel="nofollow">drag and drop</a> файлов.</header><figure><img src="/i/explorer.png" width="40" height="40" alt="Поддержка в Internet Explorer версии 10" title="Поддержка в Internet Explorer версии 10"><figcaption>10</figcaption></figure><figure><img src="/i/firefox.png" width="40" height="40" alt="Поддержка в Firefox версии 3.5" title="Поддержка в Firefox версии 3.5"><figcaption>3.5</figcaption></figure><figure><img src="/i/chrome.png" width="40" height="40" alt="Поддержка в Chrome версии 4" title="Поддержка в Chrome версии 4"><figcaption>4</figcaption></figure><figure><img src="/i/safari.png" width="40" height="40" alt="Поддержка в Safari версии 3.1" title="Поддержка в Safari версии 3.1"><figcaption>3.1</figcaption></figure><figure><img src="/i/opera.png" width="40" height="40" alt="Поддержка в Opera версии 12" title="Поддержка в Opera версии 12"><figcaption>12</figcaption></figure></div>
<p>Попробуем уделить больше внимания файлам. Добавим возможность перетаскивать файлы с компьютера сразу в форму. При этом логика предварительного просмотра и отправки без перезагрузки должна остаться. Для начала давайте выделим работу с предварительным просмотром в отдельную функцию.</p>
<pre><code class="javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">previewImages</span><span class="hljs-params">(files)</span> {</span>
  [].forEach.call(files, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(file)</span> {</span>
    <span class="hljs-keyword">if</span> (file.type.match(<span class="hljs-regexp">/image.*/</span>)) {
      <span class="hljs-keyword">var</span> reader = <span class="hljs-keyword">new</span> FileReader();

      reader.onload = <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(event)</span> {</span>
        <span class="hljs-keyword">var</span> img = document.createElement(<span class="hljs-string">'img'</span>);
        img.src = event.target.result;

        div.appendChild(img);

        queue.push({file: file, element: img});
      };

      reader.readAsDataURL(file);
    }
  });
}</code></pre>
<p>Допустим, зоной для перемещения файлов будет блок с классом <code>wrapper</code>. Добавим события для него.</p>
<pre><code class="javascript"><span class="hljs-keyword">var</span> file = document.querySelector(<span class="hljs-string">'[type=file]'</span>);
<span class="hljs-keyword">var</span> dropzone = document.querySelector(<span class="hljs-string">'.wrapper'</span>);

file.addEventListener(<span class="hljs-string">'change'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">()</span> {</span>
  previewImages(<span class="hljs-keyword">this</span>.files);
  <span class="hljs-keyword">this</span>.value = <span class="hljs-string">''</span>;
}, <span class="hljs-literal">false</span>);

dropzone.addEventListener(<span class="hljs-string">'dragover'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(event)</span> {</span>
  event.preventDefault();

  dropzone.classList.add(<span class="hljs-string">'active'</span>);
  event.dataTransfer.dropEffect = <span class="hljs-string">'copy'</span>;
}, <span class="hljs-literal">false</span>);

dropzone.addEventListener(<span class="hljs-string">'drop'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(event)</span> {</span>
  event.preventDefault();

  dropzone.classList.remove(<span class="hljs-string">'active'</span>);
  previewImages(event.dataTransfer.files);
}, <span class="hljs-literal">false</span>);</code></pre>
<p>Как видите, мы добавили события начала (<code>dragover</code>) и конца (<code>drop</code>) перемещения файлов. Все перемещённые файлы мы передаём функции <code>previewImages</code>. Таким образом, наша форма работает одинаково с файлами выбранными через сайт и перемещёнными с компьютера.</p>
<h2>Процесс загрузки файлов (progress bar)</h2>
<p>Фотографии бывают очень большими, поэтому попробуем отобразить процесс загрузки. Для визуализации этого процесса я возьму элемент <code>progress</code>, а вы можете взять <code>div</code> с двигающимся фоном. Сам процесс будет происходить в событии <code>progress</code> из спецификации XMLHttpRequest.</p>
<pre><code class="javascript"><span class="hljs-keyword">var</span> xhr = <span class="hljs-keyword">new</span> XMLHttpRequest();

xhr.upload.addEventListener(<span class="hljs-string">'progress'</span>, function(<span class="hljs-keyword">event</span>) {
  <span class="hljs-keyword">if</span> (<span class="hljs-keyword">event</span>.lengthComputable) {
    progress.<span class="hljs-keyword">value</span> = Math.round((<span class="hljs-keyword">event</span>.loaded * <span class="hljs-number">100</span>) / <span class="hljs-keyword">event</span>.total);
  }
}, <span class="hljs-keyword">false</span>);</code></pre>
<p><a href="http://simonenko.su/dev/improve-web-form/drag-and-drop.html">Демонстрация drag &amp; drop и прогресса загрузки файлов.</a></p>
<h2>В итоге</h2>
<p>Наша простая форма имеет ряд значительных улучшений в области UX.</p>
<ol><li>Валидация происходит в момент ввода;</li>
<li>Введённые данные запоминаются пока не будут отправлены;</li>
<li>Контактные данные сохраняются для повторной работы;</li>
<li>Предварительный просмотр фотографий;</li>
<li>Процесс загрузки файлов.</li>
</ol><p>При этом, так как мы действовали в соответствии с прогрессивным улучшением, наша форма работает везде:</p>
<ul><li>в IE 6-7 и других старых браузерах,</li>
<li>с отключённым JavaScript,</li>
<li>на мобильных устройствах.</li>
</ul><figure><a class="link" href="http://simonenko.su/i/u/mobile-webform.jpg" target="_blank"><img alt="Загрузка фотографий с мобильного телефона" height="500" src="http://simonenko.su/i/u/mobile-webform-thumb.jpg" title="Загрузка фотографий с мобильного телефона" width="381" itemprop="image"></a> <figcaption>Работа <code>FileReader</code> на iOS 6.</figcaption></figure>
<p>Уверен, занимаясь улучшением UX форм, можно найти более интересные решения. Пожалуйста, добавьте ваши решения, советы, критику в комментариях ниже. Спасибо!</p>]]></description><link>http://simonenko.su/38146501854/improving-ux-for-web-form</link><guid isPermaLink="true">http://simonenko.su/38146501854/improving-ux-for-web-form</guid><dc:creator><![CDATA[Алексей Симоненко]]></dc:creator><pubDate>Tue, 18 Dec 2012 07:00:00 GMT</pubDate></item><item><title><![CDATA[CSS transition и animation]]></title><description><![CDATA[<div><iframe width="560" height="315" src="https://player.vimeo.com/video/49879139?byline=0&amp;badge=0" frameborder="0" allowfullscreen></iframe></div>Выступление <a href="http://lea.verou.me" rel="nofollow" target="_blank">Lea Verou</a> об анимации в CSS.]]></description><link>http://simonenko.su/35634161869/css-transition-and-animation</link><guid isPermaLink="true">http://simonenko.su/35634161869/css-transition-and-animation</guid><dc:creator><![CDATA[Алексей Симоненко]]></dc:creator><pubDate>Tue, 13 Nov 2012 13:40:00 GMT</pubDate></item><item><title><![CDATA[Нужны ли классы в JavaScript?]]></title><description><![CDATA[<div></div><p><img align="left" alt="Нужны ли классы в JavaScript" height="244" src="http://simonenko.su/i/u/javascript.png" title="Нужны ли классы в JavaScript" width="244" itemprop="image"> Хочется нам того или нет, но классы в ECMAScript 6&#160;<a href="http://wiki.ecmascript.org/doku.php?id=strawman:maximally_minimal_classes" target="_blank" rel="nofollow">всё-таки будут</a>. Мнения по поводу самой концепции классов в JavaScript всегда были весьма противоречивыми. Одним бесклассовая природа JavaScript нравится — это делает язык отличным от других. Другие же просто ненавидят JavaScript по тем же причинам. Отсутствие классов является одной из основных психологических сложностей, с которыми сталкиваются разработчики при переходе с C++ или Java на JavaScript. Некоторые разработчики говорили мне, что именно по причине отсутствия классов они либо не любят JavaScript, либо вообще отказались его изучать.</p>

<p>В JavaScript изначально не было формального определения классов, что и внесло путаницу с самого начала его использования. Во многих книгах и статьях, посвящённых JavaScript, о классах говорится так, как если бы они реально существовали. Но то, что они называют классами, по сути своей является всего лишь пользовательскими конструкторами, которые используются для определения пользовательских типов данных. Такой способ – это наиболее близкое описание классов в JavaScript. Общий формат выглядит достаточно знакомым большинству разработчиков, вот, например, так:</p>
<pre><code class="javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyCustomType</span><span class="hljs-params">(value)</span> {</span>
  <span class="hljs-keyword">this</span>.property = value;
}

MyCustomType.prototype.method = <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">()</span> {</span>
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.property;
};</code></pre>
<p>Довольно часто такой код называется объявлением класса <code>MyCustomType</code>. На самом же деле здесь всего лишь объявляется функция <code>MyCustomType</code>, которая в дальнейшем будет вызываться с помощью <code>new</code> для создания экземпляра <code>MyCustomType</code>. Но в этой функции нет ничего особенного, равно как и нет в ней ничего, что указывало бы на её отличие от какой-либо другой функции, не используемой для создания нового объекта. Именно такой способ использования функции и делает её конструктором.</p>
<p>Этот код даже не похож на объявление какого-либо класса. По сути, сложно уловить какую-либо взаимосвязь между объявлением конструктора и методом в прототипе. Начинающим разработчикам JavaScript этот код вообще может показаться невзаимосвязанным. Однако же связь между ними существует, хотя она и близко не похожа на объявление класса, принятого в других языках.</p>
<p>Ещё более непонятным выглядит обсуждение вопроса наследования. Разработчики смело бросаются такими понятиями как «подклассы» и «суперклассы», хотя эти термины имеют смысл лишь при условии реального существования классов. И, конечно же, синтаксис для наследования выглядит довольно путанным и громоздким:</p>
<pre><code class="javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Animal</span><span class="hljs-params">(name)</span> {</span>
  <span class="hljs-keyword">this</span>.name = name;
}

Animal.prototype.sayName = <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">()</span> {</span>
  console.log(<span class="hljs-keyword">this</span>.name);
};

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Dog</span><span class="hljs-params">(name)</span> {</span>
  Animal.call(<span class="hljs-keyword">this</span>, name);
}

Dog.prototype = <span class="hljs-keyword">new</span> Animal(<span class="hljs-literal">null</span>);
Dog.prototype.bark = <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">()</span> {</span>
  console.log(<span class="hljs-string">'Woof!'</span>);
};</code></pre>
<p>Чтобы осуществить наследование нужно сначала определить конструктор, а затем переопределить прототип. Этот двухшаговый приём невероятно запутан.</p>
<p>В первом издании книги «Professional JavaScript for Web Developers» я использовал только понятие «класс». Из полученных отзывов я понял, что очень многих это сбивало с толку, и поэтому во втором издании я заменил «класс» на «тип». С тех пор я пользуюсь только этой терминологией, что часто помогает мне избежать непонимания.</p>
<p>Но проблема всё равно остаётся довольно острой. Синтаксис для определения пользовательских типов данных действительно путанный и громоздкий. Наследование между двумя типами – процесс многошаговый. Не существует простого способа вызвать метод родительского класса. Общий итог таков: создание и управление пользовательскими типами данных в JavaScript – это одна сплошная головная боль. Если не верится в то, что проблема действительно серьёзная, то достаточно взглянуть на количество библиотек, предлагающих свой способ объявления пользовательских типов данных или наследования, или того и другого вместе взятых.</p>
<ul><li><a href="http://yuilibrary.com/yui/docs/api/classes/YUI.html#method_extend" target="_blank" rel="nofollow">YUI</a> – предлагает <code>Y.extend()</code> для осуществления наследования. Также добавляет свойство <code>superclass</code> при использовании этого метода.</li>
<li><a href="http://prototypejs.org/learn/class-inheritance" target="_blank" rel="nofollow">Prototype</a> – предлагает <code>Class.create()</code> и <code>Object.extend()</code> для работы с объектами и «классами».</li>
<li><a href="http://www.sitepen.com/blog/2010/07/01/creating-and-enhancing-dojo-classes/" target="_blank" rel="nofollow">Dojo</a> – предлагает <code>dojo.declare()</code> и <code>dojo.extend()</code>.</li>
<li><a href="http://mootools.net/docs/core/Class/Class" target="_blank" rel="nofollow">MooTools</a> – имеет собственный тип данных под названием <code>Class</code> для определения и расширения классов.</li>
</ul><p>Сам факт того, что так много библиотек JavaScript предлагают какие-то свои варианты решения, свидетельствует о том, что проблема действительно существует. Определение собственных типов данных – дело непростое и далеко не интуитивное. Разработчикам JavaScript нужно нечто более удобное, чем существующий синтаксис.</p>
<p>По своей сути классы в ECMAScript 6 – это ни что иное, как синтаксический сахар для использования уже знакомых паттернов. Рассмотрим этот пример:</p>
<pre><code class="javascript"><span class="hljs-keyword">class</span> MyCustomType <span class="hljs-comment">{
  constructor(value) {
    this.property = value;
  }</span>

  <span class="hljs-function"><span class="hljs-keyword">method</span><span class="hljs-params">()</span> <span class="hljs-comment">{
    return this.property;
  }</span>
}</span></code></pre>
<p>Это объявление класса в ECMAScript 6 легко сводится к предыдущему примеру. Объект, созданный с помощью такого определения класса, работает точно так же, как и объект, созданный с помощью определения конструктора в примере выше. Единственное отличие заключается в более компактном синтаксисе. А как на счёт наследования?</p>
<pre><code class="javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Animal</span> {</span>
  constructor(name) {
    <span class="hljs-keyword">this</span>.name = name;
  }

  sayName() {
    console.log(<span class="hljs-keyword">this</span>.name);
  }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Dog</span> <span class="hljs-inheritance"><span class="hljs-keyword">extends</span></span> <span class="hljs-title">Animal</span> {</span>
  constructor(name) {
    <span class="hljs-keyword">super</span>(name);
  }

  bark() {
    console.log(<span class="hljs-string">'Woof!'</span>);
  }
}</code></pre>
<p>Этот пример легко сводится к предыдущему примеру наследования. В данном случае объявление класса выглядит более компактно, а тяжеловесный многошаговый паттерн наследования теперь заменяется простым ключевым словом <code>extends</code>. Ещё одним преимуществом является возможность использовать метод <code>super()</code> внутри определения класса, чтобы обращаться к классу родителю.</p>
<p>С точки зрения классов текущая версия ECMAScript 6 не предлагает ничего большего, чем новый синтаксис для уже знакомых паттернов из JavaScript. Наследование работает так же, как и раньше (связь прототипов плюс вызов конструктора у родителя), к прототипам добавляются методы, а свойства объявляются в конструкторе. Единственное реальное преимущество для разработчиков – меньше писанины. Определение классов по сути своей является определением типов, но с помощью другого синтаксиса.</p>
<p>И пока кто-то возмущается по поводу введения классов в ECMAScript 6, помните, что сама идея классов в данном случае весьма абстрактна. На самом деле ничего кардинально не меняется, и ничего нового не вводится. Классы – это лишь синтаксический сахар для пользовательских типов, которые и так уже давно существуют в этом языке. В данном случае просто решается давняя проблема JavaScript — путаницы при определении пользовательских типов. Лично мне очень бы хотелось использовать ключевое слово <code>type</code> вместо слова <code>class</code>, но, в конце концов, это всего лишь вопрос семантики.</p>
<p>Так нужны ли классы в JavaScript? Нет. Но в JavaScript определённо нужен более простой и понятный способ определения пользовательских классов. Просто случайно получилось так, что способ сделать это в ECMAScript 6 назвали «классом». И если это помогает разработчикам других языков проще переключиться на JavaScript, то почему бы и нет.</p>
<hr><p>Это перевод статьи Nicholas C. Zakas — «<a href="http://www.nczonline.net/blog/2012/10/16/does-javascript-need-classes/" target="_blank">Does JavaScript need classes?</a>». Николас — фронтенд-консультант и автор хороших книг о JavaScript — «Maintainable JavaScript», «Professional JavaScript for Web Developers», «High Performance JavaScript» и «Professional Ajax».</p>]]></description><link>http://simonenko.su/35212617494/does-javascript-need-classes</link><guid isPermaLink="true">http://simonenko.su/35212617494/does-javascript-need-classes</guid><dc:creator><![CDATA[Алексей Симоненко]]></dc:creator><pubDate>Wed, 07 Nov 2012 18:56:00 GMT</pubDate></item><item><title><![CDATA[CSS Shaders]]></title><description><![CDATA[<div><iframe width="560" height="315" src="https://www.youtube.com/embed/NZRqnohI3m4?rel=0" frameborder="0" allowfullscreen></iframe></div>Интересное предложение от Adobe в спецификацию <a href="https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html" rel="nofollow" target="_blank">CSS Filter Effects</a>. После видео можно подробнее об этом почитать на <a href="http://updates.html5rocks.com/2012/09/Interactive-Globe-with-CSS-shaders-Google-Maps" rel="nofollow" target="_blank">HTML5Rocks</a>.]]></description><link>http://simonenko.su/33845479228/css-shaders</link><guid isPermaLink="true">http://simonenko.su/33845479228/css-shaders</guid><dc:creator><![CDATA[Алексей Симоненко]]></dc:creator><pubDate>Thu, 18 Oct 2012 18:40:00 GMT</pubDate></item><item><title><![CDATA[Используйте свойство box-sizing]]></title><description><![CDATA[<div></div><p><img align="left" alt="Блочная модель CSS" height="177" src="http://simonenko.su/i/u/box-model.png" title="Блочная модель CSS" width="249" itemprop="image"> Странно, но многие разработчики до сих пор не знают о таком замечательном CSS3 свойстве как <code>box-sizing</code>. Или знают, но почему-то не используют. А ведь используя это свойство, можно избежать вроде бы нелогичного поведения браузеров.</p>
<p>Например, задавая ширину и высоту блоку, чаще всего мы хотим видеть его именно таких размеров. Но выходит совсем не так. Ведь на реальные размеры блока влияет размер границ и отступов. И в итоге мы получаем совсем не то, что ожидаем.</p>

<p>Всё дело в классической блочной модели, в которой размеры указываются содержимому блока, а не блоку в целом. То есть, реальные размеры блока складываются из суммы размеров содержимого, отступов и границ. Такая модель вносит трудности в ряд задач. Например, если нужно расположить в блоке заданных размеров несколько блоков в ряд.</p>
<figure><img alt="3 блока в ряд с заданными размерами" height="168" src="http://simonenko.su/i/u/box-example-1.png" title="3 блока в ряд с заданными размерами" width="450" itemprop="image"><figcaption><a href="http://simonenko.su/dev/box-sizing/inline.html">Посмотреть демонстрацию</a></figcaption></figure>
<p>Конечно, пример слишком простой, ведь легко рассчитать правильные размеры блоков. Рассмотрим более сложный пример. Допустим, нам нужно поместить 3 блока на всю ширину блока-родителя, у которого ширина указана в процентах. В этом случае просто рассчитать правильные размеры уже не получится.</p>
<figure><img alt="3 блока в ряд с процентными размерами" height="168" src="http://simonenko.su/i/u/box-example-2.png" title="3 блока в ряд с процентными размерами" width="488" itemprop="image"><figcaption><a href="http://simonenko.su/dev/box-sizing/percent.html">Посмотреть демонстрацию</a></figcaption></figure>
<p>Или проблема с формами, например, разместить <code>&lt;textarea&gt;</code> на всю ширину блока.</p>
<figure><img alt="100% ширина textarea в блоке" height="111" src="http://simonenko.su/i/u/box-example-3.png" title="100% ширина textarea в блоке" width="466" itemprop="image"><figcaption><a href="http://simonenko.su/dev/box-sizing/textarea.html">Посмотреть демонстрацию</a></figcaption></figure>
<h2>Используем box-sizing</h2>
<p>Такие проблемы можно решить с помощью CSS3 свойства <a href="https://developer.mozilla.org/en-US/docs/CSS/box-sizing"><code>box-sizing</code></a>. Одно из значений этого свойства является <code>border-box</code>, которое позволяет не учитывать в размерах блока отступы и границы. Таким образом, указывая размеры блоку, вы указываете их всему блоку, а не содержимому.</p>
<h2>Поддержка в браузерах</h2>
<p>На удивление, это свойство <a href="http://caniuse.com/#search=box-sizing" target="_blank" rel="nofollow">поддерживается во всех современных браузерах</a>.</p>
<p><a class="link" href="http://simonenko.su/i/u/caniuse.png" target="_blank"><img alt="Поддержка свойства box-sizing в браузерах" height="230" src="http://simonenko.su/i/u/caniuse-thumb.png" title="Поддержка свойства box-sizing в браузерах" width="500" itemprop="image"></a></p>
<p>Зная это, я полностью согласен с <a href="http://paulirish.com/2012/box-sizing-border-box-ftw/" target="_blank" rel="nofollow">Paul Irish</a>. Не мучайте себя, укажите для всего документа <code>box-sizing</code>.</p>
<pre><code>* {</code>
<code> -webkit-box-sizing: border-box;</code>
<code> -moz-box-sizing: border-box;</code>
<code> box-sizing: border-box;</code>
<code>}</code>
</pre>
<p>Кстати, на этом блоге именно такой способ и используется.</p>
<h2>Какие могут быть проблемы</h2>
<p><strong>Проблема №1:</strong> свойство не поддерживается в IE 6-7.<br/><strong>Решение:</strong> подключить <a href="https://github.com/Schepp/box-sizing-polyfill" target="_blank" rel="nofollow">polyfill</a> исправляющий поддержку свойства или использовать условные комментарии с отдельным стилевым файлом.</p>
<p><strong>Проблема №2:</strong> ошибка работы <code>min-height</code>, <code>max-height</code> с <code>box-sizing</code> в Firefox.<br/><strong>Решение:</strong> помнить об этом и не использовать эти свойства с <code>box-sizing</code> или дождаться выхода <a href="https://developer.mozilla.org/en-US/docs/Firefox_17_for_developers" target="_blank" rel="nofollow">Firefox 17</a> в котором этот баг (<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=308801" target="_blank" rel="nofollow">308801</a>) уже исправлен.</p>]]></description><link>http://simonenko.su/32197993404/use-css3-box-sizing</link><guid isPermaLink="true">http://simonenko.su/32197993404/use-css3-box-sizing</guid><dc:creator><![CDATA[Алексей Симоненко]]></dc:creator><pubDate>Mon, 24 Sep 2012 20:50:00 GMT</pubDate></item></channel></rss>