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

Перезагрузка фоновых изображений в IE6

Несколько раз сталкивался с одной неприятной проблемой при отображении страниц в Internet Explorer 6. Эффект проявлялся на сложных страницах со всевозможным «хитрым» позиционированием и оформлением блоков. Я заметил, что браузер при обновлении страниц постоянно перегружает фоновые изображения.

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

Решением этой проблемы послужил следующий трюк.


html {
    filter: expression(document.execCommand("BackgroundImageCache", false, true));
}

Для любителей валидировать CSS это правило можно заключить в условный комментарий только для IE.


<!--[if IE]><link rel="stylesheet" type="text/css" media="all" href="/css/ie.css"></link><![endif]-->

Так же можно использовать альтернативный вариант в виде JavaScript.

try {
    document.execCommand("BackgroundImageCache", false, true);
} catch (err) {}

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

Sticky footer — позиционирование «подвала» страницы

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

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


html, body {
    height: 100%;
}
#wrapper {
    position: relative;
    min-height: 100%;
}
* html #wrapper {
    height: 100%;
}
#content {
    padding-bottom: 120px;
}
#footer {
    position: relative;
    margin: -100px auto 0 auto;
}

Макет страницы:


<html>
    <body>
        <div id="wrapper">
            <div id="content"></div>
        </div>
        <div id="footer"></div>
    </body>
</html>

Ключевым правилом, позиционирующим футер, является отрицательное значение margin-top . Когда контента мало, он естетсвенным образом, прикрепляется к нижней границе окна из-за действия height: 100% у тегов html и body . В случае, когда размер контента превышает размер окна, футер оказывается поверх блока-обертки, но padding-bottom заранее резервирует место для него.

Единственный недостаток решения состоит в том, что вы должны знать точную высоту футера.

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

Удаляем старые архивы Time Machine

Дома в качестве системы резервного копирования я использую Time Machine . Она создает свои архивы на том же диске, где я размещаю данные — фильмы, музыку и т.п. Проблема начинается, когда свободное место подходит к концу и нужно принять решение о том, что же удалить, чтобы освободить место на диске.

Как вручную удалять архивы Time Machine, к великому сожалению, в официальной документации не написано. Были различные сообщения на форумах, что можно спокойно удалять старые папки и это не нарушит целостности архива. Но как-то нет особого доверия таким сообщениям, если речь идет о резервных копиях. Зато хорошо известен факт, что Time Machine сама отлично освобождает для себя место на диске, если это нужно для создания очередной копии. Этим способом и нужно воспользоваться.

Допустим нам нужно освободить на диске 5Гб. Для этого создаем файл требуемого размера в домашнем каталоге.


dd bs=1000000 count=5000 of=~/zero.bin if=/dev/zero

Параметр bs задает размер блока в байтах, а count — количество таких блоков.

Как показала практика, обычно, Time Machine освобождает примерно в 2 раза больше места, чем ей нужно. По этому, файл можно создать в 2 раза меньше.

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

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

Привязка данных к объектам страницы

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

Для небольших виджетов я нашел оптимальным отделять этап создания представления и подключения контролера.


var obj = [], i;
obj.push('<div class="wrapper">');
for (i = 0; i < model.lenght; i++) {
    obj.push('<div class="item">');
    obj.push('<h1>', model[i].title, '</h1>');
    obj.push('<div class="input"><input type="text" value="', model[i].value, '" /></div>');
    obj.push('</div>');
}
obj.push('</div>');
$(root).html(obj.join(''));
$('div.input input', root).change(function () {
    // нужно сохранить значение в модель
});

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


(function () {
    var uidx = 1, storage = {};

    /**
     * Generate an id that is unique among application
     * @method guid
     * @param pre {String} optional guid prefix
     * @return {String} the guid
     */
    APP.guid = function (pre) {
        var p = (pre) || 'app';
        return p + '-' + uidx++;
    };

    /**
     * Make a save point for linking DOMNodes and Objects via IDs
     * @param obj {Object}
     * @param pre {String} optional guid prefix
     * @return {String} the guid
     */
    APP.savepoint = function (obj, pre) {
        var guid = APP.guid(pre);
        storage[guid] = obj;
        return guid;
    };

    /**
     * Recall data for save point
     * @param guid {String} id of save point
     * @return {Object} stored data
     */
    APP.recall = function (guid) {
        return (guid in storage) ? storage[guid] : null;
    };
})();

Теперь поле ввода я создаю с уникальным id.


obj.push('<div class="input"><input type="text" value="', model[i].value, '" id="', APP.savepoint(model[i]), '" /></div>');

А реализация обработчика выглядит не менее сложной.

function () {
    var o = APP.recall(this.id);
    o.value = this.value;
}

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

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

Динамическая загрузка JavaScript файлов

Техника динамической загрузки данных через XHR (в простонародии, AJAX ) очень популярна в современном WEB-е. Но вот динамическую загрузку программных компонент используют пока крайне редко. Компактный модуль ScriptLoader наглядно демонстрирует то, как это может быть просто и функционально.


/**
 * JavaScript loader
 * @param modules {Array} массив имен js файлов
 */
var ScriptLoader = function (modules) {
    this.modules = modules.slice();
    this.queue = new Queue();
    this._init();
};

ScriptLoader.prototype = {
    run: function () {
        // test for header ready
        var head = document.getElementsByTagName("head");
        if (head.length > 0) {
            this.queue.iterate();
        } else {
            setTimeout(arguments.callee, 500);
        }
    },

    _init: function () {
        var i, me = this;
        for (i = 0; i < me.modules.length; i++) {
            (function (name) {
                me.queue.add(function () {
                    var head, script;
                    head = document.getElementsByTagName("head");
                    if (head.length > 0) {
                        head = head[0];
                        script = document.createElement("script");
                        script.src = name;
                        script.type = "text/javascript";
                        script.onload = script.onreadystatechange = function () {
                            if ((!this.readyState || this.readyState == "loaded" || this.readyState == "complete") ) {
                                me.queue.iterate();
                                script.onload = script.onreadystatechange = null;
                                head.removeChild(script);
                            }
                        };
                        head.appendChild(script);
                    }
                });
            })(me.modules[i]);
        }
    }
};

Обратите внимание, что в этом модуле используется Queue.

Пример использования.


var loader = new ScriptLoader(["file1.js", "file2.js", "http://server.org/js/framework.js"]);
// на этом этапе в очередь могут быть добавлены дополнительные вызовы
loader.queue.add(function () {
    Module1.init();
});
loader.run();

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