Заметки за июнь 2009 года

Обработчики события и функции обратного вызова в jQuery UI

Интересной особенностью jQuery UI для меня стала обработка событий и запуск функций обратного вызова. У обычных событий цепочки функций-обработчиков выполняются от начала и до конца или пока не будут прерваны через метод stopImmediatePropagation. К сожалению, в отличие от YUI 3, в API jQuery не предоставлена возможность определить, когда закончится выполняться эта цепочка. Зато в jQuery UI виджеты генерируют события, в которых такая возможность есть.

При конфигурировании виджета можно задать функцию обратного вызова (callback) для любого события, а так же подписаться на это событие извне. По сути, callback и цепочка обработчиков вызываются одним и тем же приватным методом _trigger. Но callback всегда вызывается последним вне зависимости от результата работы цепочки функций. А так как им обоим передается ссылка на одно и тот же событие, то в callback можно проверить статус события после обработчиков через методы isDefaultPrevented, isPropagationStopped или isImmediatePropagationStopped.

Более того, метод _trigger возвращает значение true, если все функции были выполнены и ни одна из них не вызвала preventDefault. В противном случае он вернет значение false. Это свойство фабрики виджетов тоже можно использовать для управления логикой работы событий.

Простой пример виджета:

$.widget("my.input", {
    _init: function () {
        var me = this;
        // подписываемся на некоторые события элемента input,
        // чтобы отслеживать его изменение
        me.element.find(".input").bind('keyup change blur', function (event) {
            var value = $(this).val(), opts = me.options, data;
            // исколючаем повторяющие события
            if (!opts.inProgress && value != opts.value) {
                try {
                    opts.inProgress = true;
                    data = {'current': value, 'last': opts.value};
                    me._change(data, me._trigger("change", event, data));
                } finally {
                    opts.inProgress = false;
                }
            }
        });
    },

    _change: function (data, status) {
        $(this.element).toggleClass("error", !status);
        this.options.value = data.current;
    }
});

$.extend($.my.input, {
    defaults: {
        value: "",
        inProgress: false
    }
});

$(function () {
    $("#demo")
            .input({'change': function (e, data) {
                // функция обратного вызова
                // будет выполнена самой последней при обработке события
                return data.current <= 1000;
            }})
            .bind('inputchange', function (e, data) {
                // специальный обработчик, который прерывает цепочку событий,
                // если встречается определенная строка — 007
                if (data.current == "007") {
                    e.stopImmediatePropagation();
                    return false;
                }
            })
            .bind('inputchange', function (e, data) {
                // еще один специальный обработчик
                $(".log", this).prepend("<p>" + data.current + "</p>");
            });
});

Этот виджет отслеживает вводимые в поле данные, ведет лог этих данных и осуществляет валидацию. В примере я использую метод stopImmediatePropagation, чтобы прервать выполнение цепочки. Если это специально не требуется, то лучше использовать preventDefault или возвращать false в обработчике.

Итак, если в поле ввести «007», то в лог ничего не запишется, а виджету будет назначен класс «error». Так же этот класс будет назначен, если значение в поле — число, больше 1000.

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

Клонирование элемента select

У элемента select в Internet Explorer есть неприятный побочный эффект. Если нужно клонировать опции одного элемента в другой, то не стоит это делать через свойство innerHTML. Лучше всего клонировать каждый элемент option по отдельности.

$(target).empty().append($("option", source).clone());
Оставте свой комментарий

Метод, возвращающий данные, в jQuery UI

При написании виджетов для jQuery UI важно не забыть объявить метод, который возвращает какие-то данные (getter). По умолчанию фабрика виджетов вернет ссылку на корневой элемент, чтобы обеспечить цепочечные вызовы.

Пусть у нас есть метод getFields, который должен вернуть коллекцию элементов, которые использует виджет.

$.widget("foo.bar", {
    …
    getFields: function () {
            var fields = [];
            $.each(this._fields, function () {
                fields.push(this.element);
            });
            return $(fields);
    }
});

Пока мы не сделаем такое объявление, метод будет возвращать не то, что мы ожидаем.

$.extend($.foo.bar, {
    getter: "getFields",
    defaults: {}
});

Если у виджета несколько таких методов, то их имена при описании разделяются пробелом.

Обновление: В jQuery UI 1.8 изменилась политика в отношении таких методов. Теперь не нужно специально объявлять их. Когда метод возвращает значение undefined, оно заменяется на корневой элемент экземпляра виджета, что позволяет строить вызовы цепочкой. В любом другом случае это значение передается без изменений.

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

Поиск сайтов по IP-адресу

Новый поисковик от Microsoft Bing может показать вам какие сайты хостятся на сервере с заданным IP-адресом, разумеется, если они были проиндексированы им. Например, чтобы узнать какие сайты расположены на хосте 78.108.81.40 пишем в строке поиска:

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

Верстаем формы

Красивые и ровные формы очень легко сверстать без использования таблиц.

Тестовая форма

Предположим, что все поля будут выровнены по левому краю, а надписи к ним располагаются слева и выровнены по правому краю.

Структура HTML, например, может быть такой:

<fieldset>
    <legend>Личные данные</legend>
    <div>
        <label for="lastname">Фамилия</label>
        <input type="text" value="" id="lastname" name="lastname"/>
    </div>
    <div>
        <label for="firstname">Имя</label>
        <input type="text" value="" id="firstname" name="firstname"/>
    </div>
    <div>
        <label for="middlename">Отчество</label>
        <input type="text" value="" id="middlename" name="middlename"/>
    </div>
    <div>
        <label for="birthday">Дата рождения</label>
        <input type="text" value="" id="birthday" name="birthday" class="date short"/>
        <p>день / месяц / год</p>
    </div>
    <div class="singleline">
        <label for="phone">Телефон</label>
        <span>+</span>
        <input type="text" value="" id="phone" name="phonecountry" class="compact"/>
        <span>(</span>
        <input type="text" value="" name="phonecity" class="compact"/>
        <span>)</span>
        <input type="text" value="" name="phonenumber" class="short"/>
        <p>код страны ( код города ) номер</p>
    </div>
</fieldset>

Не важно где будут находиться надписи в коде — до или после поля, так как они будут абсолютно позиционироваться с левой стороны. Задать отступ, где расположиться надпись можно либо с помощью свойства margin-left у элемента input, либо с помощью свойства padding-left у элемента div. Второй способ, на мой взгляд, предпочтительнее, так как комментарии к полю (элемент p) будут естественным образом выравниваться относительно него и не потребуется дополнительного оформления.

div.singleline {
    white-space: nowrap;
}

label {
    line-height: 1em;
    text-align: right;
    width: 160px;
    left: 0;
    top: 2px;
    position: absolute;
}

input, textarea {
    width: 200px;
}

input.short {
    width: 6em;
}

input.compact {
    width: 3em;
}

input.checkbox {
    width: auto;
    margin: 0;
    border: 0 none;
}

label.checkbox {
    width: 260px;
    top: 0;
    left: 190px;
    text-align: left;
}

div {
    padding-bottom: 0.3em;
    padding-left: 170px;
    position: relative;
}

p {
    margin: 0;
    font-size: 10px;
    width: 200px;
}

Для таких элементов как checkbox или radio естественно будет расположение надписи справа от него. По этому для таких случаев элементу label нужно добавить класс checkbox. В качестве альтернативного варианта, можно назначить ему стиль position: relative. В этом случае будет важно, чтобы в коде надпись следовала после поля.

Напоминаю, что форма отправляет только поля, у которых указан атрибут name. А элементы label ссылаются на поля по id.

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