0
votes

I've repetitive variables declarations, I think they can be improved with a for-loop, but my attempt fail.

I tried a for-loop declarer for an array but my syntax doesn't work.

I've this "Working code":


<div class ="menu1 item1-1"></div>
<div class ="menu1 item1-2"></div>
<div class ="menu1 item1-3"></div>
<div class ="menu1 item1-4"></div>

<div class ="menu2 item2-1"></div>
<div class ="menu2 item2-2"></div>
<div class ="menu2 item2-3"></div>
<div class ="menu2 item2-4"></div>

<div class ="menu3 item3-1"></div>
<div class ="menu3 item3-2"></div>
<div class ="menu3 item3-3"></div>
<div class ="menu3 item3-4"></div>

<div class ="menu4 item4-1"></div>
<div class ="menu4 item4-2"></div>
<div class ="menu4 item4-3"></div>
<div class ="menu4 item4-4"></div>


var menu1 = document.getElementsByClassName('menu1');
var menu2 = document.getElementsByClassName('menu2');
var menu3 = document.getElementsByClassName('menu3');
var menu4 = document.getElementsByClassName('menu4');

for (let i = 1; i < menu1.length; i++) {

    menu1[i].addEventListener('click', function(){ menu1[i].classList.add("test"); });
}
for (let i = 1; i < menu2.length; i++) {

    menu2[i].addEventListener('click', function(){ menu2[i].classList.add("test"); });
}
for (let i = 1; i < menu3.length; i++) {

    menu3[i].addEventListener('click', function(){ menu3[i].classList.add("test"); });
}
for (let i = 1; i < menu4.length; i++) {

    menu4[i].addEventListener('click', function(){ menu4[i].classList.add("test"); });
}


My attempt to reduce it it was this:


var MENU = [];

for (let i = 1; i <= 4; i++) {

  MENU.push("menu" + i + =document.getElementsByClassName('menu' + i +));

}
for (let j = 1; j < 5; j++) {
    for (let i = 0; i < MENU[j].length; i++) {
        MENU[j][i].addEventListener('click', function(){ MENU[j][i].classList.add("test"); });
    }
}

expected result:

the same result as the "Working code":

when I click on a "menu1" div:


<div class ="menu1 item1-1 test"></div>
<div class ="menu1 item1-2 test"></div>
<div class ="menu1 item1-3 test"></div>
<div class ="menu1 item1-4 test"></div>

<div class ="menu2 item2-1"></div>
<div class ="menu2 item2-2"></div>
<div class ="menu2 item2-3"></div>
<div class ="menu2 item2-4"></div>

<div class ="menu3 item3-1"></div>
<div class ="menu3 item3-2"></div>
<div class ="menu3 item3-3"></div>
<div class ="menu3 item3-4"></div>

<div class ="menu4 item4-1"></div>
<div class ="menu4 item4-2"></div>
<div class ="menu4 item4-3"></div>
<div class ="menu4 item4-4"></div>

actual error messages: "Unexpected token =" "Uncaught TypeError: MENU[0][i].addEventListener is not a function"

Edit: I've found the HUGE syntax error, is fixed but I get this error now.

Edit2: My question was too open and not specific, I narrow it down and corrected some syntax suggested by comments.

Edit3: I'm trying to:

click a "menu1" element to select all "menu1" elements. click a "menu2" element to select all "menu2" elements. click a "menu3" element to select all "menu3" elements. click a "menu4" element to select all "menu4" elements.

5
code which you are presenting have missing + sign after first i it should be something like MENU.push("menu" + i + "=document.getElementsByClassName('menu" + i +"')"); - noname
my other consideration is why you have so many classes? instead of calling classes menu1, menu2 etc. you could have one class menu and if needed in your css style each class like .menu:first-child, .menu:nth-child(2) etc then your code would be much easier only one call to var menus = document.getElementsByClassName('menu'); and in variable menus you have array of elements - noname

5 Answers

1
votes

Add to each menu or panl, etc. a generic class, for example: menu. So that your elements would look like:

<div class="menu menu1"></div>
<div class="menu menu2"></div>
<div class="menu menu3"></div>
<div class="menu menu4"></div>

Now you can select all of the elements with a single selector.

var menus = document.getElementsByClassName('menu');
console.log(menus[0]); // <div class="menu menu1"></div>
console.log(menus[1]); // <div class="menu menu2"></div>
console.log(menus[2]); // <div class="menu menu3"></div>
console.log(menus[3]); // <div class="menu menu4"></div>

And they are stored in a HTMLCollection which you can loop over with a for loop.

for (var i = 0; i < menus.length; i++) {
    console.log(menus[i]); // Logs each <div class="menu"> element.
}

Check the documentation of MDN to see more examples on how to use the getElementsByClassName method.

0
votes

You can use querySelectorAll.

Something like this:

const selector = '';
for (let i = 1; i <= 4; i++) {
  selector += `${selector ? ', ' : ''}menu${i}, panel${i}, navprev${i}, navrtrn${i}, navnext${i}`
}

document.querySelectorAll(selector).forEach(element => {
  element.addEventListener('click', (event) => {
    // check if it is a menu:
    if (event.target.matches('[class^=menu]')) {
       // a menu was clicked
    }
  });
});

If you don't want to change any of your other code, you can do this:

const classNamePrefixes = ['menu', 'panel', 'navprev', 'navrtrn', 'navnext'];
const varNameAndClassNamePrefix = classNamePrefixes.map((cnp) => {
  const varName = cnp.replace('nav', '').replace('panel', 'panl');
  return {
    varName,
    cnp
  };
});

for (let i = 1; i <= 4; i++) {
  varNameAndClassNamePrefix.forEach(({ varName, cnp }) => {
     window[`${varName}${i}`] = document.getElementsByClassName(`${cnp}${i}`)
  });
}
0
votes

Why do not you just give them another special attribute juste like <div class="whatever" specialAttribute>Some Html content</div> ? and then use the selector just like const selectors=document.querySelectorAll("[specialAttribute]");

0
votes

If you assign the item to the ordinal position in the index it's a lot easier.

  MENU[i] = document.getElementsByClassName('menu" + i);

Then MENU[1] will be the reference to the first element, etc.

0
votes

You shouldn't be pushing strings, just push the result of calling the function.

MENU.push(document.getElementsByClassName('menu' + i));

Note that array indexes start at 0. So MENU[0] will contain the elements with class menu1, and so on.