HarveyEL HarveyEL - 2 months ago 9
jQuery Question

'Typeerror: is not a function' when using JQuery module pattern

https://jsfiddle.net/fvkuk7c0/3/

style.css

.gallery .container {
max-width: 600px;
background-color: black;
margin: 0 auto;
text-align: center;
}

.container .frame {
position: relative;
}

.container .frame .control {
position: absolute;
top: 40%;
font-family: Futura, "Trebuchet MS", Arial, sans-serif;
color: black;
font-size: 3em;
}

.container .frame .control:hover {
background-color: rgba(0, 0, 0, 0.4);
-webkit-transition: background 0.25s linear;
-moz-transition: background 0.25s linear;
-ms-transition: background 0.25s linear;
-o-transition: background 0.25s linear;
transition: background 0.25s linear;
}

.container .frame #previous {
left: 5px;
}

.container .frame #next {
right: 5px;
}

.container .frame #previous::before {
content: '<';
}

.container .frame #next::before {
content: '>';
}

.container .frame .image {
width: 100%;
}

.container .image:first-child {
display: inline-block;
}

.container .image:not(:first-child) {
display: none;
}

.container .image img {
width: 100%;
height: auto;
}

.container .thumbnails {
padding: 15px 0 15px 0;
width: 100%;
display: block;
overflow-x: scroll;
white-space: nowrap;
}

.container .thumbnails div {
display: table-cell;
height: 100px;
width: auto;
}

.container .thumbnails img {
width: inherit;
height: inherit;
opacity: 0.8;
transition: opacity .25s ease-in-out;
-moz-transition: opacity .25s ease-in-out;
-webkit-transition: opacity .25s ease-in-out;
}

.container .thumbnails div.active img {
opacity: 0.4;
transition: opacity .25s ease-in-out;
-moz-transition: opacity .25s ease-in-out;
-webkit-transition: opacity .25s ease-in-out;
}


index.html

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Image Gallery</title>
<link rel="stylesheet" href="css/style.css">
<!--[if lt IE 9]>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.js"></script>
<![endif]-->
</head>

<body>

<section class="gallery">
<div class="container">
<div class="frame">
<div class="image">
<img src="https://placeimg.com/640/480/people" alt="">
</div>
<div class="image">
<img src="https://placeimg.com/640/480/arch" alt="">
</div>
<div class="image">
<img src="https://placeimg.com/640/480/animals" alt="">
</div>
<div class="image">
<img src="https://placeimg.com/640/480/nature" alt="">
</div>
<div class="image">
<img src="https://placeimg.com/640/480/tech" alt="">
</div>
<div class="image">
<img src="https://placeimg.com/640/480/grayscale" alt="">
</div>
<div class="image">
<img src="https://placeimg.com/640/480/sepia" alt="">
</div>
<div class="image">
<img src="https://placeimg.com/640/480/people" alt="">
</div>
<div class="image">
<img src="https://placeimg.com/640/480/arch" alt="">
</div>
<div class="image">
<img src="https://placeimg.com/640/480/animals" alt="">
</div>
<div class="image">
<img src="https://placeimg.com/640/480/nature" alt="">
</div>
<div class="image">
<img src="https://placeimg.com/640/480/tech" alt="">
</div>
<div class="image">
<img src="https://placeimg.com/640/480/grayscale" alt="">
</div>
<div class="image">
<img src="https://placeimg.com/640/480/sepia" alt="">
</div>
<div class="image">
<img src="https://placeimg.com/640/480/people" alt="">
</div>
<div class="image">
<img src="https://placeimg.com/640/480/arch" alt="">
</div>
<div class="image">
<img src="https://placeimg.com/640/480/animals" alt="">
</div>
<div class="image">
<img src="https://placeimg.com/640/480/nature" alt="">
</div>
<div class="image">
<img src="https://placeimg.com/640/480/tech" alt="">
</div>
<div class="image">
<img src="https://placeimg.com/640/480/grayscale" alt="">
</div>
<div class="image">
<img src="https://placeimg.com/640/480/sepia" alt="">
</div>
</div>
<div class="thumbnails"></div>
</div>
</section>

