Пример фотогалереи с использованием DOM

Фотогалерея, в которой увеличенные изображения можно выбирать без перезагрузки всей html-страницы. В примере используется Объектная модель документа(DOM), минимум XHTML-кода, немного CSS и JavaScript. Работает в Firefox1.02+, Opera7+, Internet Explorer5+, в других браузерах не проверял.

Пример фотогалереи с использованием DOM

Для простоты эксперимента условимся, что уменьшенных и увеличенных изображений одинаковое количество. У них одинаковые имена файлов, состоят из цифр(1, 2, 3 ... 25, 26 ... n), одинаковый тип файлов(например .jpg), а размещены они в разных директориях(например, smallimg и bigimg). Уменьшенные изображения все одинакового размера.

Однако эти ограничения приведены только для простоты конкретного эксперимента. При желании их список можно сократить, для этого потребуется лишь доработка CSS и JavaScript.

Сперва разметим место для увеличенного изображения div id="BigPhotoPlace".



<div id="BigPhotoPlace">
	<div id="BigPhoto" class="BigPhoto"></div>
	<div class="HorArrows">
		<img id="ItoRight" src="right.gif" width="8"

		height="10" alt="" class="arrowRight" />
		<img id="ItoLeft" src="left.gif" width="8"

		height="10" alt="" class="arrowLeft" />
		<div id="PhotoNum"><span>? из ??</span></div>

	</div>
</div>

Создадим CSS и будем начнем описывать правила стилей. Разумеется, оформить галерею вы можете по своему вкусу. Здесь лишь пример. Далее большинство комментариев буду приводить в коде по ходу повествования.


.HorArrows { /* позиционируем стрелки навигации */
	clear:both;
	position:relative;
	height:38px;
}

Для увеличенного изображения определим div id="BigPhoto".


#BigPhotoPlace { /* увеличенное изображение */
	position:relative;
}
#BigPhoto { /* фиксируем размеры изображения */
	width:275px;
	height:340px;
	overflow:hiddden; /* прячем лишнее */
	 /* на фоне можно написать что-то, пока
	 изображение не загрузилось, будет этот текст */
	background:url(404.gif) no-repeat 0 0;
}

В div class="HorArrows" поместим стрелки навигации по галерее(вперед-назад). А также между ними разместим номер текущего изображения и общее количество картинок в галерее. И напишем в CSS примерно следующее:


#BigPhotoPlace .HorArrows { 
/* навигация под увеличенной картинкой */
	text-align:center;
	margin:8px 0 0 0;
	width:275px; /* укажем ширину как у увеличенной картинки
	т.о. стрелки разместим по ее краям */
	position:relative;
}
.arrowRight { /* стрелка вправо */
	position:absolute;
	right:0;
	top:8px;
	cursor:pointer;
	cursor:hand;
}
.arrowLeft { /* стрелка влево */
	position:absolute;
	left:0;
	top:8px;
	cursor:pointer;
	cursor:hand;
}
#PhotoNum { /* информация о картинке */
	position:absolute;
	width:205px; /* ширина блока равна ширине
	поля с навигацией минус сумма ширины стрелок
	справа и слева */
	left:35px; /* отступ от левого края равен,
	либо больше ширины левой стрелки */
	top:4px;
}

По аналогии с увеличенным изображением делаем блок для уменьшенных div id="PreviewsPlace". Все уменьшенные изображения будем складывать в div id="Preview".


<div id="PreviewsPlace">

	<div id="Preview"></div>
	<div class="HorArrows">
		<img id="PtoRight" src="right.gif" width="8"

		height="10" alt="" class="arrowRight" />
		<img id="PtoLeft" src="left.gif" width="8"

		height="10" alt="" class="arrowLeft" />
	</div>
</div>


В блок навигации здесь, на мой взгляд, не имеет смысл включать количество изображений и номер текущего. При желании, его можно перенести из предыдущего блока навигации. Стоит обратить внимание на то, что id изображений в разных блоках навигации различаются(впрочем, и сами изображения можно заменить). В CSS добавим:


#PreviewsPlace { 
	width:345px; /* ширина блока по вкусу */
	position:relative;
	margin:0 0 0 20px;
}
#Preview { /* слой для кучи маленьких превьюшек */
	overflow:hidden;
	float:left;
}

А также сразу оформим внешний вид уменьшенных изображений:



.PrevImg { /* картинки маленьких превьюшек */
	margin:0 8px 8px 0;
	display: block;
	float:left;
	cursor:pointer;
	cursor:hand;
}

Прежде чем перейти к написанию скриптов, определимся, каким образом будем передавать скрипту настройки для галереи. Вероятно, я выбрал не самый оптимальный путь, но в данном случае так было проще. Я не знал каким образом будет собираться страница на сервере. Выбранный способ позволяет определить настройки для каждой страницы с галереей отдельно. Данные полей формы можно легко забрать и обработать с помощью JavaScript. А сами поля можно заполнить как вручную при кодировании страницы, так и с помощью серверных скриптов(Perl, PHP...), зависит от способа сборки страницы.

Минимум переменных, которые нам понадобятся в данном примере: общее количество изображений(ims), путь к уменьшенным(prevPath) и путь к увеличенным(photoPath). Поэтому закончим код вот такой формой:


<form method="post" id="PrevSetup" action="">

<input type="hidden" name="ims" value="10" />
<input type="hidden" name="prevPath" value="/smallimg/" />

<input type="hidden" name="photoPath" value="/bigimg/" />
</form>	

А теперь самое время приступить к написанию JavaScript.

Далее приведен JavaScript код для фотогалереи, описания функций и некоторых особенностей выполнены в виде комментариев.


var PhorScrolls = new Array();

try {
	window.addEventListener('load', init, false);
} catch(e) {
	window.onload = init;
}

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

    if (document.getElementById('Preview') &&
        document.getElementById('PrevSetup') &&
        document.getElementById('BigPhoto') &&
        document.getElementById('PhotoNum')){

    ownPreview = document.getElementById('Preview');
    BigImages = document.getElementById('BigPhoto');
    PhotoNums = document.getElementById('PhotoNum');

// считываем настройки галереи, условимся, что они есть
// хотя можно было бы проверять и их наличие

    setUps = document.getElementById('PrevSetup');
    ims = setUps.ims.value;
    prevPath = setUps.prevPath.value;
    bigImgPath =  setUps.photoPath.value;

// помещаем все параметры, необходимые для создания
// галереи в массив

    PhorScrolls[0] = new PhorScroll('Preview',
    				    'PtoRight',
    				    'PtoLeft',
    				    'ItoRight',
    				    'ItoLeft',
    				    'ims',
    				    'prevPath',
    				    'bigImgPath',
    				    'PhotoNum');

// и собираем галерею из уменьшенных изображений

    PhorScrolls[0].createPreviews();

// назначаем номер показываемой картинки,
// по-умолчанию = 1, т.е. первая.

if (!currPic){var currPic = 1;}	
    enLarge(currPic); 

        }
}

function PhorScroll(ownPrevID,
		    toRightID,
		    toLeftID,
		    predID,
		    sledID,
		    NumImgs,
		    prevPath,
		    photoPath,
		    PhotoNum) {

	this.index = PhorScrolls.length;
	this.prButton = document.getElementById(toRightID);
	this.plButton = document.getElementById(toLeftID);
	this.predButton = document.getElementById(predID);
	this.sledButton = document.getElementById(sledID);
	this.NumImgs = NumImgs;
	this.prevPath = prevPath;
	this.photoPath = photoPath;
	this.createPreviews = createPreviews;
	this.prevClick = prevClick;
	this.PredSledClick = PredSledClick;
}

// функция, в которой создадим массив уменьшенных изображений
// и выведем его результат на страницу

