Skip to content

Instantly share code, notes, and snippets.

@kodie
Created September 12, 2023 18:23
Show Gist options
  • Save kodie/73e923a3dc5eb6d8f680463ac43d1af4 to your computer and use it in GitHub Desktop.
Save kodie/73e923a3dc5eb6d8f680463ac43d1af4 to your computer and use it in GitHub Desktop.
A modal function created by a former HM developer - Simply here so we can pull it in remotely until it can be replaced
// BUG
// Sometimes a newly opened modal sets the image height to zero
// Stupid ios menu bar showing/hiding counts as a screen resize
// Can't calculate height with padding before resize to adjust top positioning
// - After resize, calculate the difference between the height of the content
// and the height of the image-holder and set the top of the content to
// scrollTop + the difference in top height
// -------------------------------------------------------------------------------
jQuery(function($) {
$('a[data-hmodal]').on('click', function(e) {
e.preventDefault();
var currentHmodalElement = $(this),
hmodalId = currentHmodalElement.data('hmodal'),
liveImages = [],
liveImageIdx = 0,
fadeSpeed = 400,
resizeSpeed = 400,
imageBound = false,
customOrder = false,
activeAnimation = false,
resizingImageActive = false,
swappingCaption = false,
$hmodalContent,
$activeImage,
imgPaddingTop,
imgPaddingRight,
imgPaddingBottom,
imgPaddingLeft;
// Get custom settings
// -------------------------------------------------------------------------------
if($('[data-for-hmodal="' + hmodalId + '"]').length) {
var $theModal = $('[data-for-hmodal="' + hmodalId + '"]');
if($theModal.data('hmodal-imagebound') == true) {
imageBound = true;
}
if($theModal.data('hmodal-custom-order').length) {
customOrder = true;
}
}
// Give each hmodal element an index
// -------------------------------------------------------------------------------
$('[data-hmodal="' + hmodalId + '"]').each(function(i) {
if(!$(this).data('hmodal-idx')) {
$(this).attr('data-hmodal-idx', i);
}
});
/**
* Detect if the image being displayed is portrait or landscape
* param DOM Object img Image being activated
* return String portrait/landscape Image orientation
*/
function portraitOrLandscape(img) {
if(img[0].naturalHeight > img[0].naturalWidth) {
return 'portrait';
} else {
return 'landscape';
}
}
/**
* Resize everything when the modal is opened for the firs time or an image changes
* return Void
*/
function resizeModal() {
if(resizingImageActive == false) {
resizingImageActive = true;
getUpdatedPadding();
var maxImageHeight = $activeImage[0].naturalHeight,
maxImageWidth = $activeImage[0].naturalWidth,
containerHeight = ($('#hmodal-content').outerHeight()),
containerWidth = $('#hmodal-content').outerWidth() - (imgPaddingLeft + imgPaddingRight),
waitTime = 0;
// If for some reason the sizes are 0 then wait a moment and retry
// -------------------------------------------------------------------------------
if(maxImageHeight == 0 || maxImageHeight == 0) {
waitTime = 500;
}
setTimeout(function() {
maxImageHeight = $activeImage[0].naturalHeight;
maxImageWidth = $activeImage[0].naturalWidth;
// Make adjustment of top position to account for possible padding
$('#hmodal').animate({'top': $(window).scrollTop()});
// Reset the content height so we can compare
// -------------------------------------------------------------------------------
$('#hmodal-content').css({height: $('body').height()});
containerHeight = $('#hmodal-content').height();
// Check if the raw image height is taller than allowed. If so, then base the
// height off of the raw image height
// -------------------------------------------------------------------------------
var adjustedHeight = maxImageHeight < containerHeight ? maxImageHeight : containerHeight - (imgPaddingTop + imgPaddingBottom);
// Base the new width off of the maximum image height since the image is shorter than our content
var newWidth = adjustedHeight * (maxImageWidth / maxImageHeight);
// If the image is wider than the body, then we're either on mobile or have a
// massive image. We'll want to adjust by width and not height
// -------------------------------------------------------------------------------
if(newWidth + (imgPaddingLeft + imgPaddingRight) > containerWidth) {
var newAdjustedHeight = containerWidth * (maxImageHeight / maxImageWidth);
$activeImage.animate({height: newAdjustedHeight, width: containerWidth}, resizeSpeed);
// If imageBound, then keep all content and controlls no wider than the image
if(imageBound) {
$('#hmodal-counter, #hmodal-caption-wrap, #hmodal-controller').css('max-width', containerWidth)
}
} else {
$activeImage.animate({height: adjustedHeight, width: newWidth}, resizeSpeed);
// If imageBound, then keep all content and controlls no wider than the image
if(imageBound) {
$('#hmodal-counter, #hmodal-caption-wrap, #hmodal-controller').css('max-width', newWidth)
}
}
$activeImage.animate({opacity: 1}, fadeSpeed, function() {
// Reset this variable to indicate animation done so we can do other stuff
activeAnimation = false;
resizingImageActive = false;
});
}, waitTime);
}
}
/**
* Switch to the next image
* return Void
*/
function nextImg() {
if(activeAnimation == false) {
activeAnimation = true;
liveImageIdx += 1;
if(liveImageIdx > liveImages.length - 1) {
liveImageIdx = 0
}
$('#hmodal-caption-wrap').height($('#hmodal-caption').height());
updateSlideCount();
$activeImage.animate({opacity: 0}, fadeSpeed, function() {
$activeImage.attr('src', liveImages[liveImageIdx].link).attr('alt', liveImages[liveImageIdx].alt);
$activeImage.load(function() {
if(imageBound) {
if(swappingCaption == false) {
swappingCaption = true;
$('#hmodal-caption').html(liveImages[liveImageIdx].caption);
$('#hmodal-caption-wrap').animate({height: $('#hmodal-caption').height()}, resizeSpeed, function() {
swappingCaption = false;
});
}
} else {
$('#hmodal-caption').html(liveImages[liveImageIdx].caption);
}
resizeModal();
});
});
}
}
/**
* Switch to the previous image
* return Void
*/
function prevImg() {
if(activeAnimation == false) {
activeAnimation = true;
liveImageIdx -= 1;
if(liveImageIdx < 0) {
liveImageIdx = liveImages.length - 1;
}
$('#hmodal-caption-wrap').height($('#hmodal-caption').height());
updateSlideCount();
$activeImage.animate({opacity: 0}, fadeSpeed, function() {
$activeImage.attr('src', liveImages[liveImageIdx].link).attr('alt', liveImages[liveImageIdx].alt);
$activeImage.load(function() {
if(imageBound) {
if(swappingCaption == false) {
swappingCaption = true;
$('#hmodal-caption').html(liveImages[liveImageIdx].caption);
$('#hmodal-caption-wrap').animate({height: $('#hmodal-caption').height()}, resizeSpeed, function() {
swappingCaption = false;
});
}
} else {
$('#hmodal-caption').html(liveImages[liveImageIdx].caption);
}
resizeModal();
});
});
}
}
/**
* Update the slide counter as images are clicked through
* return Void
*/
function updateSlideCount() {
$('#hmodal-img-idx').html(liveImageIdx + 1);
}
/**
* Close the modal when the close button is clicked
* return Void
*/
function closeModal() {
// Can only run the callback on one element. Otherwise
// the callback runs once for each one.
// -------------------------------------------------------------------------------
liveImageIdx = 0;
$('#hmodal-next').off('click');
$('#hmodal-prev').off('click');
$('#hmodal, #hmodal-overlay').fadeOut(fadeSpeed, function() {
// $activeImage.css('opacity', 0);
$('#hmodal').remove();
});
}
/**
* Get padding since it might have been updated via CSS
* return Void
*/
function getUpdatedPadding() {
imgPaddingTop = parseInt($('#hmodal-image-container').css('padding-top'));
imgPaddingRight = parseInt($('#hmodal-image-container').css('padding-right'));
imgPaddingBottom = parseInt($('#hmodal-image-container').css('padding-bottom'));
imgPaddingLeft = parseInt($('#hmodal-image-container').css('padding-left'));
}
/**
* Cycle through each modal item and build the modal image data
* return Void
*/
function storeLiveImages(id) {
$('a[data-hmodal="' + id + '"]:not(.hmodal-exclude)').each(function(i) {
liveImages.push(
{
'link': $(this).attr('href'),
'caption': $(this).data('hmodal-caption'),
'alt': $(this).children('img').attr('alt') ? $(this).children('img').attr('alt') : '',
'idx': $(this).data('hmodal-idx')
}
);
});
}
/**
* Set the image index based on what image was clicked to open the modal
* param String path Image path to check for
* return Void
*/
function setLiveIndex(el) {
liveImageIdx = el.data('hmodal-idx');
}
var resizeTimer;
$(window).on('resize', function(e) {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(function() {
$('#hmodal-caption-wrap').height($('#hmodal-caption').height());
resizeModal();
if(swappingCaption == false) {
swappingCaption = true;
$('#hmodal-caption-wrap').animate({height: $('#hmodal-caption').height()}, resizeSpeed, function() {
swappingCaption = false;
});
}
}, 350);
});
// Begin generating the base modal wrappers
// -------------------------------------------------------------------------------
$('body').append('<div id="hmodal" />');
var $hmodal = $('#hmodal');
$hmodal.append('<div id="hmodal-content" />');
$hmodalContent = $('#hmodal-content');
$hmodalContent.append('<div id="hmodal-image-container" />');
storeLiveImages(hmodalId);
setLiveIndex(currentHmodalElement);
$('#hmodal-image-container').append('<img id="active-hmodal-img" src="' + liveImages[liveImageIdx].link + '" alt="' + liveImages[liveImageIdx].caption + '" />');
// imgPadding is used to adjust the math when resizing image to account for CSS padding
getUpdatedPadding();
var $activeImage = $('#active-hmodal-img');
var bindingElement = imageBound ? $('#hmodal-image-container') : $hmodalContent;
// Add controls and info elements
// -------------------------------------------------------------------------------
if(customOrder) {
var settings = $('[data-for-hmodal="' + hmodalId + '"]').data('hmodal-custom-order').split(' ');
for(var i=0; i<settings.length; i++) {
switch(settings[i]) {
case 'controls':
bindingElement.append('<div id="hmodal-controller" />');
$('#hmodal-controller').html('<a href="javascript:void(0)" id="hmodal-prev"></a><a href="javascript:void(0)" id="hmodal-next"></a>');
$('#hmodal-next').off().on('click', nextImg);
$('#hmodal-prev').off().on('click', prevImg);
break;
case 'close':
bindingElement.append('<div id="hmodal-close" />');
$('#hmodal-close').html('<a href="javascript:void(0)" id="hmodal-close-button"></a>');
$('#hmodal-close').off().on('click', closeModal);
break;
case 'counter':
bindingElement.append('<div id="hmodal-counter" />');
$('#hmodal-counter').html('<span id="hmodal-img-idx">' + (liveImageIdx + 1) + '</span><span id="hmodal-count-delimiter">/</span><span id="hmodal-total-images">' + liveImages.length + '</span>');
break;
case 'caption':
bindingElement.append('<div id="hmodal-caption-wrap" />');
$('#hmodal-caption-wrap').html('<span id="hmodal-caption">' + currentHmodalElement.data('hmodal-caption') + '</span>');
break;
}
}
} else {
bindingElement.append('<div id="hmodal-controller" />');
$('#hmodal-controller').html('<a href="javascript:void(0)" id="hmodal-prev"></a><a href="javascript:void(0)" id="hmodal-next"></a>');
$('#hmodal-next').off().on('click', nextImg);
$('#hmodal-prev').off().on('click', prevImg);
bindingElement.append('<div id="hmodal-close" />');
$('#hmodal-close').html('<a href="javascript:void(0)" id="hmodal-close-button"></a>');
$('#hmodal-close').off().on('click', closeModal);
bindingElement.append('<div id="hmodal-counter" />');
$('#hmodal-counter').html('<span id="hmodal-img-idx">' + (liveImageIdx + 1) + '</span><span id="hmodal-count-delimiter">/</span><span id="hmodal-total-images">' + liveImages.length + '</span>');
bindingElement.append('<div id="hmodal-caption-wrap" />');
$('#hmodal-caption-wrap').html('<span id="hmodal-caption">' + currentHmodalElement.data('hmodal-caption') + '</span>');
}
$hmodal.append('<div id="hmodal-overlay" />');
$('#hmodal-overlay').fadeTo(400, $('#hmodal-overlay').css('opacity'));
$('#hmodal-overlay').off().on('click', closeModal);
// Position the modal to be 32px from the to of the current window position
// -------------------------------------------------------------------------------
$('#hmodal').css({'top': $(window).scrollTop()});
resizeModal();
setTimeout(function() {
$hmodalContent.css('visibility', 'visible').animate({opacity: 1}, fadeSpeed);
}, 500);
});
});
#hmodal {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 99999;
#hmodal-content {
visibility: hidden;
opacity: 0;
position: absolute;
top: 0;
left: 0;
right: 0;
margin: 0 auto;
height: 100%;
width: 90%;
max-height: 75%;
z-index: 99999;
opacity: 0;
overflow: visible !important;
#hmodal-image-container {
position: absolute;
left: 50%;
top: 50%;
background: #fff;
padding: .5rem;
// max-width: 100%;
-moz-transform: translate(-50%, -50%);
-o-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
-moz-transition: all 0.15s ease-in-out;
-o-transition: all 0.15s ease-in-out;
-ms-transition: all 0.15s ease-in-out;
-webkit-transition: all 0.15s ease-in-out;
transition: all 0.15s ease-in-out;
img {
height: auto;
width: auto;
max-height: 100%;
max-width: none;
opacity: 0;
display: block;
}
}
}
#hmodal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #000;
opacity: .75;
display: none;
z-index: 88888;
}
#hmodal-close-button {
color: #fff;
&:after {
content: "Close";
}
}
#hmodal-next {
color: #fff;
&:after {
content: "Next";
}
}
#hmodal-prev {
color: #fff;
&:after {
content: "Prev";
}
}
#hmodal-counter, #hmodal-caption-wrap, #hmodal-controller {
color: #fff;
}
#hmodal-caption {
display: inline-block;
overflow: hidden;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment