Каскадные Таблицы Стилей

индекс
324,89

Галерея с асинхронной загрузкой средствами CSS3

Занимаясь вёрсткой галерей, я искал простое и оригинальное решение для просмотра фотографий.
Экспериментируя, написал пример, которым хочу поделиться с сообществом Хабра.
  • Разметка: XHTML 1.0 Strict [Valid].
  • Оформление и управление: CSS3 [Valid].
  • Поддержка устройствами группы: screen.
  • Поддержка браузерами: Opera 9.6, 10; Firefox 3.0, 3.5; Chrome 1—4; Safari 3.2, 4.0.
  • Асинхронная загрузка, счётчик изображений.
  • Корректная ссылка вида #photobox-id.
  • Вывод гипертекста с заголовком, описанием, ссылками.
  • Центрированный «эластичный» макет с сохранением пропорций.
  • Горизонтальная навигация с прокруткой миниатюр.
  • Выделение/подсветка ссылок.


Разметка


В разметке галереи есть принципиальный момент: списки preview и photoboxes разделены, что позволяет независимо использовать любое позиционирование и оформление для превью (далее лента миниатюр) и блоков с фотографиями (далее фотобоксы).
<div id="gallery">
	<h1>CSS-Галерея</h1>
	<ul class="preview">
		<li>
			<a href="#photobox-id" title="Изображение">
				<img src="thumb.jpeg" alt="Изображение" />
			</a>
		</li>
		<!-- и т. д. элементы ленты миниатюр -->
	</ul>
	<ul class="photoboxes">
		 <li id="photobox-id">
			<h2>Наименование изображения</h2>
		 	<p>Описание изображения</p>
		</li>
		<!-- и т. д. элементы списка фотобоксов -->
	</ul>
</div>

Текст


Заголовок Галереи
Заголовок центрирован посредством margin: auto. Он имеет относительный размер шрифта и ширину в %, пропорциональную ширине экрана, а так же минимальную ширину в em, что не даёт ему деформироваться при малой ширине окна.
/* заголовок галереи */
#gallery > h1 {
	width: 25%;
	min-width: 7.5em;
	padding: 0.25em;
	margin: 0.25em auto;
	text-align: center;
	font-size: 175%;
}

Аналогичное форматирование характерно для других блочных элементов с текстом.
/* наименование фотографии */
.photoboxes > li > h2 {
	max-width: 10em;
	margin: 0.25em auto;
	text-align: center;
	font-size: 125%;
}

/* описание фотографии */
.photoboxes > li > p {
	max-width: 20em;
	margin: 0 auto;
	font-size: 100%;
}

Превью


Лента миниатюр
Предварительный просмотр реализован в виде списка, элементы которого вытянуты в строку с помощью table-cell. При переполнении миниатюрами у нижней границы ленты образуется прокрутка. Внутри ячеек блочные ссылки и изображения миниатюр.
/* лента миниатюр */
.preview {
	width: 50%;
	min-width: 17.5em;
	overflow: auto;
	margin: 0.75em auto;
}

	/* ячейка с миниатюрой */
	.preview > li { display: table-cell; /* ячейки в строку */ }
	
		/* ссылка миниатюры */
		.preview > li > a {
			display: block;
			margin: 0.5em;
			padding: 0.5em;
		}
		
			/* изображение миниатюры */
			.preview > li > a > img {
				display: block; /* без лишнего поля снизу */
				width: 75px;
				height: 75px;
			}

Фотобоксы


Список фотобоксов центрирован в блоке галереи при помощи inline-block.
/* блок галереи */
#gallery { text-align: center; /* центрирует список фотобокcов */ }

	/* список фотобокcов */
	.photoboxes { display: inline-block; /* обёртывает содержимое */ }

Такое выравнивание позволяет ему обтекать и центрировать содержимое.

Сначала в потоке форматирования только центрированный счётчик. Размер обёртки равен размеру блока счётчика.

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

Снимок фотобокса и счётчика под ним
Изначально фотобокс скрыт и выведен из нормального потока форматирования с помощью абсолютного позиционирования.
/* фотобокc */
.photoboxes > li {
	position: absolute; /* «колода» фотобокcов */
	visibility: hidden; /* невидимые фотобокcы */
	display: block; /* без маркера */
	padding: 0.75em;
}

В нужный момент он проявляется и позиционируется статично, возвращаясь в поток.
/* фотобокc синхронизированный со ссылкой */
.photoboxes > li:target {
	position: static; /* достаём фотобокc из «колоды» */
	visibility: visible; /* проявляем фотобокc */
}

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

Счётчики


Для подсчёта номера и числа фотографий счётчик (photo) ставится в списке фотобоксов на значение 0.
/* список фотобокcов */
.photoboxes { counter-reset: photo; /* счётчик установлен на 0 */ }

В каждом следующем фотобоксе значение счётчика на 1 больше, чем в предыдущем.
/* список фотобокcов */
.photoboxes > li { counter-increment: photo; /* +1 к счётчику с каждым фотобокcом */ }

В элементе, удалённом из потока нет инкремента счётчика. По этому фотобоксы скрываются (visibility: hidden), а не удаляются (display: none).

Общее число фотографий сохраняется в списке фотобоксов и выводится в его псевдоэлемент.
/* псевдоэлемент со счётчиком общего числа фотографий */
.photoboxes:after {
	display: block;
	width: 5em;
	padding: 0.125em;
	margin-left: auto; /* смещает к правому краю */
	margin-top: 0.25em; /* отступ от фотобокса */
	content: counter(photo) " фото"; /* вывод числа фотографий */
	text-align: center;
	font-size: 115%;
}

Счётчик порядкового номера фотографии печатается в псевдоэлементе фотобокса, в нижнем правом углу.
/* псевдоэлемент со счётчиком порядкового номера фотографии */
.photoboxes > li:after {
	display: block; /* счётчик под описанием */
	content: "№" counter(photo); /* вывод номера фотографии */
	text-align: right; /* номер в нижнем правом углу */
	font-size: 105%;
}

Загрузка


Прежде всего, создаётся псевдоэлемент, в который будет загружаться фотография.
/* псевдоэлемент с фотографией */
.photoboxes > li:before {
	display: block;
	padding: 0.75em;
	margin: 0 auto; /* центрирует блок */
	text-align: center; /* центрирует фотографию */
}

Чтобы размеры фотобокса не изменялись в процессе загрузки, у псевдоэлемента прописываются ширина и высота фотографии.
/* горизонтальные фотографии */
#photobox-id:before {
	width: 300px;
	height: 225px;
}

Схема загрузки фотографии следующая: активируем ссылку миниатюры, она синхронизируется с соответствующим фотобоксом по #photobox-id через :target, фотобокс вынимается из общей стопки и проявляется, далее в псевдоэлемент вверху него загружается фотография.
/* загрузка фотографий в соответствующие фотобоксы при активации ссылки */
#photobox-id:target:before { content: url("photo.jpeg"); }

Ссылки


Ссылка на #photobox-id перемещает фокус по якорю на фотобокс при его статичном/относительном позиционировании. Когда элементы галереи умещаются в окне этого не видно.

Аргументом в пользу ссылок такого типа является возможность использования браузерной навигации на странице (next/prev/reload). В принципе, фотографии в данной галерее можно загружать, используя лишь адресную строку. При этом они будут загружаться в фотобокс.

Поддержка


Приведённый пример корректно и идентично поддерживается всеми вышеупомянутыми браузерами. В Internet Explorer не работает, т. к. IE 6 и 7 не поддерживают псевдоэлементы, свойство content и счётчики. Ключевой псевдокласс :target не поддерживает даже IE 8.
Реализация для IE возможна через AJAX или эмуляцию псевдоклассов со вставкой элементов.

Максим Милованов (alemiks) написал пример эмуляции псевдокласса :target, и целую статью по эмуляции CSS для IE.
Если требуется эффектное JS-решение для проекта с поддержкой IE — вполне подойдёт клон Lightbox.

Благодарности


Спасибо Александру Макарову (SamDark) за фотографии и хост для публикации примера.

P. S. Если не понравилось выделение ссылок — посмотрите пример с полупрозрачностью (используется свойство opacity).
+52
5 августа 2009, 23:10
89

комментарии (71)

+1
ish #
Прикольно, даже история работает.
+1
InTRUEdeR #
блин, а че оно прыгает, когда я перебираю фотки?
+2
DrHolerik #
Видимо, в окно вашего браузера не умещается блок с фотографией, и при активации ссылки происходит переход по якорю вниз.
Предлагаю растянуть окно по высоте или уменьшить масштаб — всё тянется и масштабируется.

P. S. Писал об этом свойстве якорей выше под заголовком «Ссылки», 1-й абзац.
Я понимаю, что хабратопик объёмный, но там много ответов на вопросы. ;-)
0
InTRUEdeR #
у меня 1440х900, из-за этого не вмещается. можно конечно монитор развернуть на 90 градусов (а точнее картинку в нем), но это неудобно =)
0
Mithgol #
Это решение весьма радует взгляд, но его можно улучшать и далее.

Скажем, по адресу www.tinyurl.com/cross-browser-inline-block описана такая техника работы с CSS, которая позволила бы отказаться от table-cell в данном случае, а заодно позволила бы без труда переходить от однострочного и прокручиваемого списка ко двумерной сетке миниатюр и обратно, когда и если заказчик или автор сайта того пожелает.
+2
DrHolerik #
Mithgol, обо всём по порядку.

Table-cell не зло, а средство форматирования.
Ранее лента была свёрстана с помощью inline-block + nowrap.
Но inline-block даёт лишние пробелы между миниатюрами, которые надо компенсировать.
С table-cell же всё гладко, и это свойство поддерживается во всех перечисленных браузерах.

Галерея верстается давно. В начале июля её миниатюры были свёрстаны сеткой. Но в таком случае, негде было разместить фотобокс.
По этому я остановился на варианте с лентой. Из 4-х возможных позиций победила горизонтальная, между заголовком и фотобоксом.

«Если заказчик прикажет, кто же ему откажет?» ©
Но тут, думаю, заказчик не захочет такую галерею в принципе. Он пожелает «как на Яндексе», или «с оверлеями и градиентами [и блек-джеком]».

Оценят те, кто хоть что-нибудь верстал с помощью CSS.
0
simpel #
inline-block дает лишние отступы, но это решается, достаточно в коде убрать перенос строки между li.

...…

вот так.
0
DrHolerik #
Можно не издеваться над кодом, вытягивая десятки в одну строку,
а компенсировать пробелы с помощью .preview > li { margin-right: -ширина пробела; }.
Но к чему усложнять css, когда можно использовать подходящее свойство?
0
DrHolerik #
* десятки li, a, img (извиняюсь, забыл, что теги съедаются)
0
simpel #
ну так не надо вытягивать, достаточно чтобы связка закрывающего и открывающего LI не имела пробела или переноса строки. ...…
0
simpel #
тэги!

...</li><li>…
НЛО прилетело и опубликовало эту надпись здесь
0
DrHolerik #
Так что форматирование сорса, имхо — не аргумент в выборе средств.

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

вряд ли найдется решение лучше инлайновых блоков…

Одно свойство table-cell или inline-block + nowrap + margin: -ширина_пробела | </li><!-- --><li>
НЛО прилетело и опубликовало эту надпись здесь
+2
Auru #
</li><!--
--><li>
0
simpel #
логично :) спасибо
0
egorinsk #
Я обычно вставляю HTML-комментарии в конце и в начале тега li, --][li]text[/li][!--
НЛО прилетело и опубликовало эту надпись здесь
+1
CurlyBrace #
Отличный пример реализации, спасибо. Перенесите в блог CSS.
+2
DrHolerik #
CurlyBrace, рад, что вам понравилось.
Благодаря активности камрадов перенёс в блог CSS.
+1
CurlyBrace #
Активность заслуженая, надо отметить )
+1
k3NGuru #
Спасибо, отличная идея. Просто и удобно :)
+4
Buck #
Если нужно что то простое быстрое идеальный вариант, Главный плюс что без JS!
Но я использовал бы скорее Jquery простенькую подмену типа:

JS:
$("#photo a").click(function(){
	var PhotoLink = $(this).attr("href");
	var PhotoTitle = $(this).attr("title");
	$("#photo a").removeClass("on");
	$(this).addClass("on");  /* Будет добавлять класс для подсвечивание выбранной картинки  */
	$("#photo_big").attr({ src: PhotoLink, alt: PhotoTitle });
	return false;
	});

HTML:
<img src="001.jpg" alt="тайтл 001" id="photo_big" />

<p id="photo">
   <a href="001.jpg" title="тайтл 001" class="on"><img src="001_prew.jpg" alt="" /></a>
   <a href="002.jpg" title="тайтл 002"><img src="002_prew.jpg" alt="" /></a>
</p>
+1
WASD42 #
Поступил бы аналогично. Стремление соответствовать стандартам похвально, но с учётом того, что IE (всё-таки ещё самый популярный браузер) не поддерживается — реализацию можно рассматривать как эдакий просмотр далёкого будущего, когда у всех пользователей будут совместимые с данным способом браузеры.

В любом случае, за статью и за старания — большое спасибо, было интересно :)
+3
CurlyBrace #
Если продолжать работать с новейшими стандартами и внедрять их повсеместно, то вступит в силу закон, говорящий о том, что качество товара определяет потребитель. И может быть тогда Майкрософт ускорит выход браузера с поддержкой CSS3/HTML5
–2
egorinsk #
Jquery — муть, тяжелый, требует лишних 20 Кб грузить, причем блокируя страницу, ест память, провоциует на написание неэффективного кода.

// Кроме того jQuery в данном примере требует намного меньше знаний и не дает повода выпендриться перед собратьями ;)
+1
Buck #
Даже и не думал выпендриваться…
причем я написал что это идеальный вариант и его большой плюс, что без JS в отличии от многих других вариантов! а только потом добавил как бы все таки сделал я, если бы делал!

Если бы всем нравилось одно и то же было бы не интересно жить!
+1
egorinsk #
Дык, правильно, jQuery сегодня никого не удивишь, я об этом и написал :) и мне вариант с css нравится намного больше.
0
Masterkey #
это хороший выпендреж!
–1
homm #
Можно проследить эволюцию сами знаете какого браузера.
В шестой версии такое впечатление, что вообще css нет.
В седьмой появляется рамка, лишнего контента не видно.
В восьмой фотографии становятся на свое место, после загрузки страница выглядит как должна.

В одиннадцатой версии все наконец-то заработало, как в 9-й Опере. Отставание в технологиях 6 лет.
–1
homm #
Черт, в 9.0 Опере не работает, зато в ФФ 2.0 работает почти правильно.
–1
DrHolerik #
Странно, у меня в тестовой Opera 9.64 (сборка 10487) всё работает отлично.
В Firefox 2.0.0.20 — растяжение фотобокса по горизонтали. Но я не ориентировался на Firefox 2.0 — согласитесь, когда есть релизы 3.0 и даже 3.5 это не актуально.
В Opera 9.2 не работает. Видимо, она не понимает :target. Так же не актуальный браузер после версий 9.64 и 10 beta.
См. список поддерживаемых браузеров над катом — там перечислены те программы, в которых галерея не раз тестировалась.
+1
CurlyBrace #
В пост призывается Pepelsbey
+1
pepelsbey #
Пепелсбей прибыл, но пока не знает чем заняться )
Пример хороший, но не революционный:

