0

I am trying to make a website that contains images with text right below them (the text and images match up) that can be reordered.

I have arrays with the image URLs, descriptions, and webpage links that the images link to. For ex:

var imgArray = ["dog.jpg", "cat.jpg", "frog.jpg", "mouse.jpg"] //but with actual URLs for the image
var linkArray = ["dog.html", "cat.html", "frog.html", "mouse.html"]
var descArray = ["Dog", "Cat", "Frog", "Mouse"] //text that will be displayed below the image

The images that I want to show up on the screen are based on another condition that results in an array called works (ex. works = [1, 4] which means images 1 and 4 should show up on the screen). I plan on accessing the details for images 1 and 4 through the aforementioned arrays.

Also, if the user selects from a dropdown, they can change the order of the images that are displayed (ex: instead of image 1 then 3 then 4, it will change to image 4 then 1 then 3, based on another array).

Right now, what I have for the images (there will be no more than 15 displayed, but can be less based on the condition) is just:

<div class = "wrapper">
    <div class = "gallery" name="gallery" id = "gallery"><div name = "desc" class = "desc"></div></div>
    <div class = "gallery" name="gallery" id = "gallery"><div name = "desc" class = "desc"></div></div>
    <div class = "gallery" name="gallery" id = "gallery"><div name = "desc" class = "desc"></div></div>
    <div class = "gallery" name="gallery" id = "gallery"><div name = "desc" class = "desc"></div></div>
    <div class = "gallery" name="gallery" id = "gallery"><div name = "desc" class = "desc"></div></div>
    <div class = "gallery" name="gallery" id = "gallery"><div name = "desc" class = "desc"></div></div>
    <div class = "gallery" name="gallery" id = "gallery"><div name = "desc" class = "desc"></div></div>
    <div class = "gallery" name="gallery" id = "gallery"><div name = "desc" class = "desc"></div></div>
    <div class = "gallery" name="gallery" id = "gallery"><div name = "desc" class = "desc"></div></div>
    <div class = "gallery" name="gallery" id = "gallery"><div name = "desc" class = "desc"></div></div>
    <div class = "gallery" name="gallery" id = "gallery"><div name = "desc" class = "desc"></div></div>
    <div class = "gallery" name="gallery" id = "gallery"><div name = "desc" class = "desc"></div></div>
    <div class = "gallery" name="gallery" id = "gallery"><div name = "desc" class = "desc"></div></div>
    <div class = "gallery" name="gallery" id = "gallery"><div name = "desc" class = "desc"></div></div>
    <div class = "gallery" name="gallery" id = "gallery"><div name = "desc" class = "desc"></div></div>
</div>

I am sure this is bad style, but I could not find a better way to do it, in order for it to work with my next part and allow it to be stylized based on "gallery" and "desc". Then, in order to actually display the images (based on the starting order), I have:

var imgGallery = document.getElementsByName("gallery");
var imgDesc = document.getElementsByName("desc");

for(i=0;i<works.length;i++){
      var a = document.createElement('a');
      var img = document.createElement("IMG");
      a.setAttribute('href', linkArray[works[i]-1]);
      img.setAttribute('src', imgUrls[works[i]-1]);
      img.setAttribute('width', '300');
      img.setAttribute('height', '225');
      a.appendChild(img);
      var desc = document.createElement("P")
      desc.innerText = descArray[works[i]-1];
      imgGallery[i].appendChild(desc);
      imgDesc[i].appendChild(a);

    }

    }

Note: works is the array that contains the images that should be displayed - ex. works = [1, 2, 4] Also, I know it looks like desc and gallery switched, but it works when I do this.

Then, using certain conditions, I have arr2, which is an array that has the same numbers but in a different order - ex: arr2 = [1, 4, 2]. This next code displays the images in the arr2 order:

var wrapper = document.getElementsByClassName("wrapper");
var items = wrapper[0].children;
var elements = document.createDocumentFragment();

arr2.forEach(function(idx) {
  elements.appendChild(items[idx].cloneNode(true));
});

wrapper[0].innerHTML = null;
wrapper[0].appendChild(elements);

*I got this code from a user on Stack Overflow from a different question.

Now, for the issue:

This code works just fine if the works and arr2 arrays contain all of the images possible (ex. [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] as there are fifteen total images possible - I did not show all of them in the imgArray, linkArray, descArray example).

However, if there are fewer than fifteen images (for ex. just [1, 5, 7, 15]) then it will not work, and not all of the images that should show up do (such as maybe just image 1 and 7 show up). I would like to know how to make this work no matter how many images are in the "works" array.

BTW: If someone has a better approach in doing this entirely, that would be appreciated as well. In summary, what I am trying to do is:

  1. Display specific images based on an array that changes per the user (ex: works = [1, 4, 7] - I would like to display images 1, 4, 7 with their respective descriptions)

  2. If a dropdown option is selected, change the ordering of the images (and the corresponding descriptions).

I do not think I can just do document.createElement with imgs randomly in the body because I need to put them in the "gallery" and "desc" classes in order to stylize them.

EDIT: I used this answer from a different post, so now I am sort of able to reorder the images. I have this code (similar to what I had above):

if (document.getElementById('search').value == "option1") {
    //code that finds goodArray for option2
}
if (document.getElementById('search').value == "option2") {
    //code that finds goodArray for option2
}

    var wrapper = document.getElementsByClassName("wrapper");
    var items = wrapper[0].children;
    var elements = document.createDocumentFragment();

    goodArray.forEach(function(idx) {
        elements.appendChild(items[idx].cloneNode(true)); //line 353
    });

    wrapper[0].innerHTML = null;
    wrapper[0].appendChild(elements);

goodArray is the array that has the order of the images - ex: arr2 = [1, 4, 7, 8] based on option # search is the id of my dropdown option1 is the value of the first option in my dropdown, option2 is value of second option, etc.

The other code (staring with var wrapper and onwards) is being shared by all dropdowns. My current issue is that the first dropdown that is clicked (assuming the page starts blank) will work, but the next dropdown that is clicked will not change the images, even though it would work by itself. I am not sure how to make it so the code starting at "var wrapper = " and onwards will work and "restart" every time a new option is clicked. I tried placing it in the individual ifs but this did not work either.

The error that I am getting after selecting the second dropdown is:

Uncaught TypeError: Cannot read property 'cloneNode' of undefined
    at choice.html:354
    at Array.forEach (<anonymous>)
    at change (choice.html:353)
    at HTMLSelectElement.onchange (choice.html:56)
Meow
  • 1
  • 1
  • 1
    I found [this](https://stackoverflow.com/questions/34685316/reorder-html-elements-in-dom) post which is quite similar. Only difference was it used Jquery. You might want to look into it – Kenzoid Dec 27 '19 at 21:30
  • I have this, and it works when the array had all of the images ([1, 2, 3, 4, 5...14, 15]) but would not work if there were fewer than this. – Meow Dec 27 '19 at 21:32
  • 3
    why do you have 3 arrays with properties that all belong together?? I mean `const = [{url: 'dog.html', img: 'dog.jpg', text: 'Dog'},....]` would make more sense. – fubar Dec 27 '19 at 21:44
  • that is how i made it for readability (on my part) but i do understand that is not good style. – Meow Dec 27 '19 at 21:44
  • this will create a dependency on the index of all three arrays being the same so if one item goes missing they are "out of bounce'". Which might explain one of your problems. – fubar Dec 27 '19 at 21:46
  • thank you for the suggestion (and i will change it for the future) but i know that this is not the problem that is causing my error because it works when all of the images are supposed to be shown & i have checked repeatedly and i know that the array items are correct and ordered – Meow Dec 27 '19 at 21:50
  • You'll have to make the arrays always have the same number of items; if there are fewer images -- say you're missing "cat.jpg" -- just leave it blank: `var imgArray = ["dog.jpg", , "frog.jpg", "mouse.jpg"]`. You'll need to modify your code to check for undefined elements in the array. – Heretic Monkey Dec 27 '19 at 21:51
  • sorry i am a little confused on what you are saying. are you saying it will not work in general or this will cause it not work then the works array has fewer than the 15 total images (which is my problem)? – Meow Dec 27 '19 at 21:53
  • I'm saying that if you have fewer that 15 total images, add empty values until you have 15 total images. I have no idea how you're creating these arrays now; as @fubar says, this is a very problematic method for storing related information. – Heretic Monkey Dec 27 '19 at 21:55
  • does arr2 hold the correct number of items to be displayed? – fubar Dec 27 '19 at 22:22
  • I used @kenzoid suggestion in order to change some code. I edited my question accordingly but am still having some errors. – Meow Dec 27 '19 at 23:43

1 Answers1

0

Now to answer your question: you use an array which holds the order of your dropdown which you mention you already have. I presume that the value in the dropdown -1 corresponds with the index of the 3 arrays where your data is?

If this is the case use:

const imgArray = ["dog.jpg", "cat.jpg", "frog.jpg", "mouse.jpg"]
const linkArray = ["dog.html", "cat.html", "frog.html", "mouse.html"]
const descArray = ["Dog", "Cat", "Frog", "Mouse"]

const arr2 = [2,3,1,4]

// assuming you only have one wrapper
const wrapper = document.querySelector(`.wrapper`)

arr2.map(element=>{
    const div = document.createElement(`div`);
    // adding a class to element 
    div.classList.add(`gallery`)

    const a = document.createElement(`a`)
    // element coresponds to 2 for the first run, then 3 then 1 then 4
    a.setAttribute('href', linkArray[element-1]);

    const p =document.createElement(`p`)
    p.innerText = descArray[element-1]

    div.appendChild(a)
    div.appendChild(p)
    //..... you get the idea

}) 

This will clean up some of the code and also generate the displayed elements in the right order so you don't have to sort them again.

fubar
  • 383
  • 2
  • 11