function createPreviews(){
	ImgArr = new Array();
	for (var i = 1; i <= ims; i++) { 
	ImgArr[i] = new Image(); 
	ImgArr[i].src = prevPath + i + '.jpg'; 
	} 

// инициализируем массив для уменьшенных изображений
	previewI = new Array();

	for (var m = 1; m <= ims; m++){

// создаем элементы img 
	var elemI = document.createElement('img');
	elemI.src = prevPath + m + '.jpg';

// устанавливаем ширину и высоту уменьшенных изображений
// можно было также передавать эти значения с настройками
	elemI.width = 80;
	elemI.height = 80;

// и устанавливаем еще некоторые атрибуты
	elemI.alt = 'посмотреть';
	elemI.className = 'PrevImg';
	elemI.id = 'i' +m;

	ownPreview.appendChild(elemI);

// и складываем получившиеся изображения
// в назначенный для них слой div id="Preview"
// помещаем эти изображения в массив
	previewI[i] = document.getElementById('i'+m);

// и назначаем каждому из них обработчик события 
// onclick для вызова увеличенного изображения
	previewI[i].onclick = prevClick;
	}

// готовим кнопки навигации по галерее
// назначаем обработчики событий для кнопки сдвига
// изображений в галерее влево
    this.plButton.onmousedown = PrevScrollClick;
    this.plButton.onmouseup = handleScrollStop;
    this.plButton.onmouseout = handleScrollStop;
//  this.plButton.oncontextmenu = blockEvent;

// назначаем обработчики событий для кнопки сдвига
// изображений в галерее вправо
    this.prButton.onmousedown = PrevScrollClick;
    this.prButton.onmouseup = handleScrollStop;
    this.prButton.onmouseout = handleScrollStop;
//  this.prButton.oncontextmenu = blockEvent;

// назначаем обработчики событий для кнопок навигации
// под увеличенным изображением
    this.predButton.onclick = PredSledClick;
    this.sledButton.onclick = PredSledClick;
}

// отмена всех перемещений
function handleScrollStop() {
    scrollEngaged = false;
}

// обработчик события для сдвига уменьшенных изображений
// при нажатии на кнопки под галереей
function PrevScrollClick(evt){
    evt = (evt) ? evt : event;
    var target = (evt.target) ? evt.target : evt.srcElement;
    var index = target.index;

// определяем какая из кнопок нажата,
// т.е. в какую сторону двигать картинки
    course = (target.className == "arrowLeft") ? toLeft : toRight;
    scrollEngaged = true;

// одинарный клик по кнопке
    PrevScroll(index,course);

// переключатель для нажатой и удерживаемой кнопки
// задаем скорость и направление
    scrollInterval = setInterval('PrevScroll(' + index + ',' +
                                           course + ')', 200);
    evt.cancelBubble = true;
    return false;
}

// в этой функции запускаем скролл картинок
function PrevScroll(index,cource){ 
    var Pscroller = PhorScrolls[index];
    if (scrollEngaged) {
	return cource();
    } else {
       clearInterval(scrollInterval);
    }
}

// сдвиг уменьшенных изображений вправо
function toRight(){

// берем первое изображение
	aaa = ownPreview.firstChild;

// определяем последнее изображение
	zzz = ownPreview.lastChild;

// и размещаем первое изображение после последнего
// теперь первая картинка будет последней и т.д.
// по кругу при каждом нажатии на кнопку сдвига вправо
// или при нажатии на нее и удержании
	ownPreview.insertBefore(zzz, aaa);
}

// сдвиг уменьшенных изображений влево
function toLeft(){ 
// здесь чуть сложнее, чем при сдвиге вправо
// создаем любой временный элемент, например br
	var elemB = document.createElement('br');

// делаем его первым в галерее
	ownPreview.appendChild(elemB);

// снова берем первый элемент, теперь это не изображение
// а только что созданный br
	aaa = ownPreview.firstChild;

// берем последний элемент
	zzz = ownPreview.lastChild;

// и размещаем последнее изображение галереи после первого
// элемента(br), но зато оно будет теперь перед
// первым изображением в галерее
	ownPreview.insertBefore(aaa, zzz);

// и уничтожаем элемент br, который уже больше не нужен
	ownPreview.removeChild(elemB);
}

// обрабатываем щелчок по уменьшенному изображению
function prevClick(evt){ 
    evt = (evt) ? evt : event;
    var target = (evt.target) ? evt.target : evt.srcElement;
    var regexp = /\d+/;

// определяем по какой из картинок кликнули
    xxx = regexp.exec(target.getAttribute('id'));

// и увеличиваем соответствующую картинку
    enLarge(xxx);
    evt.cancelBubble = true;
    return false;
}

// выводим увеличенное изображение
function enLarge(photoId) { 

// удаляем содержимое div id="BigPhoto если там 
// уже было изображение, или что-то еще
	var BIchilds = BigImages.childNodes; 
	if (BIchilds.length > 0) {
	for (h=0; h <= BIchilds.length; h++ ){
		BigImages.removeChild(BIchilds.item(h));
		}
	}
// создаем новый элемент img для увеличенного изображения
	var elemBI = document.createElement('img');
	elemBI.src = bigImgPath + photoId + '.jpg';
	elemBI.alt = '';
	elemBI.id = 'B'+photoId;

// размещаем его в назначенный слой div id="BigPhoto
	BigImages.appendChild(elemBI);

// и вызываем функцию для вывода номера изображения
	countImg(photoId,ims);
	return false;
}


// счетчик картинок (1 из ... и т.д.)
function countImg(currImg,allImg){

// сперва прибиваем старый счетчик
	var PhotoNumschilds = PhotoNums.childNodes;
	if (PhotoNumschilds.length > 0) {
	for (h=0; h <= PhotoNumschilds.length; h++ ){
		PhotoNums.removeChild(PhotoNumschilds.item(h));
		}
	}
// создаем элемент span и вписываем в него номер картинки
// и общее количество изображений в галерее
 	var elemS = document.createElement('span');
	var txts = document.createTextNode(currImg + ' из ' +
                                                      allImg);
	elemS.appendChild(txts);

// размещаем счетчик картинок в отведенный для них слой
	PhotoNums.appendChild(elemS);
	return false;
}

// навигация под увеличенными изображениями
function PredSledClick(evt){ 
    evt = (evt) ? evt : event;
    var target = (evt.target) ? evt.target : evt.srcElement;
    var regexp = /\d+/;
    xxx = regexp.exec(BigImages.firstChild.getAttribute('id'));
    var wView = (target.className == "arrowLeft") ? -1 : 1;
    var nextPhoto = parseInt(xxx)+parseInt(wView);
    if (nextPhoto <= 0) {
    nextPhoto = ims;
    }
    if (nextPhoto > ims) {
    nextPhoto = 1;
    }
     enLarge(nextPhoto);
    evt.cancelBubble = true;
    return false;

}


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



Много комментариев (8) к “Пример фотогалереи с использованием DOM”

  1. Adepto :

    Отличное решение. Видел такое на американском сайте, только там картинки загружались сразу – что не есть хорошо (страница весила пол мегабайта).


  2. matt_reutt :

    очень здорово.
    спасибо)


  3. Dicos :

    Прекрасное решение, мне оно очень понравилось.


  4. Дядька Глюк :

    Решение то хорошее.. Только больше число картинок что-тоне шуршит. Плюс не ясно, будет ли работать скрипт, если картинки будут не попорядку – то есть не 1, 2,3,4 и т.д. а примерно так.. 1,2,3, 45, 78, 95, 146 и т.д. вот в чем вопрос..


  5. silex :

    2 Дядька Глюк
    Для ясности берем и тестируем :) У меня вообще было нестандартное размещение файлов: какждый файл с превьюхой в своей папке, имя файла произвольное. Ничего, полчаса – все решилось и адаптировалось…

    ЗЫ. Отличный скрипт :)


  6. Глеб :

    очень понравилось
    классный “движок”но то ли я чтото накосорезил адаптируя его под свои условия то ли одно из двух
    если будет возможность
    посмотрите что у меня из этого получилось
    http://pktgroup.narod.ru/foto/2ks/nz/gal.html

    360 фотографий
    приходится ждать пока все превьюшки не закачаются


  7. Глеб :

    все
    спасибо
    вроде сам разобрался
    http://pktgroup.narod.ru/foto/2ks/nz/gal.html
    может не оптимально но сделал чтобы превьюшки загружались не сразу а в процессе их же прокрутки


  8. Артур :

    Подскажите, please, что нужно изменить или добавить, чтобы при каждом обновлении страницы выводилась случайная большая картинка, а не постоянно первая, как здесь.



22 queries 0.186 seconds.