— Больше года назад CSS3.info: www.css3.info/making-an-image-gallery-with-target/
— Шесть лет назад, персональные эксперименты: andreas.web-graphics.com/footnotes/
0
homm #
Странно, у меня в тестовой Opera 9.64 (сборка 10487) всё работает отлично.
Странно, я и не говорил что у меня в ней не работает.
В Opera 9.2 не работает.
Именно так я и сказал.
0
DrHolerik #
homm, извиняюсь, ваше замечание верно.
Фразу «в 9.0 Опере» машинально воспринял как «в Opera 9» (в т. ч. и 9.5-9.6, а не только 9.0-9.2).
+1
pepelsbey #
В 9.27 не работает, но в 9.52 уже всё нормально.
+1
DrHolerik #
Наработки в этой области уже имеются:
Making an image gallery with :target — простой пример использования псевдоклассса :target для проявления изображений.
Futurebox, lightbox without the Javascript — галерея с оверлеем! Аналогично  используется :target.
A Photograph Gallery — with links — CSS2-реализация, поддержка IE, загрузка по подведению курсора.
+ ещё примеры, если погуглить «css gallery target».

К сожалению, я так и не смог найти галерею, где средствами CSS изображения загружались бы асинхронно.
В приведённых примерах крупные фотографии загружаются последовательно, согласно их расположению в разметке.
В моём примере в html нет img c большими фотографиями — они загружаются по click/enter через content: url().
+1
homm #
В приведённых примерах крупные фотографии загружаются последовательно,
На самом деле это от браузера зависит. Опера, например, не загружает картинки в невидимых блоках, поэтому большие картинки подгружаются только когда они появляются.
0
DrHolerik #
Вы обэлементах с visibility: hidden или display: none?
0
homm #
display: none, потому что для элементов visibility: hidden должны высчитываться размеры, а это не возможно без загрузки картинок.
0
DrHolerik #
На самом деле это от браузера зависит. Опера, например…

Спасибо за информацию, не знал об этой особенности Opera.
А какие ещё браузеры не загружают изображения, удалённые из потока форматирования?
0
homm #
я не знаю, просто оперой пользуюсь, часто вижу картинки, догружающиеся при появлении (и в ваших примерах тоже и в примере с ниндзей)
0
crwin #
уже стало модно ие не брать в расчёт?
в таком случае данный пример был бы актуален пару лет назад, как и разного рода ксс-фичи, которые не держит ие :)
0
DrHolerik #
Под заголовком «Реализация» я пишу о том, какими средствами можно сделать для IE.
0
rapida #
Извините, но мне кажется что с такой реализацией теряется вся изящность данного решения. А под ie, как-никак верстать надо
0
DrHolerik #
В IE старее версии 8 не поддерживается content: url(), так что его там только эмулировать, например через innerHTML.

Синхронизацию картинок с миниатюрами без :target реализовать можно. В ранних черновиках примера фигурировал :hover/:focus. Но использование этих псевдоклассов не даёт ссылки на якорь. С загрузкой в такой ситуации тоже проблемы — отвёл мышку, картинка перестала загружатся. О функционировании истории и речи нет.

Резюмируя: если нужен IE — используйте JS, не оглядываясь на CSS3.
0
FireFly #
Отлично! Автор молодец!
+1
soider #
Блин, сначала все были за разделение поведения, контента и внешнего вида, теперь радуются, что при помощи средств для формирования внешнего вида можно выполнить некоторые функции из раздела поведение. Я никогда не пойму людей :))
0
DrHolerik #
Разделение: вся разметка в index.html, все стили в main.css. Синхронизация фотографий в конце таблицы стилей, в отдельном блоке «Фотографии».