<p style="text-align: center;"><a href="http://github.com/harveylowndes/gallery">View on GitHub</a></p>

<!-- JQuery CDN -->
<script src="https://code.jquery.com/jquery-3.1.1.min.js" integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=" crossorigin="anonymous"></script>

<!--Gallery Javascript -->
<script src="js/gallerynew.js"></script>

</body>
</html>


gallerynew.js

var images = (function() {

var imageArray = $('.gallery .container .frame .image');
var currentImageIndex = 0;

var getImageArray = function() {
return $(imageArray);
};

var setCurrentImageIndex = function(newIndex) {
currentImageIndex = newIndex;
};

var getImage = function(index) {
return $(getImageArray()).get(index);
};

var getImageSource = function(index) {
var image = getImage(index);
var img = $(image).find('img').get(0);
var src = $(img).attr('src');
return src;
};

var getCurrentImageIndex = function() {
return currentImageIndex;
};

var clearCurrentImage = function() {
var current = getImage(getCurrentImageIndex);
$(current).fadeOut().css('display', 'none');

var currentThumbnail = $(thumbnails.getThumbnailArray()).find('div').get(getCurrentImageIndex);
$(currentThumbnail).removeClass('active');
};

/*
Display new image.
*/
var displayImage = function(index) {
var image = getImage(index);
$(image).fadeIn().css('display', 'inline-block');

var imageThumbnail = $(thumbnails.getThumbnailArray()).find('div').get(index);
$(imageThumbnail).addClass('active');

thumbnails.scrollThumbnails(); //Scroll to correct thumb.
};

/*
Display next image.
*/
var displayNextImage = function() {
clearCurrentImage();
var nextImage = getCurrentImageIndex() + 1;
setCurrentImageIndex(getCurrentImageIndex() + 1);
if (nextImage > getImageArray().length - 1) {
nextImage = 0;
setCurrentImageIndex(0);
}
displayImage(nextImage);
};

/*
Display previous image.
*/
var displayPreviousImage= function() {
clearCurrentImage();
var previousImage = getCurrentImageIndex() - 1;
setCurrentImageIndex(getCurrentImageIndex() - 1);
if (previousImage < 0) {
previousImage = getImageArray().length-1;
setCurrentImageIndex(images.getImageArray().length-1);
}
displayImage(previousImage);
};

// Public API
return {
getCurrentImageIndex : getCurrentImageIndex,
getImageArray : getImageArray,
getImageSource : getImageSource,
displayImage : displayImage,
displayPreviousImage, displayPreviousImage,
displayNextImage : displayNextImage,
clearCurrentImage : clearCurrentImage
};

})();

var thumbnails = (function() {
var thumbnailArray = $('.gallery .container .thumbnails');

var getThumbnailArray = function() {
return $(thumbnailArray);
};

var initThumbnails = function() {
images.getImageArray().each(function(index) {
var src = images.getImageSource(index);
$(getThumbnailArray()).append('<div><img src="' + src + '" /></div>');
});
};

var getScrollWidth = function() {
var width = 0;
var currentIndex = images.getCurrentImageIndex();
if(currentIndex - 1 != -1) {
for (var i = 0; i < images.getCurrentImageIndex - 1; i++) {
var current = $(getThumbnailArray()).find('div').get(i);
var image = $(current).find('img').get(0);
width = width + $(image).width();
}
return width;
} else {
return 0;
}
};

/*
Scroll to correct thumbnail.
*/
var scrollThumbnails = function() {
$(getThumbnailArray()).animate({
scrollLeft: getScrollWidth()
}, 500);
};

// Public API
return {
getThumbnailArray : getThumbnailArray,
initThumbnails : initThumbnails,
scrollThumbnails : scrollThumbnails
};

})();

