04.08.11

Hunting for geeks

Помню в Яндекс.Почте мы прикололись и вывели в консоль браузера сообщение:
«Любишь заглядывать в консоль? А может и js умеешь писать? http://company.yandex.ru/job/vacancies/interface_dev_mail.xml».

А сегодня случайно заметил на TechCrunch в response headers:
X-hacker: If you're reading this, you should visit automattic.com/jobs and apply to join the fun, mention this header.

Nice.

21.07.11

Что мне не хватает в Гугл+, Твиттере, Фейсбуке и многих других сервисах

Слишком много шума.

В основную ленту попадают все посты от всех людей, которых я зафрендил/зафоловил. Я же хочу видеть там посты только от близких друзей и супер-интересных мне людей. Все остальное, менее важное, я посмотрю потом, когда поеду на метро и вдруг забуду дома книжку.

Покажите главное сначала. Дайте мне возможность контролировать что важно для меня.

Нет, все сервисы устроены наоборот — сначала вываливают тебе все говно на которое ты подписался, а ты продираешься сквозь него к важным для тебя постам. Почему ни один сервис не дает возможность начеррипикать важные для меня источники, сформировать must-read-ленту и поставить ее как основную?

Да, в Гугл+ можно сделать круг «Супер», а в Твиттере — список «Пупер». Дайте мне поставить это по-дефолту, чтобы я видел это сразу, заходя в сервис с любого компа и любого устройства.

14.07.11

Неблокирующая загрузка кнопки +1

Хотите установить на свой сайт гугловую кнопку +1? Имейте в виду, что она может негативно повлиять на скорость загрузки страниц. В частности, оригинальный сниппет содержит тег <script>, который блокирует рендеринг и загрузку последующих ресурсов на странице. К счастью, эта проблема легко решается.

<g:plusone></g:plusone> остается в том месте где должна появиться кнопка. Код неблокирующей загрузки лучше поместить перед закрывающим тегом </body>:

<script>
(function(d, t) {
    var g = d.createElement(t),
        s = d.getElementsByTagName(t)[0];
    g.async = true;
    g.src = 'https://apis.google.com/js/plusone.js';
    g.text = '{lang:"nl"}';
    s.parentNode.insertBefore(g, s);
}(document, 'script'));
</script>

Код неблокирующей загрузки написан Матиасом, пофиксен для IE мной.

UPDATE: Гугл выпустил неблокирующую версию кнопки +1.

25.02.10

Книги по клиентской оптимизации

Эти две книги Стива Содерса я прочитал еще осенью прошлого года. Вообще для меня вопросы клиентской оптимизации вышли на передний план в прошлом году, что отчасти связано с переходом в Яндекс, где степень повернутости на оптимизации гораздо выше, чем в Студии, отчасти с тем, что сейчас это в целом мощный тренд, и все сообщество веб-разработчиков повернуто на этой теме.

High Performance Web Sites: Essential Knowledge for Front-End Engineers by Steve SoudersSteve Souders - High Performance Web Sites: Essential Knowledge for Front-End Engineers

Думаю, что эта книга обязательна к прочтению всеми фронтэндерами. Она небольшая и содержит 14 правил, которые помогут сделать ваш сайт быстрее. Конечно, многие правила вы наверняка уже знаете или хотя бы слышали о них, однако после прочтения этой книги все обрывки знаний уложатся в систему, восполнятся какие-то пробелы. Скажем, для меня новыми стали знания про CDN (Content Delivery Networks) и ETags. В завершение Стив устраивает своеобразную «порку» десяти крупнейших сайтов и на примере показывает, как определять требующие улучшения места, и каких результатов можно добиться, применив описанные в книге правила.

Even Faster Web Sites by Steve SoudersSteve Souders - Even Faster Web Sites

Хотите выжать из своих сайтов еще больше производительности и каждая миллисекунда у вас на счету? Вторая книга Содерса предлагает нам самые передовые идеи по убыстрению сайтов. В первую очередь она понравится тем, у кого шило в одном месте кто любит экспериментировать. Зачастую в книге предлагается проблема, возможные подходы к ее решению, за и против каждого подхода, потенциальные засады и пути обхода, и под конец Стив суммирует все свои тесты в дерево решений, которое позволяет выбрать подходящий подход в зависимости от обстоятельств. Несмотря на то, что Содерс сам умеет увлекательно излагать материал, шесть глав написаны приглашенными экспертами (Крокфорд, Закас, Стефанов), что, несомненно, бодрит.

***

Тем временем загрузил в свою читалку две книги Николая Мациевского — «Разгони свой сайт» и «Реактивные веб-сайты». Плюс очень жду новую книгу Николаса Закаса High Performance JavaScript.

12.02.10

Cоздание элементов из HTML-строк в jQuery 1.4

Еще немного на тему простого шаблонирования в JavaScript

Используя jQuery, я довольно часто создаю элементы из HTML-строк с последующим добавлением атрибутов и навешиванием обработчиков событий. Примерно так:

$("<input/>")
    .attr({
        type: "text",
        name: "city"
    })
    .val("Moscow")
    .focusin(function() {
        $(this).addClass("active");
    })
    .focusout(function() {
        $(this).removeClass("active");
    })
    .appendTo("form");

В jQuery 1.4 есть приятное нововведение: вторым аргументом можно передать объект со свойствами создаваемого элемента. Код становится проще и аккуратнее:

$("<input/>", {
    type: "text",
    name: "city",
    val: "Moscow",
    focusin: function() {
        $(this).addClass("active");
    },
    focusout: function() {
        $(this).removeClass("active");
    }
}).appendTo("form");

Этот объект может содержать любые свойства понимаемые методом attr(), там же можно навесить обработчики событий, а также, не отходя от кассы, можно задать значения для методов val, css, html, text, data, width, height и offset.

Кроме того, в такой записи неплохо смотрится и более сложный шаблон с вложенным элементом:

$("<a/>", {
    href: "/test/",
    "class": "my_link",
    html: $("<img/>", {
        src: "test.gif",
        width: 60,
        height: 30
    }),
    click: function() {
        doSomething();
    }
}).appendTo("body");

27.01.10

JavaScript Shortcuts Library

Выложил библиотеку для создания клавиатурных шорткатов JavaScript Shortcuts Library (jQuery plugin). Пригодится при разработке больших веб-приложений, а также написании игр на JavaScript.

Библиотека приятна и проста в использовании. Добавляем шорткат:

$.Shortcuts.add({
    type: 'down',
    mask: 'Ctrl+A',
    handler: function() {
        debug('Ctrl+A');
    }
});

Добавляем еще один:

$.Shortcuts.add({
    type: 'up',
    mask: 'Shift+B',
    handler: function() {
        debug('Shift+B');
    }
});

Начинаем реагировать на шорткаты:

$.Shortcuts.start();

Поддерживаются три типа событий, по которым могут срабатывать обработчики:

  1. down — на нажатие клавиши или сочетания клавиш
  2. up — на отпускание
  3. hold — на нажатие и удержание (обработчик будет вызван сразу после нажатия и потом будет вызываться с некоторой периодичностью пока нажата клавиша)

Строка задающая сочетание клавиш должна состоять из имен клавиш разделенных плюсами. Может быть не более одной клавиши отличной от Ctrl, Shift или Alt. Строка нечувствительна к регистру. Примеры: "Esc", "Shift+Up", "ctrl+a".

Кроме того, можно создавать несколько списков шорткатов, а затем, в зависимости от ситуации на странице, активизировать определенный список.

Добавляем шорткат в список "another":

$.Shortcuts.add({
    type: 'hold',
    mask: 'Shift+Up',
    handler: function() {
        debug('Shift+Up');
    },
    list: 'another'
});

Переключаемся на список шорткатов "another":

$.Shortcuts.start('another');

Также можно удалять шорткаты:

$.Shortcuts.remove({
    type: 'hold',
    mask: 'Shift+Up',
    list: 'another'
});

Останавливаем работу шорткатов (делается unbind навешенных обработчиков событий):

$.Shortcuts.stop();

Поддерживаемые клавиши:

  • Модификаторы: Ctrl, Shift, Alt
  • Цифры: 0—9
  • Буквы: A—Z (case-insensitive)
  • Специальные клавиши: Backspace, Tab, Enter, Pause, CapsLock, Esc, Space, PageUp, PageDown, End, Home, Left, Up, Right, Down, Insert, Delete, F1—F12, ? (знак вопроса), минус, плюс

Буду рад замечаниям и предложениям по улучшению.

22.01.10

Простое шаблонирование в JavaScript

Задача: в JS на основе имеющихся данных сгенерить блок (кусок хтмл), заполнить его этими данными и вставить в DOM.

Например, нам нужно создать простой блок с превьюшкой и подписью. Для него мы каким-то образом получили вот такие данные:

var data = {
    url: '/test/',
    thumb_src: 'test.gif',
    thumb_width: 60,
    thumb_height: 30,
    caption: 'Трам-парам!'
};

В итоге для этих данных хотим получить вот такой хтмл:

<div class="preview"><p class="image"><a href="/test/"><img src="test.gif" width="60" height="30"/></a></p><p class="caption">Трам-парам!</p></div>

Встают вопросы: в каком виде хранить шаблон и как впердолить в этот шаблон наши данные?

Вручную создавать элементы (createElement) и аппендить их (appendChild) довольно муторно. Получается громоздко и шаблон размазывается тонким слоем по JS-коду.

Часто делают конкатенацию кусочков шаблона вперемешку с данными:

var result = '<div class="preview"><p class="image"><a href="' + data.url + '"><img src="' + data.thumb_src + '" width="' + data.thumb_width + '" height="' + data.thumb_height + '"/></a></p><p class="caption">' + data.caption + '</p></div>';

Тоже не ахти — постоянно путаешься в этом нагромождении одинарных и двойных кавычек, к тому же хочется все-таки иметь шаблон в чистом виде.

Я использую способ, который подсмотрел в лекциях Дугласа Крокфорда. Добавляем в прототипы объекта String метод supplant, который в строке ищет выражения заключенные в фигурные скобки {}. Каждое найденное выражение используется как ключ к переданному объекту, и, если по этому ключу лежит строковое или числовое значение, то выражение в фигурных скобках заменяется этим значением.

/**
 * supplant() does variable substitution on the string. It scans
 * through the string looking for expressions enclosed in {} braces.
 * If an expression is found, use it as a key on the object,
 * and if the key has a string value or number value, it is
 * substituted for the bracket expression and it repeats. 
 */
String.prototype.supplant = function(o) {
    return this.replace(/{([^{}]*)}/g,
        function(a, b) {
            var r = o[b];
            return typeof r === 'string' || typeof r === 'number' ? r : a;
        }
    );
};

Теперь мы можем сделать так:

var template = '<div class="preview"><p class="image"><a href="{url}"><img src="{thumb_src}" width="{thumb_width}" height="{thumb_height}"/></a></p><p class="caption">{caption}</p></div>';

var result = template.supplant(data);

Еще один пример (подстановка данных в сообщение пользователю):

var template = 'Take train {number} from {from} to {to}.';

var data = {
    from: 'Madrid',
    to: 'Barcelona',
    number: '78A'
};

var result = template.supplant(data);

05.01.10

Coders at Work

Coders at Work by Peter SeibelЭтот сборник интервью с известными программистами я решил прочитать по совету Джеффа Атвуда, автора клевого блога Coding Horror. Умение Питера Сибела задавать животрепещущие вопросы, помноженное на 15 самых влиятельных программистов, дало на выходе захватывающую книгу, от которой с трудом отрываешься только в четыре часа ночи.

