Даркбокс

20 ноября 2008

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

И вроде решений для этого написано уйма — бери, да используй. Но вот беда — все они сделаны по принципу «plug-n-play», мол подключай и не парься. В итоге эти решения снабжены всевозможными фичами, которые позволяют открывать окошки с любым содержимым, передвигаться по галерее при помощи клавиатурных сокращений и многое-многое другое. Классно, ведь! Нет? Ну, не совсем — в итоге код такого плагина распухает от всех этих возможностей, которыми никто не будет пользоваться, и непонятных HTML-шаблонов, содержащих сомнительный код — и тут же стили к нему. А это это уже не слишком классно…

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

Начиналось всё с простых, но очень правильных принципов:

  • Данные, т.е. картинка, — в HTML
  • Оформление — в CSS
  • Динамика — в JS, с полной обратной совместимостью

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

Даркбокс — пример работы, весь код в одном файле.

Принципы работы

Очень полезно начинать описание задачи не с фраз вроде «а там такая штука крутится полупрозрачная, а потом пфф! и блёстки…». Поэтому я начал с простого: у меня есть просто ссылка на просто картинку, и только если у пользователя включён JS, ссылка не срабатывает, а открывается то самое полупрозрачное с блёстками.

Контейнер для открытия картинки представляет собой блок вырванный из потока и спозиционированный при помощи position:fixed. Внутри него расположено само полупрозрачное затемнение и блок загрузчика с крутящимся псевдо-датчиком загрузки. Все эти объекты создаются динамически и складываются в нужном порядке:

  1. <div class="popup-frame">
  2. <div class="popup-shadow"></div>
  3. <div class="popup-loader"></div>
  4. </div>

Это дело красиво появляется и дальше в загрузчик вкладываются пока невидимые картинка и кнопка закрытия. Атрибут src берётся из href ссылки, alt из её title:

  1. <img src="…" alt="…"/>
  2. <span title="Закрыть"></span>

Сразу после загрузки нашей картинки, загрузчику выдаётся класс popup-loaded, который отключает датчик загрузки. Дальше он плавно меняет свои размеры, смещение, прозрачность и, наконец, ещё раз сменив класс на popup-canvas, показывает нам картинку. А на тень, кнопку и Esc вешается обработчик закрытия всей этой красоты.

Таким образом, всё оформление, что не участвует в анимации, применяется при помощи CSS-классов, а не присутствует в JS явно, как очень любят делать все плагинописатели.

Неизбежное исключение

Да, чуть было не забыл про IE. Данному скрипту удалось угодить всем его актуальным версиям, начиная с 5.5 и заканчивая 8.0. Проблемы с position:fixed для IE6 и младше решаются при помощи expression:

  1. * HTML BODY {
  2. background:url(about:blank) fixed;
  3. }
  4. * HTML .popup-frame {
  5. position:absolute;
  6. top:expression(0+(
  7. (e=document.documentElement.scrollTop)
  8. ?e:document.body.scrollTop)+'px');
  9. }

Правило background:url(about:blank) fixed задаёт пустое фиксированное изображение для того, чтобы позиционирование блока в IE6 работало плавно, без рывков. Прозрачность для всех версий IE решается при помощи filter:alpha(opacity=N), а использование PNG-24 картинки для кнопки, столь же традиционно лечится для IE6 с помощью AlphaImageLoader.

В целом, написание скрипта прошло гладко, как по учебнику, за исключением выходки браузера Opera. Ему, видите ли, не получить размеры динамически созданной и уже загруженной картинки, пока она не будет вставлена в документ. Даже IE5 ничего подобного не вытворял, в прочем, мне не жалко.

Итак, перед вами не претендующее на красоту и универсальность, однако, простое и эффективное решение для открытия картинок в стиле вебдваноль. Если кому-то пригодится — буду только рад.

19 декабря 2008: благодаря внимательности читателей, поправлены некоторые ошибки в IE6, в частности — методика борьбы с дрожащим фоном и expression теперь универсальны и работают как в Standard Mode, так и в Quirks Mode.

Комментарии: добавить

rsboarder 20 ноября 2008 / 16:39

Спасибо. Единственное НО: мне кажется, что привычнее "закрыть" делать справа от картинки. Когда хотел закрыть рука на автомате потянулась в правый верхний угол. имхо

