Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community!

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript

How do you change images based off a click?

I'm trying to create a image gallery, that changes depending on which button/link you click. However, I can only seem to get the "image 1" gallery to show. "Image 2" and "image 3" don't appear at all.

I'm trying to achieve - when you click on one gallery, the other images that were there previously would disappear. I only want one gallery to display at a time. However, my "deleteImages" function doesn't seem to working, either.

I continue to get the error whenever I click "image 2" - Uncaught TypeError: Cannot read property 'length' of undefined

let link = document.querySelectorAll('a');
let imageContainer = document.querySelector('.image-container');
let images = document.querySelector('.image-container img');
let localPath;
let imageURL;


let imagesName = [

    {
        "album1": ["pexels-photo-412537.jpeg", "pexels-photo-748898.jpeg", "pexels-photo-1313191.jpeg"]
    },

    {
        "album2": ["pexels-photo-426893.jpeg", "pexels-photo-573241.jpeg", "pexels-photo-1139370.jpeg"]
    },

    {
        "album3": ["pexels-photo-58625.jpeg", "pexels-photo-1308684.jpeg", "pexels-photo-1308754.jpeg"]
    }

];


function deleteImages() {

    if(imageContainer.childElementCount > 0) {

        images.forEach((img) => {
            imageContainer.removeChild(img);
        }); 

    }
}



function createImg(src) {

        let img = document.createElement("img");
        img.src = src;
        imageContainer.appendChild(img);

}


link.forEach((link) => {
    link.addEventListener('click', (e) => {
        e.preventDefault();

        switch(link.textContent) {
            case "image 1":

                deleteImages();
                localPath = "/images/album1/";
                for (let i = 0; i < imagesName.length; i++) {
                    imageURL = imagesName[i].album1;
                    for (let n = 0; n < imageURL.length; n++) {
                        createImg(localPath + imageURL[n]);
                    }
                }

                break;
            case "image 2":

                deleteImages();
                localPath = "/images/album2/";
                for (let i = 0; i < imagesName.length; i++) {
                    imageURL = imagesName[i].album2;
                    for (let n = 0; n < imageURL.length; n++) {
                        createImg(localPath + imageURL[n]);
                    }
                } 

                break;
            case "image 3":

                deleteImages();
                localPath = "/images/album3/";
                for (let i = 0; i < imagesName.length; i++) {
                    imageURL = imagesName[i].album3;
                    for (let n = 0; n < imageURL.length; n++) {
                        createImg(localPath + imageURL[n]);
                    }
                }

                break;
        }

    });
});
<!DOCTYPE html>
<html lang="">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title></title>

    <style>

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

        img {
            max-height: 333px;
            overflow: hidden;
        }

    </style>
</head>

<body>

   <a href="#">image 1</a>
   <a href="#">image 2</a>
   <a href="#">image 3</a>

   <div class="image-container">



   </div>


<script src="js.js"></script>
</body>
</html>

1 Answer

Steven Parker
Steven Parker
225,730 Points

At first glance, I noticed that "deleteImages" relies on "images", which is being set at the beginning of the program, before any images are added. And "querySelectorAll" returns a static NodeList and not a live HTMLCollection, so it will remain empty even after images are added.

Hey, Steven - I found this solution worked:

(function() {
var links = document.querySelectorAll('a'); // grab all <a> tags that trigger the image gallery
    var imageContainer = document.querySelector('.image-container'); // grab the div that will contain the imahges
    var localPath; // create an undefined variable that will later create the first part of the path to each image


    var imagesName = { // array of image names 

            "album1": ["pexels-photo-412537.jpeg", "pexels-photo-748898.jpeg", "pexels-photo-1313191.jpeg", "pexels-photo-935973.jpeg"],

            "album2": ["pexels-photo-426893.jpeg", "pexels-photo-573241.jpeg", "pexels-photo-1139370.jpeg"],

            "album3": ["pexels-photo-58625.jpeg", "pexels-photo-1308684.jpeg", "pexels-photo-1308754.jpeg"]

    };




    function createImg(src) { // after the deleteImages function runs, populate the .image-container with new images 

            var img = document.createElement("img");
            img.src = src;
            imageContainer.appendChild(img);

    }




        localPath = "/images/album1/"; // this code will automatically generate images from album 1 when the page loads 
        for (let i = 0; i < imagesName.album1.length; i++) {
                createImg(localPath + imagesName.album1[i]); 
        }



    function deleteImages() { // delete any images that were in the .image-container 

        var images = document.querySelectorAll('.image-container img');

        if(imageContainer.childElementCount > 0) { // check if .image-container has any children - if so, delete them. 

            images.forEach((img) => {
                imageContainer.removeChild(img);
            }); 

        }
    }



    links.forEach((link) => { // loop through <a> tags and create click event on each 
        link.addEventListener('click', () => {

            switch (link.textContent) { // check name of link clicked 
                case "image 1":

                    deleteImages();
                    localPath = "/images/album1/"; // folder directory path to album 1
                    for (let i = 0; i < imagesName.album1.length; i++) {
                        createImg(localPath + imagesName.album1[i]); // join folder directory with image name to create img src attr 
                    }


                    break;
                case "image 2":

                    deleteImages();
                    localPath = "/images/album2/"; // folder directory path to album 2
                    for (let i = 0; i < imagesName.album2.length; i++) {
                        createImg(localPath + imagesName.album2[i]); // join folder directory with image name to create img src attr 
                    }


                    break;
                case "image 3":

                    deleteImages();
                    localPath = "/images/album3/"; // folder directory path to album 3
                    for (let i = 0; i < imagesName.album3.length; i++) {
                        createImg(localPath + imagesName.album3[i]); // join folder directory with image name to create img src attr 
                    }

                    break;
            }

        });
    });

    })();

I basically changed how I looped through the array. At first, I had an array that had 3 objects that contained an array within. So, I changed that to one object, that contains the 3 albums.

Steven Parker
Steven Parker
225,730 Points

And moving where "images" is assigned fixed the empty list issue. Good deal.

Kristian Woods — You can mark the question solved by choosing a "best answer".
And happy coding!