1

I want to apply function to multiple div pairs in multiple wrappers. Divs should be selected in parallel by order from 2 different classes.

The best what I can think of is to make an array with n pairs of divs from n number of modules, but I don't know if the concept itself and syntax is right.

Now, I want to apply function to first/second/third/... object-1 and descript-1 divs inside only one module at the same time. And the same goes for next module, instead function should be applied to object-2 - descript-2 pair.

Updated code:

Now I have three different functions, one for next-prev buttons, one for thumbnail control and last one for showing object/description class divs and highlighting thumbs. I've made nested functions attempt but it doesn't work. Should I declare vars, and get content before making 3 separate modules.forEach functions?

<script>
  // nodes Array
  let modules = Array.prototype.slice.call(document.querySelectorAll(".module"));
 
  // Loop over the modules without index.
  modules.forEach(function(module){
 
  var divIndex = 1;
  showDivs(divIndex);
 
  // Objects, descr, thumbs
  let objects = module.querySelectorAll(".object");
  let descripts = module.querySelectorAll(".descript");
  let thumbs = module.querySelectorAll(".thumb");

  // next-prev buttons
  function plusDivs(n) {
    showDivs(divIndex += n);
  }
 
  // thumb control
  function currentDiv(n) {
    showDivs(divIndex = n);
  }

  // div display
  function showDivs(n) {
    if (n > objects.length) {divIndex = 1}
    if (n < 1) {divIndex = objects.length}
    // hide content, shade thumb
    objects.style.display = "none";
    descripts.style.display = "none";
    thumbs.className = thumbs.className.replace(" active", "");
 
    // show only selected object-descr pair and highlight thumb
    for(var i = 0; i < objects.length; i++) {
      objects[divIndex-1].style.display = "block";
      descripts[divIndex-1].style.display = "block";
      thumbs[divIndex-1].className += " active";
    }
  }
});
</script>
<div class="module">
  <div class="content">LOREM IPSUM 1</div>
 
  <div class="wrapper">
  <div class="content">LOREM IPSUM 1</div>
  
    <div class="object">o1</div>
    <div class="object">o2</div>
    <div class="object">o3</div>

    <div class="descript">d1</div>
    <div class="descript">d2</div>
    <div class="descript">d3</div>
 
    <div class="thumb" onclick="currentDiv(1)">t1</div>
    <div class="thumb" onclick="currentDiv(2)">t2</div>
    <div class="thumb" onclick="currentDiv(3)">t3</div>
  
    <a class="prev" onclick="plusDivs(-1)">X</a>
    <a class="next" onclick="plusDivs(1)">X</a>
   </div>
</div>

<div class="module">
  <div class="content">LOREM IPSUM 2</div>
 
  <div class="wrapper">
  <div class="content">LOREM IPSUM 2</div>
  
    <div class="object">o4</div>
    <div class="object">o5</div>
    <div class="object">o6</div>

    <div class="descript">d4</div>
    <div class="descript">d5</div>
    <div class="descript">d6</div>
  
    <div class="thumb" onclick="currentDiv(1)">t4</div>
    <div class="thumb" onclick="currentDiv(2)">t5</div>
    <div class="thumb" onclick="currentDiv(3)">t6</div>
  
    <a class="prev" onclick="plusDivs(-1)">X</a>
    <a class="next" onclick="plusDivs(1)">X</a>
  </div>
</div>
deneb
  • 21
  • 5
  • can you please give a jsfiddle or codepen to edit your code? – Arun Kumar Apr 12 '19 at 11:58
  • 4
    @ArunKumar Please don't ask for Fiddles or CodePens (or any other code at 3rd party sites). Stack Overflow has its own code execution environment, called a code snippet, that allows you to place executable code right in your question. – Scott Marcus Apr 12 '19 at 12:02
  • @Arun Kumar - I've putted the code in a snippet. Feel free to edit. – deneb Apr 22 '19 at 16:05

1 Answers1

1

Is this what you are looking for? See comments inline. Also, don't use .getElementsByClassName().

// Convert the node list into an Array for the best browser compatibility with Array.forEach()
let modules = Array.prototype.slice.call(document.querySelectorAll("div[class^='module-']"));

// Loop over the modules.
// The Array.forEach() method is much simpler than manual loops because you don't have
// to maintain the loop indexer.
modules.forEach(function(module){

  // Get the objects and descriptions (no arrays needed here because we're just
  // going to need to use indexes against the node lists.
  let objects = module.querySelectorAll("div[class='object']");
  let descriptions = module.querySelectorAll("div[class='descript']");
  
  // Clear out the objects and descriptions in the module.
  // Start by getting all the objects and descriptions into an array.
  let objectsDescriptions = Array.prototype.slice.call(
      module.querySelectorAll("[class='object'], [class='descript']"));
      
  // Then remove each item in the array from the document
  objectsDescriptions.forEach(function(element){ element.parentNode.removeChild(element); });
  
  // Loop the amount of times that matches the number of items in one of the arrays.
  // Here, a regular counting loop makes the most sense because it's all about looping
  // the correct number of times, not looping over DOM elements.
  for(var i = 0; i < objects.length; i++){
    // Repopulate the module with the current child elements, but in the new sequence
    module.insertBefore(objects[i], module.querySelector(".thumb"));
    module.insertBefore(descriptions[i], module.querySelector(".thumb"));
  }
  
  // Set up all the clickable elements to have click event handlers
  module.addEventListener("click", function(evt){
    // Check to see if it was a thumb or a prev/next that was clicked
    if(evt.target.classList.contains("thumb")){
      // Show the div that has the same index as the thumbnail that was clicked
      let thumbs = Array.prototype.slice.call(evt.target.parentNode.querySelectorAll(".thumb"));
      showDiv(evt.target.parentNode, thumbs.indexOf(evt.target));
    } else if(evt.target.classList.contains("prev") || evt.target.classList.contains("next")){
      // Show the div according to the data-offset attribute of the clicked element
      showDiv(evt.target.parentNode, +evt.target.dataset.offset, true);
    } 
  });
  
});

// ****************************************************************
// CODE TO SHOW DIVS
// ****************************************************************

let currentIndex = 0;

// div display
function showDiv(parent, index, nav) {
  // Hide all the objects and descriptions
  let items = parent.querySelectorAll(".object, .descript");
  Array.prototype.slice.call(items).forEach(function(el){
    el.classList.add("hidden");
  });
  
  if(nav){
    currentIndex += index; // Adjust for the offset
    if(currentIndex < 0){
      currentIndex = 0;
    } else if(currentIndex > (items.length / 2) - 1){
      currentIndex = (items.length / 2) - 1;
    }
    // Show just the ones that are supposed to be shown
    parent.querySelectorAll(".object")[currentIndex].classList.remove("hidden");
    parent.querySelectorAll(".descript")[currentIndex].classList.remove("hidden");
  } else {   
    // Show just the ones that are supposed to be shown
    parent.querySelectorAll(".object")[index].classList.remove("hidden");
    parent.querySelectorAll(".descript")[index].classList.remove("hidden");
  }

}
.hidden { display:none; }
.thumb, .prev, .next { cursor:pointer; color:blue; }
<div class="module-1">
    <div class="object">o1</div>
    <div class="object">o2</div>
    <div class="object">o3</div>

    <div class="descript">d1</div>
    <div class="descript">d2</div>
    <div class="descript">d3</div>
    
  <div class="thumb">t1</div>
  <div class="thumb">t2</div>
  <div class="thumb">t3</div>
  
  <span class="prev" data-offset="-1">&lt;</span>
  <span class="next" data-offset="1">&gt;</span>    
</div>

<div class="module-2">
    <div class="object">o4</div>
    <div class="object">o5</div>
    <div class="object">o6</div>

    <div class="descript">d4</div>
    <div class="descript">d5</div>
    <div class="descript">d6</div>
    
  <div class="thumb">t4</div>
  <div class="thumb">t5</div>
  <div class="thumb">t6</div>
  
  <span class="prev" data-offset="-1">&lt;</span>
  <span class="next" data-offset="1">&gt;</span>   
</div>
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
  • Thanks @Scott Marcus! `Array.forEach()` and `querySelectorAll` are very good ideas. However I think I haven't provided enough details. I can't use `appendChild` since all object-description div pairs should be hidden beside selected one - `.style.display: "none";` in my question. I've updated my code, and there are additional wrappers for styling and content that I don't want to change - look class "content" - so `module.innerHTML = "";` will not work for me. – deneb Apr 22 '19 at 15:23
  • @silex See updated answer. It just required some additional code to set up the event bindings and separate the navigation elements from the content elements. – Scott Marcus Apr 22 '19 at 18:24
  • Sorry for delay. @Scott Marcus, it seems your code works partially. I've checked on multiple browsers. First thing it seems that `el.classList.add("hidden");` doesnt work properly since when you run snippet all items are visible instead the first one. I'm not very familiar with js, so don't know if you included this or not. Second thing is when you navigate to let say `thumb3` and then use prev button its moving you back to element 1 skipping element 2. Anyway thanks again for help. – deneb May 23 '19 at 12:13