Skip to content

Instantly share code, notes, and snippets.

@c-vetter
Last active September 25, 2015 11:08
Show Gist options
  • Save c-vetter/912465 to your computer and use it in GitHub Desktop.
Save c-vetter/912465 to your computer and use it in GitHub Desktop.
simple click-through gallery, two variants, see comments and example pages -- add your own sample pics
/**
* The ImageCycle class cycles through a given set of images upon clicking the automatically generated buttons.
*
* @param Node
* This container node is expected to contain at least one 'img' tag.
* The first 'img' tag found will be used for displaying the cyled images.
* @param Array of Array of String [optional]
* Array of Arrays, each of which contains
* first the image path, then the title, optionally the caption.
*
* Expects the generated elements to be positioned/styled by CSS.
*
* Generates buttons inside the container node.
* The buttons are 'a' tags with classes 'next_image' and 'previous_image', each containing an 'img' tag
* pointing to '/images/next_image.png' or '/images/previous_image.png', respectively.
*
* Generates a caption element inside the container node.
* The caption element is a 'p' tag with class 'caption'.
* For empty captions, the caption element gets the additional class 'empty'.
* If the caption starts with content enclosed in brackets ('[' and ']'),
* that content is assumed to be a css class and added as such.
* Apart from that, the caption is considered and handled as plain text.
*
* Requires DOM functions!
* Almost no error handling throughout!!
*/
function ImageCycle(container, images) {
this.container = container;
for(i in this.container.childNodes)
if (this.container.childNodes[i].tagName
&& this.container.childNodes[i].tagName.toLowerCase() == 'img') {
this.image = this.container.childNodes[i];
break;
}
this.caption = document.createElement('p');
this.caption.className = 'caption';
this.container.appendChild(this.caption);
this.images = [];
this.set_images(images);
this.forth_button = document.createElement('a');
this.forth_button.cycler = this;
this.forth_button.onclick = this.next_image;
image = document.createElement('img');
image.src = this.next_image_button_path;
image.alt = this.next_image_label;
this.forth_button.appendChild(image);
this.forth_button.className = 'next_image';
this.container.appendChild(this.forth_button);
this.back_button = document.createElement('a');
this.back_button.cycler = this;
this.back_button.onclick = this.previous_image;
image = document.createElement('img');
image.src = this.previous_image_button_path;
image.alt = this.previous_image_label;
this.back_button.appendChild(image);
this.back_button.className = 'previous_image';
this.container.appendChild(this.back_button);
}
// global defaults
ImageCycle.prototype.next_image_label = 'next image';
ImageCycle.prototype.next_image_button_path = 'images/next_image.png';
ImageCycle.prototype.previous_image_label = 'previous image';
ImageCycle.prototype.previous_image_button_path = 'images/previous_image.png';
/**
* Updates the array of images with the given array.
*
* @param Array of Array of String
*
* @return false if given object is not an array
*
* Expects an Array of Arrays, each of which contains
* first the image path, then the title, optionally the caption.
*/
ImageCycle.prototype.set_images = function(new_images) {
if (is_array(new_images)) {
this.images = [];
for(i in new_images) {
array = new_images[i];
object = {};
object.path = array[0];
object.title = array[1];
if (array[2]) {
caption_data = /^(\s*\[(.+?)\]\s*)?(.*)/.exec(array[2]);
object.caption = caption_data[3];
object.caption_style = caption_data[2] || '';
} else {
object.caption = '';
object.caption_style = 'empty';
}
this.images.push(object);
}
count_down = this.images.length;
while (count_down-- > 0 && this.image_data().path != this.image.src)
this.cycle_images_forth();
this.update_image();
} else {
return false;
}
}
/**
* Updates the image and caption elements with the current image data.
*/
ImageCycle.prototype.update_image = function() {
this.image.src = this.image_data().path;
this.image.alt = this.image_data().title;
this.image.title = this.image_data().title;
this.caption.innerHTML = this.image_data().caption;
this.caption.className = 'caption '+this.image_data().caption_style;
}
/**
* Returns the data for the current image.
*
* @return Object
*/
ImageCycle.prototype.image_data = function() {
return this.images[0];
}
/**
* Tells the cycling object to advance by one image.
*
* Is automatically added to the generated next-button,
* expects to be called from there.
*/
ImageCycle.prototype.next_image = function() {
this.cycler.cycle_images_forth();
this.cycler.update_image();
}
/**
* Tells the cycling object to regress by one image.
*
* Is automatically added to the generated previous-button,
* expects to be called from there.
*/
ImageCycle.prototype.previous_image = function() {
this.cycler.cycle_images_back();
this.cycler.update_image();
}
/**
* Cycles the images array, putting the first element to the rear.
*/
ImageCycle.prototype.cycle_images_forth = function() {
this.images.push(this.images.shift());
}
/**
* Cycles the images array, getting the last element to the front.
*/
ImageCycle.prototype.cycle_images_back = function() {
this.images.unshift(this.images.pop());
}
// Helper functions not directly part of the class
/**
* Checks whether the given object is an Array.
*
* @param Object
*
* @return true if given object is an Array, false otherwise
*/
function is_array(object) {
try {
return object instanceof Array;
} catch (error) {
// if the client doesn't support the instanceof operator this has the same result, albeit at a lower speed
return (obj.constructor.toString().indexOf("Array") != -1);
}
}
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>ImageCycle example</title>
<script type="text/javascript" src="image_cycle.js"/>
</head>
<body>
<div id="gallery"><img src="images/sample.jpg" alt="what you see when there's no gallery"/></div>
<script type="text/javascript">
gallery = new ImageCycle(document.getElementById('gallery'), [
['images/one.jpg', "one's company"],
['images/two.jpg', "two's a crowd", 'crowded places make me itchy, especially my trigger finger'],
['images/three.jpg', "three's a party", '"Party on, Wayne" "Party on, Garth"']
]);
</script>
</body>
</html>
/**
* The ExtendedImageCycle class is based on the ImageCycle class.
*
* @param Node
* This container node is expected to contain at least one 'img' tag.
* The first 'img' tag found will be used for displaying the cyled images.
* The second 'img' tag found will be used for displaying the extra images.
* @param Node
* Title element
* @param Node
* Descritption element
* @param Array of Array of String [optional]
* Array of Arrays, each of which contains in this order:
* + image path
* + title
* + description
* + optionally the additional image path
*
* Expects the generated elements to be positioned/styled by CSS.
*
* Generates buttons inside the container node.
* The buttons are 'a' tags with classes 'next_image' and 'previous_image', each containing an 'img' tag
* pointing to '/images/next_image.png' or '/images/previous_image.png', respectively.
*
* The ExtendedImageCycle class extends upon and modifies the ImageCycle class in these regards:
* - It adds an optional second image for every data set
* - Instead of the optional caption with an associated style it uses a mandatory description,
* which may contain markup, in contrast to the plain-text caption.
* - It utilizes more space, namely additional nodes for the description and the title
*
* Requires DOM functions!
* Almost no error handling throughout!!
*/
function ExtendedImageCycle(container, title, description, images) {
this.container = container;
for(i in this.container.childNodes)
if (this.container.childNodes[i].tagName
&& this.container.childNodes[i].tagName.toLowerCase() == 'img') {
if (this.image) {
this.extra_image = this.container.childNodes[i];
break;
} else
this.image = this.container.childNodes[i];
}
this.title = title;
this.description = description;
this.images = [];
this.set_images(images);
this.forth_button = document.createElement('a');
this.forth_button.cycler = this;
this.forth_button.onclick = this.next_image;
image = document.createElement('img');
image.src = this.next_image_button_path;
image.alt = this.next_image_label;
this.forth_button.appendChild(image);
this.forth_button.className = 'next_image';
this.container.appendChild(this.forth_button);
this.back_button = document.createElement('a');
this.back_button.cycler = this;
this.back_button.onclick = this.previous_image;
image = document.createElement('img');
image.src = this.previous_image_button_path;
image.alt = this.previous_image_label;
this.back_button.appendChild(image);
this.back_button.className = 'previous_image';
this.container.appendChild(this.back_button);
}
// global defaults
ExtendedImageCycle.prototype.next_image_label = 'next image';
ExtendedImageCycle.prototype.next_image_button_path = 'images/next_image.png';
ExtendedImageCycle.prototype.previous_image_label = 'previous image';
ExtendedImageCycle.prototype.previous_image_button_path = 'images/previous_image.png';
/**
* Updates the array of images with the given array.
*
* @param Array of Array of String
*
* @return false if given object is not an array
*
* Expects an Array of Arrays, each of which contains in this order:
* the image path, the title, the description, optionally the additional image path.
*/
ExtendedImageCycle.prototype.set_images = function(new_images) {
if (is_array(new_images)) {
this.images = [];
for(i in new_images) {
array = new_images[i];
object = {};
object.path = array[0];
object.title = array[1];
object.description = array[2];
if (array[3])
object.extra_path = array[3];
this.images.push(object);
}
count_down = this.images.length;
while (count_down-- > 0 && this.image_data().path != this.image.src)
this.cycle_images_forth();
this.update_image();
} else {
return false;
}
}
/**
* Updates the display elements with the current image data.
*/
ExtendedImageCycle.prototype.update_image = function() {
data = this.image_data();
this.image.src = data.path;
this.title.innerHTML = data.title;
this.description.innerHTML = data.description;
if(data.extra_path) {
this.extra_image.src = data.extra_path;
this.show_extra_image();
} else
this.hide_extra_image();
}
/**
* Returns the data for the current image.
*
* @return Object
*/
ExtendedImageCycle.prototype.image_data = function() {
return this.images[0];
}
/**
* Tells the cycling object to advance by one image.
*
* Is automatically added to the generated next-button,
* expects to be called from there.
*/
ExtendedImageCycle.prototype.next_image = function() {
this.cycler.cycle_images_forth();
this.cycler.update_image();
}
/**
* Tells the cycling object to regress by one image.
*
* Is automatically added to the generated previous-button,
* expects to be called from there.
*/
ExtendedImageCycle.prototype.previous_image = function() {
this.cycler.cycle_images_back();
this.cycler.update_image();
}
/**
* Cycles the images array, putting the first element to the rear.
*/
ExtendedImageCycle.prototype.cycle_images_forth = function() {
this.images.push(this.images.shift());
}
/**
* Cycles the images array, getting the last element to the front.
*/
ExtendedImageCycle.prototype.cycle_images_back = function() {
this.images.unshift(this.images.pop());
}
/**
* Makes the optional additional image visible.
*/
ExtendedImageCycle.prototype.show_extra_image = function() {
this.extra_image.removeAttribute('style');
}
/**
* Makes the optional additional image invisible.
*/
ExtendedImageCycle.prototype.hide_extra_image = function() {
this.extra_image.setAttribute('style', 'display: none');
}
// Helper functions not directly part of the class
/**
* Checks whether the given object is an Array.
*
* @param Object
*
* @return true if given object is an Array, false otherwise
*/
function is_array(object) {
try {
return object instanceof Array;
} catch (error) {
// if the client doesn't support the instanceof operator this has the same result, albeit at a lower speed
return (obj.constructor.toString().indexOf("Array") != -1);
}
}
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>ImageCycle example</title>
<script type="text/javascript" src="image_cycle_extended.js"/>
</head>
<body>
<p id="title" style="font-weight: bold"/>
<div id="gallery">
<img src="images/sample.jpg" alt="what you see when there's no gallery"/>
<img src="images/extra.jpg" alt="what else you see when there's no gallery"/>
</div>
<div id="description"/>
<script type="text/javascript"><![CDATA[
gallery = new ExtendedImageCycle(
document.getElementById('gallery'),
document.getElementById('title'),
document.getElementById('description'),
[
['images/one.jpg', "one's company", ''],
['images/two.jpg', "two's a crowd", 'crowded places make me <em>itchy</em>, especially my <strong>trigger finger</strong>'],
['images/three.jpg', "three's a party", '<blockquote>Party on, Wayne</blockquote><blockquote>Party on, Garth</blockquote>', 'images/wayne.jpg']
]
);
]]></script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment