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]

23.10.09

Домашка №2

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

20.10.09

Нет отрицательной логике

Что общего у этого кусочка яваскрипта (время от времени встречаю такое в чужом коде):

function init(noLoginForm) {
    if (!noLoginForm) {
        ...
    }
}

и этого фрагмента формы (постоянно сталкиваюсь на сайтах):

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

function init(showLoginForm) {
    if (showLoginForm) {
        ...
    }
}

(Не вижу ничего плохого в том, чтобы по умолчанию чекбокс был зажженным.)

Кстати, думаю, именно по этой причине в Гугл Ридире чекбокс Mark as unread переделали в Mark as read.

15.10.09

Домашка №1

По примеру Сергея тоже хочу загрузить вас домашкой :) Не пугайтесь, задания не сложные (пока). Итак, сегодня две задачки по яваскрипту.

Задача 1

Написать метод, который принимает два массива в качестве параметров и возвращает позицию, начиная с которой второй параметр входит как подмассив в первый параметр.

Пример. Первый массив: [4, 8, 15, 16, 23, 42], второй массив: [15, 16], результат: 2

Задача 2

Есть массив с числами. Нужно написать метод, который трансформирует исходный массив в массив функций, которые возвращают исходные числа.

Пример. На входе: [4, 8, 15], на выходе: [function() {return 4;}, function() {return 8;}, function() {return 15;}]

10.10.09

Откуда берутся веб-разработчики?

Думаете это обычный гимнастический мяч валяется возле моего рабочего стола? А вот и нет! Это яйцо, из которого скоро вылупится новый веб-разработчик.

10.09.09

Ajax со вкусом апельсина

Купил на днях в калифорнийском магазине вот такое средство для мытья посуды :)

28.08.09

Улыбаемся и машем

На мотив Yellow Submarine: «Запустили мы видеописьмо, видеописьмо, видеописьмо. Вместе дружно пробуем его, тестируем его, о-го-го-го-го!»

Теперь прямо в Яндекс.Почте можно записать видеоролик, который выложится на Яндекс.Видео, а секретная ссылка на этот ролик вставится в письмо. Все вместе это называется «видеописьмо».

06.08.09

Однопиксельные прозрачные уголки

Часто (особенно для небольших блоков) достаточно лишь обозначить скругления. Сделать это можно однопиксельными уголками, которые обычно реализуют с помощью четырех элементов абсолютно спозиционированных по углам блока.

Есть более крутой способ, суть его в следующем:

Упрощенный код для понимания идеи:

.corners {
    background-color: #80aaff;
    margin: 0 1px;
    padding: 1px 0;
}

.corners-i {
    background-color: #80aaff;
    margin: 0 -1px;
}

Чтобы применить этот подход к инлайновым и флоатным элементам и добиться кроссбраузерности, нужно написать еще немного CSS. Я подготовил страничку, где можно посмотреть детали реализации.

Достоинства способа:

  • Всего лишь один доп. элемент.
  • Уголки получаются прозрачные, а значит не нужно их «красить» в цвет фона.
  • Нет JS, экспрешенов, спрайтов.

Недостатком, конечно, является ограничение радиуса скругления в 1 пиксель :)

Рассмотренный способ отлично сочетается с бордер-радиусом, то есть, в браузерах поддерживающих border-radius используем border-radius, в остальных браузерах задействуем доп. элемент .corners-i и делаем однопиксельные уголки.

24.06.09

border-radius спешит на помощь

В соавторстве с Вадимом Макишвили

Для реализации скругленных уголков мы искали максимально легкое решение — отсутствие дополнительной разметки, минимум CSS и JS. Конечно, есть очень мощные средства, например, rocon, но нам они показались избыточными и громоздкими.

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

  • Firefox начиная с версии 1.0 (~24% пользователей)
  • Safari начиная с 3.0 + Chrome (~2%)
  • Konquerer (~0,05%)

Итого, для ~25% пользователей уголки можно рисовать средствами браузера — уже неплохо.

Смешно, но свойство border-radius, которое есть в CSS3, пока не поддерживается ни одним браузером. Однако, некоторые браузеры поддерживают аналогичные проприетарные свойства, например, -moz-border-radius в Firefox.

.corners {
    /* Возможно какой-нибудь браузер в будущем, будем надеяться IE */
    border-radius: 4px;
    /* WebKit (Safari/Chrome) */
    -webkit-border-radius: 4px;
    /* KHTML (Konquerer) */
    -khtml-border-radius: 4px;
    /* Возможно Opera в будущем */
    -opera-border-radius: 4px;
    /* Gecko (Firefox) */
    -moz-border-radius: 4px;
}

Для остальных браузеров, не поддерживающих border-radius, будем яваскриптом вставлять доп. элементы для реализации уголков.


// Проверяет, поддерживает ли элемент elem CSS-свойство border-radius.
var isBorderRadiusSupported = function (elem) {
    var s = elem.style;
    return typeof s.borderRadius === "string" ||
           typeof s.WebkitBorderRadius === "string" ||
           typeof s.KhtmlBorderRadius === "string" ||
           typeof s.OperaBorderRadius === "string" ||
           typeof s.MozBorderRadius === "string";
};

// Добавляет внутрь элемента elem доп. элементы реализующие уголки.
var wrapInCorners = function (elem) {
    // Реализуем уголки любым известным способом
    $(elem).prepend('<i class="lt"/><i class="rt"/><i class="lb"/><i class="rb"/>');
};

// Реализует закругленные уголки с помощью доп. элементов
// для браузеров не поддерживающих CSS-свойство border-radius.
var checkCorners = function () {
    // Ищем все элементы с классом corners
    $('.corners').each(function () {
        // Если для данного элемента браузер не поддерживает border-radius
        if (!isBorderRadiusSupported(this)) {
            // Добавляем внутрь элемента доп. элементы реализующие уголки
            wrapInCorners(this);
        }
    });
};

// DOM загрузился и готов к обходу и манипуляциям
$(function () {
    checkCorners();
});

Плюсы решения:

  • HTML льется чистый, без доп. элементов, и, соответственно, лишняя разметка не мозолит глаз ни в статической верстке, ни в XSL-шаблонах.
  • В прогрессивных браузерах (примерно у четверти пользователей) уголки рисуются средствами браузера (максимально быстро).
  • Есть задел на будущее — если IE или Opera надумают реализовать border-radius, то наше решение готово к этому радостному событию.

UPD: В комментариях указали на слабые стороны этого решения. Также откликнулись мои коллеги и помогли улучшить решение по производительности. Спасибо всем! Для этого мне и нужен блог — поделиться решением, собрать фидбек и найти более удачный вариант.

1. Не нужно проверять поддержку border-radius для каждого элемента. Сделаем эту проверку один раз в теге <head> до загрузки контента и добавим соответствующий класс у элемента <html>.

// Проверяет, поддерживает ли браузер CSS-свойство border-radius.
var isBorderRadiusSupported = function () {
    var s = document.documentElement.style;
    return typeof s.borderRadius === "string" ||
           typeof s.WebkitBorderRadius === "string" ||
           typeof s.KhtmlBorderRadius === "string" ||
           typeof s.MozBorderRadius === "string";
};

document.documentElement.className += isBorderRadiusSupported() ?
        " border-radius" : " no-border-radius";

2. Создавать из строки ноды под каждый уголок недешево, а зааппендить их в DOM еще дороже. Вставка каждого уголка это рефлоу блока, в который он вставляется. Выгоднее просто отдавать доп. разметку сразу в HTML. Задействовать доп. разметку будем в зависимости от класса, который мы выставили у элемента <html>.

.no-border-radius .corners .lt {
    // Стили для уголков
}

3. На текущий момент в Опере нет поддержки border-radius. Не стоит гонять балластный код ради неизвестного будущего. Поэтому отрываем -opera-border-radius: 4px; и соответствующую проверку в JS.

UPD2: Opera 10.50 pre-alpha поддерживает border-radius, причем без префикса. Ура!