Какие скиллы важны для программистов, и как их улучшить? Насколько важно образование, полученное в учебном заведении? На что собеседники Сибела обращают внимание при поиске и найме программистов? Как разобраться в чужом коде? Как улучшить совместную работу с коллегами? Эти и многие другие темы затронуты в книге. Помимо обсуждения общих вопросов, чуваки рассказывают увлекательные истории о проектах, благодаря которым они стали известными.

Чуть ли не всю книгу хочется цитировать. Я понавыписывал целую кучу цитат, и несколькими из них хочу поделиться с вами (из первых трех интервью с Завински, Фитцпатриком и Крокфордом).

Читать цитаты

21.12.09

input type="file" multiple

Оказывается, Safari 4, Chrome 3 и Firefox 3.6 Beta 4 поддерживают выбор сразу нескольких файлов с помощью атрибута multiple.

<input name="att" type="file" multiple="true"/>

Сафари рядом с кнопкой показывает количество выбранных файлов, плюс при поднесении мышки вылезает тултип с названиями файлов:

Еще более удивительно то, что Opera уже давно поддерживает multiple-файлы с помощью атрибутов min и max.

<input name="att" type="file" min="1" max="99"/>

При этом в Опере меняется название кнопки.

Обычный инпут:

С атрибутами min и max:

Очень хочется верить, что multiple заимплементируют в IE 9. Тогда можно будет, наконец, полностью отказаться от дурацкой флешки.

02.11.09

Результаты домашки №1

Ну что же, давайте подведем итоги первой домашки :)

Задача 1

Эта задача вызвала некоторые затруднения почти у всех принявших участие. Я заметил несколько типичных проблем:

1. Велосипедизм. Как верно заметил nop, задача сводится к классической задаче поиска подстроки в строке. Многие это почувствовали, однако поленились погуглить и решили изобрести свой велосипед. Существует огромное количество алгоритмов поиска подстроки, самый простейший (brute force algorithm) можно закодить так:

var subArrayIndex = function(haystack, needle) {
   var i, j;
   var m = haystack.length;
   var n = needle.length;

   for (i = 0; i <= m - n; i += 1) {
      for (j = 0; j < n && needle[j] === haystack[i + j]; j += 1);
      if (j >= n) {
          return i;
      }
   }

   return -1;
};

2. Проверка написанного кода только на входных данных приведенных в задании. Каждое второе решение выдавало правильный результат для массивов приведенных в задании в качестве примера, но оказывалось неработающим при небольшой модификации входных данных. Никогда не стоит сразу успокаиваться, получив верный результат для одного набора входных данных. Нужно подумать, в чем в задаче может быть засада и проверить всякие «дерьмовые» случаи — не сломают ли они ваш алгоритм.

3. Сравнение с приведением типов. Практически все для сравнения элементов массивов использовали == и !=. Ребята, если вы еще не знаете, знайте: в яваскрипте == и != делает приведение типов. Это означает, например, что 1 == '1' выдаст true. Для сравнения без приведения типов используйте === и !==.

Задача 2

Вторая задача была на замыкание и проблем не вызвала. Просто приведу свое решение:

var toArrayOfFunctions = function(array) {
    var makeFunction = function(item) {
        return function() {
            return item;
        };
    };
    var i;
    var len = array.length;
    var functions = [];
    for (i = 0; i < len; i += 1) {
        functions[i] = makeFunction(array[i]);
    }
    return functions;
};

UPD: В одном из решений второй задачи заметил еще проблему, которую назовем «порча входных данных». Нужно помнить, что объекты (массивы в том числе) передаются по ссылке, а не по значению. Если в функции вы делаете какие-то хирургические операции с принятым в аргументах объектом, то вы меняете непосредственно переданный объект.

Пример:

var add1 = function(array) {
    for (var i = 0; i < array.length; i += 1) {
        // Трах-бабах-хуяк - портим переданный массив
        array[i] = array[i] + 1;
    }
    return array;
};

var a = [1, 2, 3];
var b = add1(a); // a и b теперь один и тот же массив [2, 3, 4]