Заметки за 2009 год (страница 6)

Lightbox: презентационный инструмент

На мой взгляд, одним из эффектных презентационных инструментов для документов или изображений является Lightbox. Содержимое основной страницы затеняется или осветляется, и поверх него накладывается слой, например, с изображением.

Архив с исходным кодом и файлами изображений

Конструктор

  • new LightBox(options);

    Создает новый объект Lightbox с указанными параметрами или значениями по-умолчанию.

Методы

  • show(options)

    Отображает Lightbox с заданными параметрами или параметрами по-умолчанию.

  • hide()

    Закрывает Lightbox. Этот метод автоматически вызывается после нажатия пользователем значка «Закрыть»

Конфигурационные параметры

  • baseUrl

    Полный путь до оформления. Необходимость в этом параметре в основном вызвана тем, что для правильного отображения полупрозрачных PNG в IE6 используется фильтр AlphaImageLoader.

  • opacity

    Степень прозрачности подложки от 0 до 1.

  • color

    Цвет подложки – любое допустимое значение CSS.

  • showCallback

    Функция, которая будет вызвана после отображения Lightbox. В качестве аргумента функции передается DOMNode контейнера.

  • hideCallback

    Функция, которая будет вызвана после закрытия Lightbox.

  • dimensionsCallback

    Функция, которая возвращает требуемые значения ширины и высоты контейнера. Если она не будет определена, то контейнер будет занимать 80% от размеров окна по ширине и высоте.

Чтобы изменить тень, отбрасываемую контейнером, создайте аналогичное рисунку изображение и разрежьте его на 8 частей. Части тени нужно сохранить в формате PNG-24 с прозрачностью.

Шаблон для тени

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

Пример демонстрирует использования Lightbox в качестве просмотрщика изображений с Flickr.


var lb = new LightBox({
    'dimensionsCallback': function () {
        return {
            width: 240,
            height: 160
        };
    },
    'showCallback': function (node) {
        $.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?tags=apple,mac&tagmode=all&format=json&jsoncallback=?",
                function(data) {
                    $(node).append('<img src="' + data.items[0].media.m + '" title="' + data.items[0].title + '" />');
                });
    }
});

lb.show();

Оставте свой комментарий

Использование prototype

Принципиально отличающимся от замыканий шаблоном написания модулей является объявление полей и методов через prototype.


var Product = function (name, cost) {
    this.name = name;
    this.cost = cost;
};

Product.prototype.draw = function () {
    // Комплексный метод создания представления продукта
    // Доступ к полям экземпляра осуществляется только через this
};

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

К недостаткам же можно отнести:

  • полное отсутствие частных полей (как правило, программисты оформляют такие поля символом «_» перед именем);
  • доступ к полям экземпляра осуществляется только через this, так как это единственная связующая переменная между привилегированными и общими методами.

Если все-таки важно иметь действительно частные поля, защищенные от доступа извне, то запросто можно скомбинировать этот прием с замыканием.


var Product = function (name, cost) {
    // частная переменная
    var id = name + '-' + new Date().getTime();
    // привилегированный метод для доступа к частной переменной
    this.getId = function () {
        return id;
    };
    // привилегированные поля
    this.name = name;
    this.cost = cost;
};

Оставте свой комментарий

Замыкания в JavaScript

Механизм, который позволяет скрывать переменные и методы реализуется благодаря замыканиям (сlosures). Внутренняя функция всегда имеет доступ к переменным и параметрам своей внешней функции, даже после того как внешняя функция закончила выполняться. Это чрезвычайно мощное свойство языка.

var Module = function () {
    var private = 100;
    this.getPrivate = function () {
        return private;
    };
};

Единственным способом получить доступ к переменной private — это вызвать привилегированный метод getPrivate . В этом примере замыканием служит первая функция, которая ограничивает область видимости.

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

Другое применение замыкание находит в обработчиках событий.

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


function drawList(items) {
    var i, li;
    for (i = 0; i < items.length; i++) {
        li = $('<li class="item">' + items[i].name + '</li>').appendTo(root);
        li.click(function () {
            alert(items[i].data);
        });
    }
}

Этот пример не будет работать, как мы ожидаем потому, что переменная i внутри обработчика события click не соответствует порядковому номеру элемента в списке. Значение этой переменной всегда будет равно items.length.

Чтобы пример заработал, обернем обработчик в замыкание.

