This question has already been answered:

There is a list (11 items)

<ul class="styled-list"> <li class="styled-list-item">1</li> <li class="styled-list-item">1</li> <li class="styled-list-item">1</li> <li class="styled-list-item">1</li> </ul> 

There is a set of paragraphs, each of which corresponds to a list item:

 <p class="lesson-info-box"><p> <p class="lesson-info-box"><p> <p class="lesson-info-box"><p> <p class="lesson-info-box"><p> 

Each paragraph contains transparency and absolute positioning through css, I want to make it so that when you click on the first element of the list, the transparency of the first paragraph disappears, it becomes visible, when you click on the second element of the list, the same thing happens with the second paragraph. Here is how I tried to solve the problem:

 var itemList = document.querySelectorAll('.styled-list .styled-list-item'); var detailsList = document.querySelectorAll('.lesson-info-box'); for(i=0;i<itemList.length;i++){ itemList[i].onclick = function(){ detailsList[i].className = "lesson-info-box onclick-list-item"; } }; 

I take two arrays, the first is all the elements of the list on the page, the second paragraphs. Then in the loop I loop through each element and hang a handler on it. I do not understand, but it does not work ... In the debugger, it is clearly seen that when I click on the first element, the value 11 is substituted in i. How to do it right?

Reported as a duplicate by participants Alexey Shimansky , user194374, Grundy javascript 12 Feb '17 at 12:46 .

A similar question was asked earlier and an answer has already been received. If the answers provided are not exhaustive, please ask a new question .

  • 2
  • @ Alexey Shimansky, thank you, I saw this topic, I recently began to get acquainted with js, I did not immediately understand that this was a solution to my problem, I will try) - Alexey Vladimirovich
  • one
    change the onclick handler to function(i){detailsList[i].className = "lesson-info-box onclick-list-item";}.bind(i) . The problem is that for each function it is not the specific value of the variable i that is attached, but the variable itself. And when the handler is called, it searches for the variable i and since the loop has already been committed to finding this variable with the value 11 - vihtor

1 answer 1

It will be correct not to attach a handler to each list item, but to use event delegation is the first time. If you do not know what it is, then in short it is an approach, when an event is hung not on each element, but on its “container” and already in the handler there is a check on which element the event occurred and the necessary actions are taken. What you need to know to solve the problem:
1. the item on which the event occurred.
2. its index in the array.
So, everything is simple with the first item, let's check the event.target , for the second item, you can use the forEach method.
Let me remind you that the element index is necessary for us to access the desired element in the array with descriptions.
You also need a function showing the required element.
Then gathering everything in a bunch to get the following code:

 // Итак получим все еобходимые нам узлы DOM, элементы списка и описания к ним сразу поместим в массив. Это понадобиться для работы с методом forEach var container = document.querySelector('.container dl'); var listItems = Array.prototype.slice.apply(container.querySelectorAll('dt')); var details = Array.prototype.slice.apply(container.querySelectorAll('dd')); // функция показа элемента, ее будем вызывать позже. function showDetails(arr, i) { arr[i].style.opacity = 1; } // вешаем обработчик на контейнер container.addEventListener('click', function(event) { event.preventDefault(); // это привычка :) отменяет действия браузера по умолчанию (полезно для обработки кликов по ссылкам..) if (event.target.tagName == 'DT') { // проверяем что событие возникло на элементе списка for (var i = 0; i < details.length; i++) { // просто проходим по массиву с описаниями и сбрасываем прозрачность. Необходимо для скрытия уже показанного описания при клике по другому элементу списка details[i].style.opacity = ''; } //Дальше на массиве с элементами списка запускаем forEach listItems.forEach(function(item, i, arr) { if (item == event.target) { // сравниваем каждый элемент массива с элементом события showDetails(details, i); // запускаем функцию показа описания } }); } }, false); 
 dd { opacity: 0; } 
 <div class="container"> <dl> <dt>элемент списка</dt> <dd>а это наверное описание термина или пункта списка</dd> <dt>элемент списка</dt> <dd>а это наверное описание термина или пункта списка</dd> <dt>элемент списка</dt> <dd>а это наверное описание термина или пункта списка</dd> <dt>элемент списка</dt> <dd>а это наверное описание термина или пункта списка</dd> <dt>элемент списка</dt> <dd>а это наверное описание термина или пункта списка</dd> </dl> </div> 

PS You can use the usual for loop, but then you need to keep the current i in the closure. To do this, you can use the functional expression with an immediately called function in the body of the loop. It looks like this:

 for(var i = 0; i < listItems.length; i++) { (function(i){ if(listItems[i] == event.target) { showDetails(details, i); } })(i); } 

[Addition]
As Grundy correctly noted, running around the elements in a loop each time can be expensive if we have several thousand of them ...
Therefore, instead of looping, the following can be applied directly to the markup in the response:
Since the description of the list item goes right after the element itself, you can get an element in the DOM tree, standing to the right of the one that was clicked and manipulate it.

 if(event.target.nextElementSibling.tagName == 'DD') { event.target.nextElementSibling.style.opacity = 1; } 
  • Thank you so much) I will understand. - Alexey Vladimirovich
  • @Alexey Vladimirovich, Yes, no way. JS closures are a powerful tool, mastering which to develop will become easier and more interesting. I also want to draw attention to the events and ways of hanging them. I highly recommend not using on{событие} - this approach is not flexible and outdated. - pepel_xD
  • something an example with delegation is very sad, each time to run in a loop through all the elements to determine which one was clicked? - Grundy
  • @Grundy, I’m not prfi js still learning myself, so I don’t know how else to get the index of the desired element in the array. If we abandon arrays, then it is in this markup that you can use DOM navigation, namely, perform actions on your right neighbor ... Although to whom I am telling all this .... How would you do? - pepel_xD
  • @pepel_xD, you can, for example, in the date attribute push the index, and take it from the target - Grundy