заметки верстальщика

Fixed fancybox

31.10.2009 Jman

Из-за того что мне нехватает времени написать свою галерею, пользуюсь в последнее время Fancybox, для тех кто не знаком — плагин jQuery для фотогалереи, очередной клон lightbox. Как у всего в этом мире есть приемущества и есть недостатки.
Основные приемущества — это конечно же настройки. Не надо передавать в скрипт никаких картинок, как в lightbox. Большая часть оформления через css. Приведу основные настройки.

padding
Позволяет задать отступ от картинки, по дефолту стоит 10px, нужно менять если меняете стили отображения рамочки
imageScale (true/false)
машатабировать изображение чтоб поместилось в окно или нет
zoomOpacity (true/false)
Анимировать прозрачность всплвыющего блока при анимации или нет
zoomSpeedIn, zoomSpeedOut, zoomSpeedChange
Скорость анимации в милисикундах (если 0 анимации нет) при открытии, закрытии и переключении картинки
easingIn, easingOut, easingChange
Управление плавностью, и ефектами анимации (нужен плагин jquery.easing)
frameWidth, frameHeight
ширина и высота фрейма (если открываем флеш, iframe, html)
overlayShow (true/false)
отображать подложку (по умолчанию не отображается)
overlayOpacity (от 0 до 1)
Прозрачность подложки
hideOnContentClick (true/false)
Прятать FancyBox, при клике на откртый блок
centerOnScroll (true/false)
Центровать картинку при скроле окна
itemArray ([массив])
Задать свой массив элементов
callbackOnStart, callbackOnShow, callbackOnClose
Вызов произвольной callback-функции по определённому событию — при старте , при отображениии, и при закрытии

Последнее особенно полезная настройка.
Но и проблем у него придостаточно, даже как для неискушённого пользователя-разработчика. Я укажу те что я смог исправить.

  1. первое попроще, но и столкнутся проще — неправильно отображается рамка если задан отступ через настройку padding
  2. при завершении работы fancybox, скрипт снимает обработчики событий scroll resize keydown

Кусок кода в котором заключается первая проблема

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
if (pad > 0) {
    width    += pad * 2;
    height   += pad * 2;

        $("#fancy_content").css({
        'top'    : pad + 'px',
        'right'  : pad + 'px',
        'bottom' : pad + 'px',
        'left'   : pad + 'px',
        'width'  : 'auto',
        'height' : 'auto'
    });

    if (isIE) {
        $("#fancy_content")[0].style.setExpression('height',    '(this.parentNode.clientHeight - 20)');
        $("#fancy_content")[0].style.setExpression('width', '(this.parentNode.clientWidth - 20)');
    }

} else {
    $("#fancy_content").css({
        'top'    : 0,
        'right'  : 0,
        'bottom' : 0,
        'left'   : 0,
        'width'  : '100%',
        'height' : '100%'
    });
}

Тут долго разбиратся и ненадо, если установлен padding то заполяем css атриуты 'top', 'right', 'bottom', 'left' значением отступа (padding), ширину и высоту ставим auto, такое значение MSIE6 не любит, поэтом для него отдельное правило, в котором и заключена ошибка.
По умолчанию padding = 10, в блоке для ие выставляется высота и ширина равная высоте и ширине родительского блока минус отсуп умноженный на 2, тоесть 20. Блин а если мы задали свой padding то пофиг получается :) . Исравляется легко, заменяем код для MSIE

1
2
3
4
if (isIE) {
    $("#fancy_content")[0].style.setExpression('height', '(this.parentNode.clientHeight - ' + pad * 2 + ')');
    $("#fancy_content")[0].style.setExpression('width',  '(this.parentNode.clientWidth - ' + pad * 2 + ')');
}

Вторая проблема возникла из-за способа которым автор устанавливает и удаляет обработчик событий. Просто и ненавящиво

1
2
3
4
//установил событие
$(window).bind("resize scroll", $.fn.fancybox.scrollBox);
//снял событие
$(window).unbind("resize scroll");

Очень мило со сторны скрипта, разбиндить resize и scroll которые которые назначены на окно. Удаляются все обработчики, и те что назначены фансибоксом и те что назначены другими плагинами или в ручную. Для того чтобы добится совместимости с скриптами правильней было бы сделать вот так

