Заметки в категории «JavaScript» (страница 15)

Запомнить порядок сортировки jQuery UI Sortable

В сервисе Google «Вопросы и ответы» одним пользователем был задан этот вопрос и, к моему удивлению, очень долго никто не давал на него ответа. А на самом деле эта задача очень просто решается благодаря встроенному методу serialize.

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

Сохраняем ссылку на корневой элемент сортируемого списка. Он не однократно потребуется нам в дальнейшем. Хоть эта функция и работает очень быстро, но так получить доступ можно будет еще быстрее.

var root = $('#items');

Назначаем всем элементам списка уникальные идентификаторы. Это обязательное условие правильной работы метода serialize . Префикс может быть произвольным, но в наших интересах, чтобы он был как можно короче. В качестве разделителя можно использовать символы подчеркивание, равенство или дефис.

$('> *', root).each(function (index) {
    this.id = 'item-' + index;
});

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


root.sortable({
    'update': function (event, ui) {
        var order = $(this).sortable('serialize');
        $.cookies.set('sortable', order);
    }
});

Восстанавливаем порядок элементов списка после загрузки страницы.


var c = $.cookies.get('sortable');
if (c) {
    $.each(c.split('&'), function () {
        var id = this.replace('[]=', '-');
        $('#' + id).appendTo(root);
    });
}

Пробегаемся по всем элементам, сохраненным в cookie , и перемещаем их в конец списка. Если был сохранен порядок для всех элементов, то они естественным образом займут свои места. Если для каких-то элементов позиция не сохранялась, то они «всплывут» наверх списка.

Отдельно хочу заметить, что в этом примере вместо штатного API для доступа к cookies использовался плагин, который значительно облегчил работу с ними.

Комментарии к заметке: 9

Презентация картинок в виде слайдшоу

Как сделать красивое оформление для презентации я писал в заметке о Lightbox . А теперь расскажу, как собственно можно сделать саму презентацию.

Поставщиком картинок у нас будет сервис Flickr.


/**
 * Flickr photos provider
 */
(function () {

    var FLICKR_KEY = '4e1a2666b38001e7cb295852430274fe';
    var FLICKR_PREFIX = 'flickr-photo-';

Создаем замыкание, ограничивающее область видимости переменных и объявляем некоторые константы.

Замечу, что FLICKR_KEY содержит уникальный ключ Flickr API, который каждый разработчик должен бесплатно получить для своего приложения.

    APP.Flickr = function () {
        var me = Observable(this);
        me.init();
    };

Конструктор нашего поставщика. Он так же заботится об инициализации сервиса в целом. Для связи сервиса с приложением используется система событий.


    APP.Flickr.prototype = {
        init: function () {
            this.imageCache = document.createElement('div');
            $(this.imageCache)
                .attr('id', FLICKR_PREFIX + 'holder')
                .css({
                    'position': 'absolute',
                    'width': 0,
                    'height': 0,
                    'overflow': 'hidden'
                })
                .appendTo('body');
        },

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


        load: function (tags) {
            var me = this;
            $.getJSON('http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=' + FLICKR_KEY + '&format=json&tags=' + tags + '&content_type=1&jsoncallback=?',
                function(data) {
                    me.photos = data.photos;
                    me.notify('photosDidLoad', me.photos);
                }
            );
        },

Загружаем список картинок с заданными критериями поиска. Для взаимодействия с Flickr используется команда flickr.photos.search и протокол обмена информацией JSON.

Метод генерирует событие photosDidLoad, после удачного получения списка.


        showPhoto: function (index) {
            var me = this;
            var photo = me.photos.photo[index], img;
            var cached = $('#' + FLICKR_PREFIX + photo.id);
            if (cached.length === 0) {
                img = $('<img id="' + FLICKR_PREFIX + photo.id + '" title="' + photo.title + '" class="photo" />')
                        .appendTo(this.imageCache)
                        .load(function () {
                            me.notify('imageReady', img);
                        })
                        .attr('src', 'http://farm' + photo.farm + '.static.flickr.com/' + photo.server + '/' + photo.id + '_' + photo.secret + '.jpg');
            } else {
                me.notify('imageReady', cached);
            }
        }
    };
})();

Загружаем изображение из списка или берем его из кеша, если оно было уже загружено ранее.

Метод генерирует событие imageReady и передает подписчикам изображение, когда оно будет готово к использованию.

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


$(function () {
    var provider = new APP.Flickr(),
        wait = false;
    provider.attachObserver('photosDidLoad', function (data) {
        var i = 0;
        (function () {
            i = (i >= data.length) ? 0 : i;
            if (!wait) {
                wait = true;
                provider.showPhoto(i++);
            }
            setTimeout(arguments.callee, 2000);
        })();
    });

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

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


    provider.attachObserver('imageReady', function (img) {
        $('#showroom img.photo')
                .animate({
                    'opacity': 0
                }, function () {
                    $(provider.imageCache).append(this);
                });
        $(img).appendTo('#showroom')
                .css({
                    'opacity': 0
                })
                .centering()
                .animate({
                    'opacity': 1
                }, function () {
                    wait = false;
                });
    });

Как только очередная картинка будет загружена, она центрируется на странице и ее прозрачность плавно меняется с 0 до 1. Тем временем, у текущей картинки меняется прозрачность до 0 и затем она отправляется в кеш.


    provider.load('apple,mac');
});

Загружаем список картинок с тегами «apple» и «mac».

Слайдшоу начнется сразу после загрузки страницы и будет продолжаться до тех пор, пока страница не будет закрыта.

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

How To: Проверить используется летнее время или нет

Тема часовых поясов в отношении веб-страниц поднимается очень редко. Иногда все же приходится вычислять часовые пояса в JavaScript.

Как правило, на странице можно получить текущее время пользователя, которое может быть преобразовано в «универсальное координированное время». Но это время может быть как «зимним», так и «летним » (с поправкой на 1 час). Чтобы проверить, нужно ли использовать поправку или нет, сделаем сравнения часовых поясов для данного региона зимой и летом. В качестве тестовых точек возьмем даты «1 февраля» и «1 августа».


var s,
    now = new Date(),
    nowTZ = now.getTimezoneOffset(),
    winterTZ = new Date(now.getFullYear(), 1, 1).getTimezoneOffset(),
    summerTZ = new Date(now.getFullYear(), 7, 1).getTimezoneOffset();

if (winterTZ === summerTZ) {
    s = 'no daylight saving';
} else {
    if (nowTZ === winterTZ) {
        s = 'winter time';
    } else if (nowTZ === summerTZ) {
        s = 'summer time';
    } else {
        throw new Error('Unknown time zone');
    }
}

В некоторых странах (например, Япония, Китай, большая часть Африки и Южной Америки) перевод на «летнее время» не используется и это можно определить, если часовой пояс зимой и летом не будет отличаться. Если переход на «летнее время» все-таки используется, то проверяем, какой часовой пояс сейчас используется – летний или зимний.

Обновление от 31 октября 2011 года : В России с 5 августа 2011 года вступил в силу «Закон об исчислении времени», отменяющий сезонный перевод часов. Печальной новостью стало то, что в Windows (начиная с XP и заканчивая 7 со всеми последними обновлениями) часовые пояса для дат в прошлом определяются не верно. В браузерах, запущенных нa OSX или Linux, такой проблемы с часовыми поясами для дат в прошлом не возникает.

Комментарии к заметке: 9

jQuery UI виджет, имитирующий кнопку

В статьях jQuery UI API и Фабрика элементов интерфейса jQuery UI я кратко рассказал о внутреннем устройстве надстройки jQuery UI. На примере кнопки я покажу, как можно писать очень полезные виджеты.

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

Объявляем наш виджет.

$.widget("trv.button", {

Так как я его изначально писал для проекта под кодовым названием «TRAVEL», то и пространство имен у меня будет особое, а не стандартное «ui». Если вы будете использовать свое пространство имен (а я настоятельно рекомендую вам это, чтобы избежать путаницы), то не забудьте переименовать классы CSS.

Далее объявляем инициализирующий метод нашего будущего виджета.


    _init: function() {
        var me = this;
        me.element
            .bind('mousedown.' + me.name, function() {
                me._down();
                return false;
            })
            .bind('mouseup.' + me.name, function () {
                me._up();
                return false;
            })
            .bind('click.' + me.name, function(event) {
                if (!me._getData('disabled')) {
                    me._trigger('press', event);
                }
                return false;
            });
    },

В этом методе подписываемся на события, связанные с нажатием левой кнопки мыши — «mousedown», «mouseup» и «click». Я не буду создавать дополнительные элементов для оформления кнопки, так-как предполагаю, что вся необходимая верстка присутствует на странице.


    destroy: function() {
        var me = this;
        me.element
            .unbind('mousedown.' + me.name)
            .unbind('mouseup.' + me.name)
            .unbind('click.' + me.name);
    },

Лично я в своей практике не применял метод удаления виджета, но тем не мене создал его, как предписывает руководство.

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


    _down: function () {
        var me = this;

        if (!me._getData('disabled')) {
            me.element
                .addClass(me.widgetBaseClass + '-pressed')
                .one('mouseout.' + me.name, function () {
                    me._up();
                    me.element.one('mouseover.' + me.name, function () {
                        me._down();
                    });
                });

            $(document).one('mouseup.' + me.name, function () {
                me._up();
            });
        }
    },

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

Стоит заметить, что событие «mouseup» ловится на документе потому, что когда указатель мышки покинет элемент, то он уже не сможет ловить это событие.


    _up: function () {
        var me = this;
        me.element
            .unbind('mouseout.' + me.name)
            .unbind('mouseover.' + me.name)
            .removeClass(this.widgetBaseClass + '-pressed');
    }

Убираем дополнительный CSS класс с элемента, чтобы вернуть кнопку в исходное состояние, и отписываемся от событий «mouseout» и «mouseover».

});

Верстка кнопки может варьироваться вне зависимости от кода. Например, я использую такую:


<span class="trv-button">
    <span class="first-child">
        <em>Press me!</em>
    </span>
</span>

Кнопка полностью состоит из строчных элементов и теоретически может быть размещена внутри текста.

Когда на нее нажимают, то к основному элементу добавляется класс «trv-button-press». Этот класс автоматически создается из названия виджета и суффикса «-press».

Отслеживать нажатие кнопки можно через функцию обратного вызова «press» или соответствующее событие «buttonpress». Выбирайте, что вам больше нравится.

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

Фабрика элементов интерфейса jQuery UI

Обновление: Некоторые свойства и методы фабрики в версии 1.8 были изменены. Данная статья актуальна только для версий jQuery UI 1.7.x

Фабрика элементов интерфейса или, как их еще называют, виджетов (widget) используется для создания всех элементов jQuery UI . Она защищает от попыток создать несколько экземпляров виджета в одном элементе и от вызова скрытых методов (помеченных подчеркиванием).

Элементы интерфейса создаются функцией $.widget, в качестве параметров которой передается название и прототип:

$.widget('ui.myWidget', { … });

Свойства

  • this.element

    Элемент, который используется для создания экземпляра плагина. Это поле всегда содержит jQuery объект с одним элементом. Если при создании виджета была использована выборка с несколькими элементами, то для каждого элемента из коллекции будет создан свой экземпляр.

  • this.options

    Настройки экземпляра, которые при создании объединяются с настройками по умолчанию, определенными в $.ui.myWidget.defaults . Если у плагина есть какие-то метаданные, то они тоже объединяются с настройками.

  • this.namespace

    Пространство имен плагина (в нашем примере это «ui»). Обычно не используется внутри плагинов.

  • this.name

    Имя плагина (например, «myWidget»). Также редко используется внутри плагина как и пространство имен.

  • this.widgetEventPrefix

    Это свойство используется для определения названия событий, которые генерирует плагин. Например, у диалогового окна есть функция обратного вызова callback close . Когда выполняется эта функция, генерируется событие «dialogclose». Название события — конкатенированные префикс события и название функции обратного вызова. Префикс по умолчанию совпадает с названием виджета, но может быть заменен. Например, для плагина draggable были бы не приемлемы названия событий «draggablestart» и т.п. По этому, меняя префикс на «drag», получаем названия «dragstart» и т.п. Требуемое значение нужно установить $.ui.myWidget.eventPrefix.

  • this.widgetBaseClass

    Это полезное свойство помогает создавать названия CSS классов элементов внутри вашего виджета. Например, чтобы отметить элемент как «активный» вы делаете

    
    element.addClass(this.widgetBaseClass + '-active').
    

    Это свойство широко используется в фабрике и абстрактных плагинах типа $.ui.mouse.

Методы

  • _init

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

  • destroy

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

  • option

    Используется для получения и изменения настроек плагина после создания экземпляра. Сигнатура метода аналогична css и attr . Вы можете указать только имя, чтобы получить значение настройки, имя и значение, чтобы изменить настройку или объект для изменения нескольких настроек. Этот метод внутри вызывает _getData или _setData.

  • _getData

    Вызывается, когда пользователь получает значения настроек или через метод option. Это метод никогда не должен переопределяться плагином.

  • _setData

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

  • enable

    Вспомогательный метод, который вызывает _setData(‘disabled’, false) .

  • disable

    Вспомогательный метод, который вызывает _setData(‘disabled’, true).

  • _trigger

    Метод должен использоваться для запуска функций обратного вызова. Обязательным параметром метода является название функции, который нужно выполнить. Все функции так же генерируют события. Так же вы можете передать объект event, который вызвал запуск. Например, событие перетаскивания «drag» было вызвано событием «mousemove», поэтому в метод _trigger должно быть передано исходное значение события. Третьим параметром может быть произвольные данные, которые передаются в функцию и обработчик события.

Когда плагин переопределяет методы из $.widget.prototype, то оригинальные методы тоже должны быть вызваны:


$.widget('ui.myPlugin', {
    destroy: function() {
        this.element.removeAttr('something');
        $.widget.prototype.destroy.apply(this, arguments);
    }
});

Источник: http://wiki.jqueryui.com/Widget-factory

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