Дмитрий 20 ноября 2008 / 17:07

Очень здорово!

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

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

Было бы это - я бы забыл про лайтбокс.

Дмитрий 20 ноября 2008 / 17:09

...так сказать, перешел бы на темную сторону.)

Эди 20 ноября 2008 / 17:16

Вадим, было не плохо иметь метки к записям и к этой в частности solution (к примеру), чтобы можно было посмотреть все ваши решения по разным проблемам )

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

chestah 20 ноября 2008 / 17:27

здорово!
осталось добавить поддержку массивов изображений для галлерей

Дмитрий 20 ноября 2008 / 17:32

любопыственно.
попробуем.

Денис 20 ноября 2008 / 17:40

А зачем создавать элементы при открытии картинки? Во первых, это задержит пользователя (немножко), а во вторых — будет создавать всё новые элементы при каждом нажатии. Звучит неэкономно.

engel 20 ноября 2008 / 17:59

Ага! То есть, все-таки, полгода нытья «напиши мне плагин для картинок» — мне, а вся слава в результате тебе?!

cogonet 20 ноября 2008 / 18:31

А если на странице будут присутствовать select-ы они будут перекрыватся в IE6 ? )

enternet 20 ноября 2008 / 18:56

IE8b2, режим совместимости со стандартами - кнопка закрытия в демке попадает девушке на лоб.

Ви 20 ноября 2008 / 23:19

Если, честно, скрипт на 4 с натяжкой (если расценивать не как «просто мозг размять», а как претендент на публичный проект):
1) как заметил cogone, в IE 6 select'ы будут просвечивать через фон и box
2) наверно, лучше бы, добавлять динамический контент один раз (например при первой загрузки darkbox'а или при загрузки страницы)
3) для распространения и использования, код удобнее бы обернуть в класс или plug-in для jQuery.

Плюс, если уж начал придираться, то: если картинка по размеру больше чем окно браузера, то darkbox «улетает» за границы. :))

Dmitry Baranovskiy 21 ноября 2008 / 3:27

Ви прав, динамический контент нужно добавлять один раз, а не при каждом попапе. Кроме этого в функции closeit не помешало бы unbind keydown с документа.
И ещё вот эта конструкция: shadow.click(function(){closeit()}); смело заменяется вот этой: shadow.click(closeit);

Kildor 21 ноября 2008 / 8:44

Ещё бы он не перебивал CtrlShiftClick, который открывает картинку в фоновой странице, да и ShiftClick, или открыть в новой странице, было бы куда-ни-шло.

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

Артём Сапегин 21 ноября 2008 / 10:50

«Ещё бы он не перебивал CtrlShiftClick, который открывает картинку в фоновой странице, да и ShiftClick, или открыть в новой странице, было бы куда-ни-шло».

Вот за это всегда не любил такие открывалки. Оно, конечно, красиво. И, если на странице одна-две картинки, даже удобно. Но если надо посмотреть несколько картинок, то это просто не работает.

Артём Сапегин 21 ноября 2008 / 10:52

«…в прочем, мне не жалко».

pepelsbey 21 ноября 2008 / 12:39

если расценивать не как «просто мозг размять»

В точку. Это как раз мелочь, написанная для конкретной задачи, именно разминка для мозга. По этой же причине в заметке рассматривается только вёрсточная часть этого решения. JS-часть безусловно нужно оптимизировать, чем я и займусь на основе ваших советов )

Спасибо всем за отзывы.

akira 21 ноября 2008 / 14:12

Вот побаловался тут маленько :)
http://riamatic.com/poligon/darkbox
http://riamatic.com/poligon/darkbox/docs

milax 21 ноября 2008 / 18:41

А за что мой коммент был удален? За ссылку, так уберу ее.
Или скрины нужны чтобы убедиться что в ИЕ6 не все так гладко?

pepelsbey 21 ноября 2008 / 19:34

А за что мой коммент был удален?

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

И спасибо за ваше замечание — публикация готовилась на ходу, поэтому склейка двух версий прошла не очень гладко — кое-что потерялось.

IE8 пока сброшен в IE7-режим, а для IE6 всё поправлено.

pepelsbey 21 ноября 2008 / 19:38

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

milax 21 ноября 2008 / 19:41

pepelsbey, спасибо
Ссылку на свой блог больше оставлять не буду, ато мало ли :)

pepelsbey 21 ноября 2008 / 19:44

milax, на войне как на войне, к сожалению. А со спамерами сейчас именно война (

pepelsbey 22 ноября 2008 / 2:40

мне кажется, что привычнее "закрыть" делать справа от картинки

Кнопка Esc тоже находится слева и тоже срабатывает ;) Я понимаю, что для вас расположение контролов справа удобнее так же, как для меня слева — из-за Windows и Mac OS X, соответственно.

Я выбрал такой вариант, по праву определяя концепцию этого сайта.

Octane 23 ноября 2008 / 17:02

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

Frize 25 ноября 2008 / 17:37

Octane, на маленьких тоже притормаживает.

Решение отличное, но немного смущает размер файла jquery.js, ~55кб. Сжать бы его до 20-30кб, и тогда скрипт возможно, пошел бы в массы.

pepelsbey 25 ноября 2008 / 17:53

Скрипт задумывался для случая, когда вы уже используете jQuery. В любом случае, если jQuery ещё и Gzip'овать, то получается порядка 15 кб.

Cinic 26 ноября 2008 / 16:28

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

pepelsbey 26 ноября 2008 / 17:04

Неторопливо, но верно делаю новую версию, помочь с созданием плагинов для mootools и jQuery взялся один хороший JS-программист, так что всё будет ;) …и на качественно новом уровне.

ЯR 26 ноября 2008 / 23:07

Во втором абзаце «Принципов работы» слово «превдо-датчиком»...

Alexander 2 декабря 2008 / 1:06

Во втором абзаце «мол подклюйчай».

pepelsbey 2 декабря 2008 / 1:33

Большое спасибо за найденные ошибки — вот что значит писать в дороге )

SelenIT 3 декабря 2008 / 18:40

Подозрительное правило filter:true, в данной ситуации, заменяет традиционную методику с фальшивой картинкой и background-attachment:fixed для того, чтобы позиционирование блока работало плавно, без рывков.

Судя по всему, это работает только в Quirks mode. Ваш пример у меня, к сожалению, дергается при скроллинге (кроме стопроцентного блока — он не дрожит никогда, даже без доп. ухищрений). В Standards mode мне удалось победить дрожание, лишь задав body полноценный фильтр — ценой изрядных тормозов.

Все-таки, по моим наблюдениям, самый юзабельный и "кросс-режимный" вариант — это

  1. html { background: #fff url(about:blank) fixed; }
pepelsbey 7 декабря 2008 / 2:55
  1. url(about:blank)

Эта штука действительно срабатывает так же, как и фейковая картинка?

ADAM 8 декабря 2008 / 3:51

хороший скрипт только под в Internet Explorer 7 если страничка больше самого экрана, не закрашивается она, как то так. А так всё хорошо,

SelenIT 8 декабря 2008 / 11:49

У меня да, независимо от доктайпа (режима).

Panya 8 декабря 2008 / 16:00

Можно даже так:

  1. url(a)

url просто не должен быть пустым.

SelenIT 8 декабря 2008 / 16:53

Только, насколько я понимаю, url(a) — это обычный относительный URI (притом скорее всего ошибочный), а about:blank — легитимный URL, заведомо не вызывающий запроса к серверу (полностью "удовлетворяемый" локально). По этим соображениям я его и выбрал...

smmurf 10 декабря 2008 / 11:49

а в таком случае url(#) не будет ли полноценной аналогией?
Тоже, получается, легитимный URI, не требующий запроса к серверу, только запись короче.

SelenIT 12 декабря 2008 / 17:39

Про "#" я не так уверен, это все-таки ссылка не заведомо "в пустоту", а на тот же документ. А IE6 вообще любит тащить фоновые картинки с сервера по каждому поводу и без повода (по крайней мере, при некоторых настройках и без волшебного средства от Попы:). К тому же был прецедент (хоть и в др. браузере).

Эффект фиксации-то оно дает, но за отсутствие запросов не ручаюсь, чуть позже проверю на полноценном IE6...

Kitich 17 декабря 2008 / 3:58

Действительно здорово. Надо будет на досуге поковырять.

То есть там 1 скриптик остался+ jQuery?

bestann 25 декабря 2008 / 16:41

http://fancy.klade.lv/
FancyBox. Не похож на ваш?