(function (i) {
    li.click(function () {
        alert(items[i].data);
    });
})(i);

Или можно сразу получить требуемый элемент.


(function (item) {
    li.click(function () {
        alert(item.data);
    });
})(items[i]);

Можно заметить, что замыкания — это просто анонимные функции. Нам нет нужды выносить эти вспомогательные функции на основной уровень и тем более давать им имена.

Оставте свой комментарий

Конструкторы в JavaScript

В JavaScript нет понятия класс, но есть конструкторы, с помощью которых можно реализовать функцию класса, такую, например, как контейнер переменных и методов. Объекты класса в JavaScript могут иметь скрытые переменные и методы, а так же поддерживают не только классическое наследование, но и другие модели повторного использования кода.

Конструктор представляет собой обычную функцию.

var Module = function () {
};

или

function Module() {
}

Эти две записи тождественны. Я все-таки предпочитаю использовать первый вариант, так как он позволяет объявить функцию с достаточно сложным названием. Например, App.Package1.Module2 .

Для создания нового объекта конструктор вызывается через оператор new.

var instance = new Module();

Оператор new меняет значение переменной this внутри конструктора. В отличие от его обычного значения, this будет новым объектом. Тело конструктора обычно инициализирует поля объекта. По завершению конструктор вернет этот новый объект, если точка выхода не будет явно переопределена через оператор return.

Некоторый смущающий момент заключается в том, что если конструктор будет вызван без оператора new, то this уже не будет новым объектом, а будет ссылаться на window!

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


var Module = function () {
    var me = {};
    return me;
};

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

Оставте свой комментарий

Система оповещения модулей

Приятно наблюдать за тем как развиваются языки программирования, за появлением различных фреймворков и паттернов. Так в моем любимом JavaScript в последнее время появилась хорошая тенденция оформлять код в виде модулей (классов). Это обеспечивает и лучшую читаемость программ и повышает повторное использование кода.

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


/**
 *  Observable Object
 *  Just call it in constructor of your class (delegate pattern)
 *  Ex: var me = Observable(); // create new object
 *      or
 *      Observable(me);        // extend existing object
 */
var Observable = function (target) {
    // callback holder
    var observers = {};
    // create new object if needed
    target = target || {};

    target.attachObserver = function (eventType, callback) {
        if (!observers[eventType]) {
            observers[eventType] = [];
        }
        observers[eventType].push(callback);
        return this;
    };

    target.detachObserver = function (eventType, callback) {
        var a, i;
        if (eventType) {
            if (observers[eventType] && callback) {
                a = observers[eventType];
                for (i = 0; i < a.length; i++) {
                    if (a[i] === callback) {
                        a.splice(i, 1);
                        break;
                    }
                }
            } else {
                delete observers[eventType];
            }
        } else {
            observers = {};
        }
        return this;
    };

    target.notify = function () {
        var a, i, handler, data = [];
        // copy notification arguments
        Array.prototype.push.apply(data, arguments);
        a = observers[data.shift()];
        if (a && a.length > 0) {
            for (i = 0; i < a.length; i++) {
                handler = a[i];
                if (typeof handler === 'string') {
                    handler = this[handler];
                }
                if (handler instanceof Function) {
                    handler.apply(this, data);
                }
            }
        }
        return this;
    };

    return target;
};

Этот компонент при инициализации создает в указанном объекте 3 метода:

  • attachObserver — добавляет колбек для определенного события
  • detachObserver — удаляет один или все колбеки для определенного события
  • notify — инициализирует событие, при этом вызываются все зарегистрированные колбеки с указанными аргументами

Все указанные методы поддерживают цепочный вызов.


var FieldObject = function (root) {
    var me = Observable(this);

    $(root).append('<div class="field"><input type="text" value="" /></div>');
    $('div.field input', root).change(function () {
        me.notify('valueDidChange', this.value);
    });

    return me;
};

FieldObject($('body'))
    .attachObserver('valueDidChange', function (data) {
        alert(data);
    });

В этом примере я создал простой программный компонент FieldObject, которой добавляет на страницу текстовое поле, а затем подписался на событие valueDidChange , которое случается при обновлении этого текстового поля. Весь фокус в том, что я могу не знать, как устроен этот объект, и откуда получить значение его текстового поля. Все что мне нужно – это отреагировать на изменение поля.

Оставте свой комментарий