Хотя, впрочем, я согласен с вами. По логике для управления нужно использовать скрипт.
В таком случае эта статья — это повод для JS-кодеров подметить:
«Как замечательно, что я владею AJAX, и могу сделать такую штуку, только во много раз функциональнее и концептуальнее.»

Я не против Javascript и за концепцию разделения разметки, стилей и скриптов, но очень люблю интересные примеры вёрстки.
+1
mitja #
оффтопик: эскалатороподобная дорожка называется травтолатор
НЛО прилетело и опубликовало эту надпись здесь
0
homm #
float: left работает с nowrap? Если даже работает, по моему не везде.
+1
Buck #
Float: Выравнивает элемент по одному краю и позволяет сделать обтекание по второму
table-cell: как бы эмуляция ячейки таблицы, которой можно задавать такие же стили как для обычной TD — и минус в том что по моему не поддерживается меньше ie7 включительно
0
DrHolerik #
Форматирование превью в картинках

Миниатюры в строку, переносятся, с пробелами:
.preview > li { display: inline-block; }



Обтекаются, переносятся, без пробелов:
.preview > li { float: left; }



В строку, не переносятся, без обтекания, без пробелов:
.preview > li { display: table-cell; }



P. S. На Хабре есть замечательная статья по потоку форматирования, где очень популярно описываются все 3 способа.
0
egorinsk #
Можно юзать inline-block совместно с white-space: nowrap :)
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
0
DrHolerik #
блоки в ряд, впритык, одинаковой высоты, без единого разрыва :)

Именно такое форматирование и требуется для данной ленты.
НЛО прилетело и опубликовало эту надпись здесь
0
deniamnet #
за попытку спасибо, но есть галереи гораздо лучше
я обычно не изобретаю велосипед, а просто подстраиваю его под себя
0
DrHolerik #
Не случайно статья публикуется не под заголовком «Новый клон Lightbox» — я не ставил цели переплюнуть функционал JS-галерей.
Просто продемонстрировал возможности (загрузка, счётчики, подсветка), заложенные в CSS и всё это реализовал для просмотра фото.
+1
fatal #
Поддержка браузерами: Opera 9.6, 10
В Опере :target работает только частично (навигация назад и вперёд в истории никак не работает).
0
DrHolerik #
Сейчас посмотрел в Opera 9.64 и 10 beta: история якорей по :target работает не до конца
(next/prev перематывает #photobox-id в адресной строке, но фотографии не листаются).
Со стороны Opera это странно. Хотелось бы услышать комментарий эксперта.
+1
fatal #
Просто брак, недоделка — тут и к доктору эксперту не ходи.
Не уверен, что исправят быстро, т.к. редко где применяется в реальной жизни.
–1
tyv #
понравившуюся фотку сохранить можно только сделав скриншот :)
0
DrHolerik #
Webkit — текст и графика, вставленные через content не сохраняются и не выделяются (во всяком случае, с помощью курсора).
Firefox — нет пункта для сохранения аналогичного изображения, но его можно перетащить в другое окно с помощью drag&drop.
Opera — весь текст, в т. ч. и счётчики выделяются, фотографии сохраняются нормально.
+1
Theo_from_Sed #
Отлично, CSS3 «пошёл» :)
Интересный пример, изучаю.
0
hannimed #
Раньше не мог себе представить, что такое возможно. Очень интересно.

Из недостатков хотелось бы отметить, что разработчикам было бы не удобно помещать список изображений в CSS файл… Может быть есть какой-то другой способ, например хранить адреса в атрибуте фотобокса и как-то доставать (я к сожалению CSS3 не знаю, по-этому не бейте, если глупость сморозил).
0
DrHolerik #
Тут нужен селектор с псевдоклассом, а атрибут style в теге c id=«photobox-id» в html — это то же, что #photobox-id {} (без :target) в css.
Списки синхронизации фотобоксов с фотографиями можно хранить в отдельном *.css, или в контейнере <style>.
0
hannimed #
Действительно, не подумал об отдельном файле. Спасибо.

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