var interval = (function() {
//Cycle stuff
var cycleTime = 5000;
var _intervalId;


var imageCycle = function() {
images.displayNextImage(); //Display next image.
};

var getCycleTime = function() {
return cycleTime; //Display next image.
};

/*
Interval manager for cycle.
*/
var intervalManager = function(flag, animate, time) {
if (flag)
_intervalId = setInterval(animate, time);
else
clearInterval(_intervalId);
}

function startInterval() {
intervalManager(true, imageCycle(), getCycleTime());
}

/*
Restarts the interval.
*/
function resetInterval() {
intervalManager(false); //Clear the interval.
intervalManager(true, imageCycle, cycleTime); //Start the interval.
}

// Public API
return {
startInterval : startInterval,
resetInterval: resetInterval
};
})();

$(document).ready(function() {
//Controls
$('.gallery .container .frame')
.append('<span id="previous" class="control"></span><span id="next" class="control"></span>');

//Generate thumbnails
thumbnails.initThumbnails();

//Set first thumb active.
var currentThumbnail = $(thumbnails.getThumbnailArray).find('div').get(images.getCurrentImageIndex());
$(currentThumbnail).addClass('active');

//Start interval.
interval.startInterval();
});

$(thumbnails.getThumbnailArray).find("div").click(function() { //Thumnail click.
var divIndex = $(this).index();
if(divIndex != images.getCurrentImageIndex) {
images.clearCurrentImage(); //Clear current image from browser.
images.getCurrentImageIndex = divIndex; //Set current image to clicked thumbnail index.
images.displayImage(images.getCurrentImageIndex); //Display new current image.
interval.resetInterval(); //Restart the interval.
}
});

$("#previous").click(function() { //Previous control click.
images.displayPreviousImage();
interval.resetInterval();
});

$("#next").click(function() { //Next control click.
images.displayNextImage();
interval.resetInterval();
});


I get the error:

TypeError: images.getCurrentImageIndex is not a function


I have searched around for the fix to this problem and I thought it may have been because it is calling the function before it has loaded but even with changes this still doesn't work.

Answer

The error is caused by the following code:

$(thumbnails.getThumbnailArray).find("div").click(function() { //Thumnail click.
    var divIndex = $(this).index();
    if(divIndex != images.getCurrentImageIndex) {
        images.clearCurrentImage(); //Clear current image from browser.
        images.getCurrentImageIndex = divIndex; //Set current image to clicked thumbnail index.
        images.displayImage(images.getCurrentImageIndex); //Display new current image.
        interval.resetInterval(); //Restart the interval.
    }
});

In that code, the images.getCurrentImageIndex is accessed as it were a number. But it is a function. So this condition:

if(divIndex != images.getCurrentImageIndex) {

... will always be true (both sides of the equation are coerced to string, and they are never equal).

Then things get worse:

images.getCurrentImageIndex = divIndex;

Now you destroy the function, and replace it with a plain number. Next time you call the "function" it will fail with the error you got.

So fix the above code block to:

$(thumbnails.getThumbnailArray).find("div").click(function() { //Thumnail click.
    var divIndex = $(this).index();
    if(divIndex != images.getCurrentImageIndex()) { // **Call it!
        images.clearCurrentImage(); //Clear current image from browser.
        images.setCurrentImageIndex(divIndex); // **Use the function!!
        images.displayImage(images.getCurrentImageIndex()); // **Call it!!
        interval.resetInterval(); //Restart the interval.
    }
});

That resolves the runtime error.

Other things to fix

There are several places where you reference the function, but should in fact call it:

var clearCurrentImage = function() {
    var current = getImage(getCurrentImageIndex);

You need to call getCurrentImageIndex:

var clearCurrentImage = function() {
    var current = getImage(getCurrentImageIndex()); // <--

And again, here:

var clearCurrentImage = function() {
    var current = getImage(getCurrentImageIndex);
    $(current).fadeOut().css('display', 'none');

    var currentThumbnail = $(thumbnails.getThumbnailArray()).find('div').get(getCurrentImageIndex);

The last line should end with:

    .get(getCurrentImageIndex()); // <---

And again, here:

var getScrollWidth = function() {
    var width = 0;
    var currentIndex = images.getCurrentImageIndex();
    if(currentIndex - 1 != -1) {
        for (var i = 0; i < images.getCurrentImageIndex - 1; i++) {

The for loop should be:

        for (var i = 0; i < images.getCurrentImageIndex() - 1; i++) {

... etc.