1
2
3
4
//установил событие
$(window).bind("resize scroll", $.fn.fancybox.scrollBox);
//снял событие
$(window).unbind("resize scroll", $.fn.fancybox.scrollBox);

Благо есть обработчик = хендлер. Но есть ещё одна проблема с обработкой события — обработка нажатия клавиш клавиатуры.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$(document).keydown(function(e) {
    if (e.keyCode == 27) {
        $.fn.fancybox.close();
        $(document).unbind("keydown");
    } else if(e.keyCode == 37 && opts.itemCurrent != 0) {
        opts.itemCurrent--;
        _change_item();
        $(document).unbind("keydown");
    } else if(e.keyCode == 39 && opts.itemCurrent != (opts.itemArray.length - 1)) {
        opts.itemCurrent++;
        _change_item();
        $(document).unbind("keydown");
    }
});

После каждого нажатия клавиши вызывается анониманая функция, в которой определяется какая клавиша была нажата и если это ESC, кусор влево или курсор вправо выполняет определённую функцию и удаляет обработчик события нажатия кнопки, и как свегда если у нас стоит свой обработчик на кнопки они удаляются тоже.
Есть два пути решения это й проблемы.
Переписать назначение обработчика, чтоб исключить аононимную функцию и использовать хендлер.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$(document).keydown(keyHandler(event));
function keyHandler(e){
    if (e.keyCode == 27) {
        $.fn.fancybox.close();
        $(document).unbind("keydown", keyHandler);
    } else if(e.keyCode == 37 && opts.itemCurrent != 0) {
        opts.itemCurrent--;
        _change_item();
        $(document).unbind("keydown", keyHandler);
    } else if(e.keyCode == 39 && opts.itemCurrent != (opts.itemArray.length - 1)) {
        opts.itemCurrent++;
        _change_item();
        $(document).unbind("keydown", keyHandler);
    }
}

И второй способ, который выбрал я (незнаю какой лучше) использует одну фичу в jQuery событиях - неймспейсы, вуаля:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$(document).bind("keydown.fancybox", function(e) {
    if (e.keyCode == 27) {
        $.fn.fancybox.close();
        $(document).unbind("keydown.fancybox");
    } else if(e.keyCode == 37 && opts.itemCurrent != 0) {
        opts.itemCurrent--;
        _change_item();
        $(document).unbind("keydown.fancybox");
    } else if(e.keyCode == 39 && opts.itemCurrent != (opts.itemArray.length - 1)) {
        opts.itemCurrent++;
        _change_item();
        $(document).unbind("keydown.fancybox");
    }
});

намного лучше fancybox не стал, но глобальные баги пофиксены. Свою версию выкладывать пока не буду, так как в том скрипте есть ещё много допилов под конкретный проект. Кому надо, тот может скачать с сайта автора, и внести нужные правки. Вполне возможно, что автор уже и сам всё исправил, так как я писал о найденных багах, и путях устранения в гугл-группу поддержки

Сайт проекта jQuery
Сайт проекта fancybox

Комментариев: 5 на запись “Fixed fancybox”

  1. Комментирует alexpts

    Из всех аналогичных скриптов понравился тоже fancybox и еще один, сейчас не могу найти название… где-то в закладках есть, там очень красиво перелистывались фотографии.

  2. Комментирует Jman

    2alexpts
    Неплохой скрипт — prettyPhoto. Но у него тоже куча проблем:
    1. плохо скролится
    2. непонятный алгоритм ресайза. (немного углублясь в код я понял что многие цыфры берутся с потолка).

  3. Комментирует alexpts

    prettyPhoto стоит на моем блоге alexpts.ru и my-wordpress.ru. Работает, но fancyBox симпотичнее, переделывать лень просто.

  4. Комментирует Владимир

    Здравствуйте :) У меня проблема такого рода. Всплывающие окно прячется под верхнее меню. Как сделать чтоб окно было поверх меню, а не под ним? Вот пример http://dominat-ua.com//images/pmm.jpg

  5. Комментирует warmrobot

    Спасибо, что нашли багу с unbind скролла для window.

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