This question is not a duplicate, as it affects not only closures, but also other common errors in JS, as well as their elimination using the new features of the ES 2015 specification.

When iterating through elements, it is not possible to register correctly the click event handlers of the iterated elements. Events of all enumerated elements are registered by an anonymous function intended for the most recent element.

As planned. 1 When you hover over a fieldset message, the delete icon of each message is highlighted, and when you move the mouse, the delete icon of the corresponding message disappears again. 2 Hovering over the delete icon pops up a prompt with the time of the corresponding message. 3 When you click on the icon of the corresponding delete message, an alert over time of the corresponding message pops up.

I have 1 When hovering over any fieldset message, the delete icon of the last message is highlighted, while moving the mouse, the delete icon, the last message, disappears again. 2 This is the only thing that works correctly. 3 When you click on the icon to delete any message, an alert with the last message time pops up.

What am I doing wrong that I do not understand here and how can I do it right?

 window.addEventListener('load',function() { var imgs=document.getElementsByClassName('right')[0].getElementsByClassName('del'); for(var i=0,l=imgs.length;i<l;i++) { var img=imgs[i]; var doc=document.getElementById('mes-'+img.dataset.date+'-'+img.dataset.fid); doc.addEventListener('mouseover',function(){img.style.opacity=1;}); doc.addEventListener('mouseout',function(){img.removeAttribute('style');}); img.setAttribute('title','Delete '+img.dataset.title+' message'); img.addEventListener('click',function() { if(!isNaN(img.dataset.date)&&img.dataset.date>0&&!isNaN(img.dataset.fid)&&img.dataset.fid>0&&img.dataset.title) alert('Are you sure you want to delete '+img.dataset.title+' message?'); }); } }); 
 * { margin:0; padding:0; border:0; border-radius:5px; transition:all 0.2s linear; } html,body { height:100%; min-height:100%; } body { background-color:#fff; color:#000; font:12px/18px Arial,Helvetica,sans-serif; margin:0 auto; } fieldset { border:1px solid #ccc; } legend { font-weight:bold; } div.message { min-width:400px; width:400px; max-height:400px; margin:0 auto; padding:10px; text-align:center; } div.message div.left, div.message div.right { float:left; height:100%; overflow:auto; } div.message div.right { width:250px; text-align:center; } div.message div.right hr { border-top:1px solid #ccc; margin-top:10px; } div.message div.right hr+span { background-color:#fff; font-weight:bold; padding:0 10px; position:relative; bottom:10px; } div.message div.right fieldset { margin:0 0 10px; padding:10px; text-align:left; position:relative; } div.message div.right fieldset.from { margin-right:50px; border-color:#6f6; background-color:#dfd; color:#040; } div.message div.right fieldset.to { margin-left:50px; border-color:#66f; background-color:#ddf; color:#004; } div.message div.right fieldset img.del { width:16px; top:0; right:8px; } div.message img.del { position:absolute; opacity:0.3; } div.message img.del:hover { cursor:pointer; opacity:1; } 
 <!DOCTYPE html> <html lang="ru-ru" dir="ltr"> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"/> <title>Test</title> </head> <body> <div class="message"> <div class="left"></div> <div class="right"> <hr/><span>08.10.2016</span> <fieldset class="from" id="mes-1475888362-1"> <legend>Ilya Indigo&nbsp;<span>03:59</span></legend> <img class="del" data-date="1475888362" data-fid="1" data-title="03:59" src="http://dolinasnov.com/uploads/delete.png" alt="del"/> <p>Тестовое сообщение<br/>новая строка<br/>ещё одна строка<br/>и ещё одна строка.</p> </fieldset> <fieldset class="from" id="mes-1475888608-1"> <legend>Ilya Indigo&nbsp;<span>04:03</span></legend> <img class="del" data-date="1475888608" data-fid="1" data-title="04:03" src="http://dolinasnov.com/uploads/delete.png" alt="del"/> <p>Ещё одно.</p> </fieldset> <fieldset class="to" id="mes-1475889423-2"> <legend>Ilya Indigo 2&nbsp;<span>04:17</span></legend> <img class="del" data-date="1475889423" data-fid="2" data-title="04:17" src="http://dolinasnov.com/uploads/delete.png" alt="del"/> <p>Отвечаю.</p> </fieldset> <fieldset class="from" id="mes-1475892511-1"> <legend>Ilya Indigo&nbsp;<span>05:08</span></legend> <img class="del" data-date="1475892511" data-fid="1" data-title="05:08" src="http://dolinasnov.com/uploads/delete.png" alt="del"/> <p>Ещё одно.</p> </fieldset> </div> </div> </body> </html> 

Answer: The easiest and fastest way to declare all variables is via let instead of var .

  • one
    Possible duplicate question: How closures work in JavaScript - Grundy
  • one
    even this one is better: stackoverflow.com/questions/272864/… - Grundy
  • @Grundy I, of course, are familiar with JS on a 3- to 5-point scale, but I don’t understand one thing, why hang up a handler performing the same actions on each element in the loop? Is not it easier to hang the whole ONE on the container of these elements, and then check on which element the event occurred. What is the right thing to do? - pepel_xD
  • @pepel_xD, there was already a question here such as an example. And, if I'm not mistaken, there were a few more. In a nutshell: for each task you should look separately. - Grundy
  • @pepel_xD Please write an example JS-code in which my task is solved by the delegation method that you mentioned. - Ilya Indigo

3 answers 3

At first glance it may seem that there is a classic closure problem, and this is what it really is.

But! The solution here is much simpler: since the handler implies working with the element that was clicked on, the closure problem is solved using this instead of the img variable. Since, in this case, this - will be, just the element on which they clicked.

 window.addEventListener('load', function() { var imgs = document.getElementsByClassName('right')[0].getElementsByClassName('del'); for (var i = 0, l = imgs.length; i < l; i++) { var img = imgs[i]; var doc = document.getElementById('mes-' + img.dataset.date + '-' + img.dataset.fid); doc.addEventListener('mouseover', function() { // здесь проблема остается, так как обработчик вешается на fieldset img.style.opacity = 1; }); doc.addEventListener('mouseout', function() { // здесь проблема остается, так как обработчик вешается на fieldset img.removeAttribute('style'); }); img.setAttribute('title', 'Delete ' + img.dataset.title + ' message'); img.addEventListener('click', function() { if (!isNaN(this.dataset.date) && this.dataset.date > 0 && !isNaN(this.dataset.fid) && this.dataset.fid > 0 && this.dataset.title) alert('Are you sure you want to delete ' + this.dataset.title + ' message?'); }); } }); 
 legend { font-weight: bold; } div.message { width: 400px; } div.message div.right fieldset { text-align: left; position: relative; border-color: #6f6; background-color: #dfd; color: #040; } div.message div.right fieldset.to { background-color: #ddf; } div.message div.right fieldset img.del { width: 16px; top: 0; right: 8px; position: absolute; opacity: 0.3; } 
 <div class="message"> <div class="left"></div> <div class="right"> <hr/><span>08.10.2016</span> <fieldset class="from" id="mes-1475888362-1"> <legend>Ilya Indigo&nbsp;<span>03:59</span> </legend> <img class="del" data-date="1475888362" data-fid="1" data-title="03:59" src="http://dolinasnov.com/uploads/delete.png" alt="del" /> <p>Тестовое сообщение <br/>новая строка <br/>ещё одна строка <br/>и ещё одна строка.</p> </fieldset> <fieldset class="from" id="mes-1475888608-1"> <legend>Ilya Indigo&nbsp;<span>04:03</span> </legend> <img class="del" data-date="1475888608" data-fid="1" data-title="04:03" src="http://dolinasnov.com/uploads/delete.png" alt="del" /> <p>Ещё одно.</p> </fieldset> <fieldset class="to" id="mes-1475889423-2"> <legend>Ilya Indigo 2&nbsp;<span>04:17</span> </legend> <img class="del" data-date="1475889423" data-fid="2" data-title="04:17" src="http://dolinasnov.com/uploads/delete.png" alt="del" /> <p>Отвечаю.</p> </fieldset> <fieldset class="from" id="mes-1475892511-1"> <legend>Ilya Indigo&nbsp;<span>05:08</span> </legend> <img class="del" data-date="1475892511" data-fid="1" data-title="05:08" src="http://dolinasnov.com/uploads/delete.png" alt="del" /> <p>Ещё одно.</p> </fieldset> </div> </div> 

Causes of error:

Variables declared with the var keyword have a functional scope. This means that neither is available within the entire function in which it is declared and within the declared nested functions.

Although the variable declaration is inside for , only one variable will be declared , the value of which will change.

Since handlers are executed not immediately inside the loop, but when a certain event occurs, they take the value of the specified variable at the time of execution, and this, in this case, is the value assigned at the last iteration of the loop.

Possible solutions:

  1. Creating a function that creates a specific handler: in this case, the problem is solved by creating a factory function that will create a handler function based on the parameter. A special case of this approach is the use of IIFE , when the factory function is called immediately upon declaration

    An example with a named factory:

     function createClickHandler(img) { return function() { if (!isNaN(img.dataset.date) && img.dataset.date > 0 && !isNaN(img.dataset.fid) && img.dataset.fid > 0 && img.dataset.title) alert('Are you sure you want to delete ' + img.dataset.title + ' message?'); }; } function createMouseoverHandler(img) { return function() { img.style.opacity = 1; }; } function createMouseoutHandler(img) { return function() { img.removeAttribute('style'); }; } window.addEventListener('load', function() { var imgs = document.getElementsByClassName('right')[0].getElementsByClassName('del'); for (var i = 0, l = imgs.length; i < l; i++) { var img = imgs[i]; var doc = document.getElementById('mes-' + img.dataset.date + '-' + img.dataset.fid); doc.addEventListener('mouseover', createMouseoverHandler(img)); doc.addEventListener('mouseout', createMouseoutHandler(img)); img.setAttribute('title', 'Delete ' + img.dataset.title + ' message'); img.addEventListener('click', createClickHandler(img)); } }); 
     legend { font-weight: bold; } div.message { width: 400px; } div.message div.right fieldset { text-align: left; position: relative; border-color: #6f6; background-color: #dfd; color: #040; } div.message div.right fieldset.to { background-color: #ddf; } div.message div.right fieldset img.del { width: 16px; top: 0; right: 8px; position: absolute; opacity: 0.3; } 
     <div class="message"> <div class="left"></div> <div class="right"> <hr/><span>08.10.2016</span> <fieldset class="from" id="mes-1475888362-1"> <legend>Ilya Indigo&nbsp;<span>03:59</span> </legend> <img class="del" data-date="1475888362" data-fid="1" data-title="03:59" src="http://dolinasnov.com/uploads/delete.png" alt="del" /> <p>Тестовое сообщение <br/>новая строка <br/>ещё одна строка <br/>и ещё одна строка.</p> </fieldset> <fieldset class="from" id="mes-1475888608-1"> <legend>Ilya Indigo&nbsp;<span>04:03</span> </legend> <img class="del" data-date="1475888608" data-fid="1" data-title="04:03" src="http://dolinasnov.com/uploads/delete.png" alt="del" /> <p>Ещё одно.</p> </fieldset> <fieldset class="to" id="mes-1475889423-2"> <legend>Ilya Indigo 2&nbsp;<span>04:17</span> </legend> <img class="del" data-date="1475889423" data-fid="2" data-title="04:17" src="http://dolinasnov.com/uploads/delete.png" alt="del" /> <p>Отвечаю.</p> </fieldset> <fieldset class="from" id="mes-1475892511-1"> <legend>Ilya Indigo&nbsp;<span>05:08</span> </legend> <img class="del" data-date="1475892511" data-fid="1" data-title="05:08" src="http://dolinasnov.com/uploads/delete.png" alt="del" /> <p>Ещё одно.</p> </fieldset> </div> </div> 

    Example with IIFE:

     window.addEventListener('load', function() { var imgs = document.getElementsByClassName('right')[0].getElementsByClassName('del'); for (var i = 0, l = imgs.length; i < l; i++) { var img = imgs[i]; var doc = document.getElementById('mes-' + img.dataset.date + '-' + img.dataset.fid); doc.addEventListener('mouseover', function createMouseoverHandler(imgParam) { return function() { imgParam.style.opacity = 1; }; }(img)); doc.addEventListener('mouseout', function createMouseoutHandler(imgParam) { return function() { imgParam.removeAttribute('style'); }; }(img)); img.setAttribute('title', 'Delete ' + img.dataset.title + ' message'); img.addEventListener('click', function createClickHandler(imgParam) { return function() { if (!isNaN(imgParam.dataset.date) && imgParam.dataset.date > 0 && !isNaN(imgParam.dataset.fid) && imgParam.dataset.fid > 0 && imgParam.dataset.title) alert('Are you sure you want to delete ' + imgParam.dataset.title + ' message?'); }; }(img)); } }); 
     legend { font-weight: bold; } div.message { width: 400px; } div.message div.right fieldset { text-align: left; position: relative; border-color: #6f6; background-color: #dfd; color: #040; } div.message div.right fieldset.to { background-color: #ddf; } div.message div.right fieldset img.del { width: 16px; top: 0; right: 8px; position: absolute; opacity: 0.3; } 
     <div class="message"> <div class="left"></div> <div class="right"> <hr/><span>08.10.2016</span> <fieldset class="from" id="mes-1475888362-1"> <legend>Ilya Indigo&nbsp;<span>03:59</span> </legend> <img class="del" data-date="1475888362" data-fid="1" data-title="03:59" src="http://dolinasnov.com/uploads/delete.png" alt="del" /> <p>Тестовое сообщение <br/>новая строка <br/>ещё одна строка <br/>и ещё одна строка.</p> </fieldset> <fieldset class="from" id="mes-1475888608-1"> <legend>Ilya Indigo&nbsp;<span>04:03</span> </legend> <img class="del" data-date="1475888608" data-fid="1" data-title="04:03" src="http://dolinasnov.com/uploads/delete.png" alt="del" /> <p>Ещё одно.</p> </fieldset> <fieldset class="to" id="mes-1475889423-2"> <legend>Ilya Indigo 2&nbsp;<span>04:17</span> </legend> <img class="del" data-date="1475889423" data-fid="2" data-title="04:17" src="http://dolinasnov.com/uploads/delete.png" alt="del" /> <p>Отвечаю.</p> </fieldset> <fieldset class="from" id="mes-1475892511-1"> <legend>Ilya Indigo&nbsp;<span>05:08</span> </legend> <img class="del" data-date="1475892511" data-fid="1" data-title="05:08" src="http://dolinasnov.com/uploads/delete.png" alt="del" /> <p>Ещё одно.</p> </fieldset> </div> </div> 

  2. Use let / const . Using these keywords, which appeared in ES2015, allows you to declare variables the scope of which the current block. This means that at each iteration of the loop a new variable will be created, so during the execution of the handler, the correct value will be used.

     window.addEventListener('load', function() { var imgs = document.getElementsByClassName('right')[0].getElementsByClassName('del'); for (var i = 0, l = imgs.length; i < l; i++) { let img = imgs[i]; var doc = document.getElementById('mes-' + img.dataset.date + '-' + img.dataset.fid); doc.addEventListener('mouseover', function() { img.style.opacity = 1; }); doc.addEventListener('mouseout', function() { img.removeAttribute('style'); }); img.setAttribute('title', 'Delete ' + img.dataset.title + ' message'); img.addEventListener('click', function() { if (!isNaN(img.dataset.date) && img.dataset.date > 0 && !isNaN(img.dataset.fid) && img.dataset.fid > 0 && img.dataset.title) alert('Are you sure you want to delete ' + img.dataset.title + ' message?'); }); } }); 
     legend { font-weight: bold; } div.message { width: 400px; } div.message div.right fieldset { text-align: left; position: relative; border-color: #6f6; background-color: #dfd; color: #040; } div.message div.right fieldset.to { background-color: #ddf; } div.message div.right fieldset img.del { width: 16px; top: 0; right: 8px; position: absolute; opacity: 0.3; } 
     <div class="message"> <div class="left"></div> <div class="right"> <hr/><span>08.10.2016</span> <fieldset class="from" id="mes-1475888362-1"> <legend>Ilya Indigo&nbsp;<span>03:59</span> </legend> <img class="del" data-date="1475888362" data-fid="1" data-title="03:59" src="http://dolinasnov.com/uploads/delete.png" alt="del" /> <p>Тестовое сообщение <br/>новая строка <br/>ещё одна строка <br/>и ещё одна строка.</p> </fieldset> <fieldset class="from" id="mes-1475888608-1"> <legend>Ilya Indigo&nbsp;<span>04:03</span> </legend> <img class="del" data-date="1475888608" data-fid="1" data-title="04:03" src="http://dolinasnov.com/uploads/delete.png" alt="del" /> <p>Ещё одно.</p> </fieldset> <fieldset class="to" id="mes-1475889423-2"> <legend>Ilya Indigo 2&nbsp;<span>04:17</span> </legend> <img class="del" data-date="1475889423" data-fid="2" data-title="04:17" src="http://dolinasnov.com/uploads/delete.png" alt="del" /> <p>Отвечаю.</p> </fieldset> <fieldset class="from" id="mes-1475892511-1"> <legend>Ilya Indigo&nbsp;<span>05:08</span> </legend> <img class="del" data-date="1475892511" data-fid="1" data-title="05:08" src="http://dolinasnov.com/uploads/delete.png" alt="del" /> <p>Ещё одно.</p> </fieldset> </div> </div> 

  3. Use instead of for , forEach functions . Since in this case the function parameter will be used in the closure, and not the variable, the value at the time of the execution of the event handler will be correct. Due to the fact that the getElementsByClassName function does not return an array, but a collection, you cannot use the forEach function with it. But you can convert this collection into an array, or use call .

    Example:

     window.addEventListener('load', function() { var imgs = document.getElementsByClassName('right')[0].getElementsByClassName('del'); [...imgs].forEach(function(img) { var doc = document.getElementById('mes-' + img.dataset.date + '-' + img.dataset.fid); doc.addEventListener('mouseover', function() { img.style.opacity = 1; }); doc.addEventListener('mouseout', function() { img.removeAttribute('style'); }); img.setAttribute('title', 'Delete ' + img.dataset.title + ' message'); img.addEventListener('click', function() { if (!isNaN(img.dataset.date) && img.dataset.date > 0 && !isNaN(img.dataset.fid) && img.dataset.fid > 0 && img.dataset.title) alert('Are you sure you want to delete ' + img.dataset.title + ' message?'); }); }); }); 
     legend { font-weight: bold; } div.message { width: 400px; } div.message div.right fieldset { text-align: left; position: relative; border-color: #6f6; background-color: #dfd; color: #040; } div.message div.right fieldset.to { background-color: #ddf; } div.message div.right fieldset img.del { width: 16px; top: 0; right: 8px; position: absolute; opacity: 0.3; } 
     <div class="message"> <div class="left"></div> <div class="right"> <hr/><span>08.10.2016</span> <fieldset class="from" id="mes-1475888362-1"> <legend>Ilya Indigo&nbsp;<span>03:59</span> </legend> <img class="del" data-date="1475888362" data-fid="1" data-title="03:59" src="http://dolinasnov.com/uploads/delete.png" alt="del" /> <p>Тестовое сообщение <br/>новая строка <br/>ещё одна строка <br/>и ещё одна строка.</p> </fieldset> <fieldset class="from" id="mes-1475888608-1"> <legend>Ilya Indigo&nbsp;<span>04:03</span> </legend> <img class="del" data-date="1475888608" data-fid="1" data-title="04:03" src="http://dolinasnov.com/uploads/delete.png" alt="del" /> <p>Ещё одно.</p> </fieldset> <fieldset class="to" id="mes-1475889423-2"> <legend>Ilya Indigo 2&nbsp;<span>04:17</span> </legend> <img class="del" data-date="1475889423" data-fid="2" data-title="04:17" src="http://dolinasnov.com/uploads/delete.png" alt="del" /> <p>Отвечаю.</p> </fieldset> <fieldset class="from" id="mes-1475892511-1"> <legend>Ilya Indigo&nbsp;<span>05:08</span> </legend> <img class="del" data-date="1475892511" data-fid="1" data-title="05:08" src="http://dolinasnov.com/uploads/delete.png" alt="del" /> <p>Ещё одно.</p> </fieldset> </div> </div> 

  4. Use this in handlers, and look for elements relative to this element. Since, when adding a handler using addEventListener , inside this handler will point to the element to which the handler was added, when you click on the picture, you can use this instead of a closure. In the case when the handler is added to the fieldset you can find the desired image inside this , for example, using querySelector

    Example

     window.addEventListener('load', function() { var imgs = document.getElementsByClassName('right')[0].getElementsByClassName('del'); for (var i = 0; i < imgs.length; i++) { var img = imgs[i]; var doc = document.getElementById('mes-' + img.dataset.date + '-' + img.dataset.fid); doc.addEventListener('mouseover', function() { this.querySelector('img').style.opacity = 1; }); doc.addEventListener('mouseout', function() { this.querySelector('img').removeAttribute('style'); }); img.setAttribute('title', 'Delete ' + img.dataset.title + ' message'); img.addEventListener('click', function() { if (!isNaN(this.dataset.date) && this.dataset.date > 0 && !isNaN(this.dataset.fid) && this.dataset.fid > 0 && this.dataset.title) alert('Are you sure you want to delete ' + this.dataset.title + ' message?'); }); } }); 
     legend { font-weight: bold; } div.message { width: 400px; } div.message div.right fieldset { text-align: left; position: relative; border-color: #6f6; background-color: #dfd; color: #040; } div.message div.right fieldset.to { background-color: #ddf; } div.message div.right fieldset img.del { width: 16px; top: 0; right: 8px; position: absolute; opacity: 0.3; } 
     <div class="message"> <div class="right"> <fieldset class="from" id="mes-1475888362-1"> <legend>Ilya Indigo&nbsp;<span>03:59</span> </legend> <img class="del" data-date="1475888362" data-fid="1" data-title="03:59" src="http://dolinasnov.com/uploads/delete.png" alt="del" /> <p>Тестовое сообщение <br/>новая строка <br/>ещё одна строка <br/>и ещё одна строка.</p> </fieldset> <fieldset class="from" id="mes-1475888608-1"> <legend>Ilya Indigo&nbsp;<span>04:03</span> </legend> <img class="del" data-date="1475888608" data-fid="1" data-title="04:03" src="http://dolinasnov.com/uploads/delete.png" alt="del" /> <p>Ещё одно.</p> </fieldset> <fieldset class="to" id="mes-1475889423-2"> <legend>Ilya Indigo 2&nbsp;<span>04:17</span> </legend> <img class="del" data-date="1475889423" data-fid="2" data-title="04:17" src="http://dolinasnov.com/uploads/delete.png" alt="del" /> <p>Отвечаю.</p> </fieldset> <fieldset class="from" id="mes-1475892511-1"> <legend>Ilya Indigo&nbsp;<span>05:08</span> </legend> <img class="del" data-date="1475892511" data-fid="1" data-title="05:08" src="http://dolinasnov.com/uploads/delete.png" alt="del" /> <p>Ещё одно.</p> </fieldset> </div> </div> 

  5. Delegation of events instead of a cycle. In this approach, the handler hangs not on a specific image, but on the container where all the pictures lie. Inside the handler is already determined by what element is clicked and the necessary functions are performed.

    Example

     window.addEventListener('load', function() { var right = document.getElementsByClassName('right')[0]; right.addEventListener('click', function(e) { if (e.target.className === 'del') { if (!isNaN(e.target.dataset.date) && e.target.dataset.date > 0 && !isNaN(e.target.dataset.fid) && e.target.dataset.fid > 0 && e.target.dataset.title) alert('Are you sure you want to delete ' + e.target.dataset.title + ' message?'); } }); [...right.querySelectorAll('img')].forEach(img => img.setAttribute('title', 'Delete ' + img.dataset.title + ' message')); var fieldSets = right.querySelectorAll('fieldset'); for (var i = 0; i < fieldSets.length; i++) { fieldSets[i].addEventListener('mouseover', function() { this.querySelector('img').style.opacity = 1; }); fieldSets[i].addEventListener('mouseout', function() { this.querySelector('img').removeAttribute('style'); }); } }); 
     legend { font-weight: bold; } div.message { width: 400px; } div.message div.right fieldset { text-align: left; position: relative; border-color: #6f6; background-color: #dfd; color: #040; } div.message div.right fieldset.to { background-color: #ddf; } div.message div.right fieldset img.del { width: 16px; top: 0; right: 8px; position: absolute; opacity: 0.3; } 
     <div class="message"> <div class="right"> <fieldset class="from" id="mes-1475888362-1"> <legend>Ilya Indigo&nbsp;<span>03:59</span> </legend> <img class="del" data-date="1475888362" data-fid="1" data-title="03:59" src="http://dolinasnov.com/uploads/delete.png" alt="del" /> <p>Тестовое сообщение <br/>новая строка <br/>ещё одна строка <br/>и ещё одна строка.</p> </fieldset> <fieldset class="from" id="mes-1475888608-1"> <legend>Ilya Indigo&nbsp;<span>04:03</span> </legend> <img class="del" data-date="1475888608" data-fid="1" data-title="04:03" src="http://dolinasnov.com/uploads/delete.png" alt="del" /> <p>Ещё одно.</p> </fieldset> <fieldset class="to" id="mes-1475889423-2"> <legend>Ilya Indigo 2&nbsp;<span>04:17</span> </legend> <img class="del" data-date="1475889423" data-fid="2" data-title="04:17" src="http://dolinasnov.com/uploads/delete.png" alt="del" /> <p>Отвечаю.</p> </fieldset> <fieldset class="from" id="mes-1475892511-1"> <legend>Ilya Indigo&nbsp;<span>05:08</span> </legend> <img class="del" data-date="1475892511" data-fid="1" data-title="05:08" src="http://dolinasnov.com/uploads/delete.png" alt="del" /> <p>Ещё одно.</p> </fieldset> </div> </div> 

  • Thank you for your detailed answer, which I have not yet finished reading and have not digested in my head. :-) The solutions are more elegant than the other; I liked most of all with let. What I realized is that I practically do not know JS. As it turned out, to read Flanigan's book (5th edition) from cover to cover, and learn how to iterate over DOM elements, handle events and work with Level 2 Ajax, this does not mean at all that I know JS well. About let and forEach and I did not know. I will continue to read and digest your answer. :-) PS I hope my question is not deleted, otherwise it will be very disappointing. - Ilya Indigo
  • @IlyaIndigo, well, it will not be definitely deleted, because there are descriptive answers, and the question itself is plucked. The maximum can be closed as a duplicate, but now I'm not sure whether it is worth it. - Grundy

The answer is here. https://javascript.ru/basic/closure#primer-oshibochnogo-ispolzovaniya And the introduction of the author "С вопроса "Почему это не работает?" люди обычно начинают изучение замыканий." Simply, as never before, it hits the ball and hits hard!

The working example will be ironed like that.

 window.addEventListener('load',function() { var imgs=document.getElementsByClassName('right')[0].getElementsByClassName('del'); for(var i=0,l=imgs.length;i<l;i++) { var img=imgs[i]; var doc=document.getElementById('mes-'+img.dataset.date+'-'+img.dataset.fid); doc.addEventListener('mouseover',function(x){return function(){x.style.opacity=1;}}(img)); doc.addEventListener('mouseout',function(x){return function(){x.removeAttribute('style');}}(img)); img.setAttribute('title','Delete '+img.dataset.title+' message'); img.addEventListener('click',function(x) { return function() { if(!isNaN(x.dataset.date)&&x.dataset.date>0&&!isNaN(x.dataset.fid)&&x.dataset.fid>0&&x.dataset.title) alert('Are you sure you want to delete '+x.dataset.title+' message?'); } }(img)); } }); 

But still, the ECMA Script 2015 standard allows you to use a simpler and more elegant method, declaring variables not through var , but through let .

 window.addEventListener('load',function() { let imgs=document.getElementsByClassName('right')[0].getElementsByClassName('del'); for(let i=0,l=imgs.length;i<l;i++) { let img=imgs[i]; let doc=document.getElementById('mes-'+img.dataset.date+'-'+img.dataset.fid); doc.addEventListener('mouseover',function(){img.style.opacity=1;}); doc.addEventListener('mouseout',function(){img.removeAttribute('style');}); img.setAttribute('title','Delete '+img.dataset.title+' message'); img.addEventListener('click',function() { if(!isNaN(img.dataset.date)&&img.dataset.date>0&&!isNaN(img.dataset.fid)&&img.dataset.fid>0&&img.dataset.title) alert('Are you sure you want to delete '+img.dataset.title+' message?'); }); } }); 

    В качестве примера могу привести следующий код:

     //функция создает объекты в циле, из интерисующих нас параметров box- контейнер в котором находятся DOM эелементы и plate - сами элементы function createPlates(box, plate, step, speed, bool) { var box = document.querySelector(box); if(!box) { return false; } else { var plates = Array.prototype.slice.apply(box.querySelectorAll(plate)); //в цикле создаем объекты с помощью конструктора (будет описан ниже) for(var i = 0; i < plates.length; i++) { plates[i] = new ServisePlate (plates[i], step, speed, bool); } // на контейнер вешаем обработчик на клик box.addEventListener('click', function(event) { // И перебираем массив plates с созданными объектами plates.forEach(function(element, i) { // если объект события совпадает со ссылкой на DOM элемент, сохраненной в объекте, запускаем его метод. if(event.target == plates[i].elem) { plates[i].disclose(); // Здесь проверка на клик по дочернему элементу необходимого нам элемента }else if(plates[i].elem.contains(event.target)){ for(var j = 0; j < plates.length; j++) { if(plates[j].elem.style.height) { plates[j].disclose(); } } plates[i].disclose(); }else { return false; } }); }); } } createPlates('.servises', '.servises_content', 5, 20, true); 

    Собственно сам конструктор:

     function ServisePlate(plate, step, speed, headSet) { this.elem = plate; this.header = plate.querySelector('h3'); this.body = plate.querySelector('div'); this.link = plate.querySelector('a'); if(this.link) { this.bodySize = this.body.offsetHeight + this.link.offsetHeight; }else{ this.bodySize = this.body.offsetHeight; } this.settings = { step: step, speed: speed } this.initialSize = this.elem.offsetHeight; var self = this; function setHead() { if(29 < self.header.innerHTML.length) { self.header.classList.add('servises_content_header--p18'); } if(59 < self.header.innerHTML.length) { self.header.classList.remove('servises_content_header--p18'); self.header.classList.add('servises_content_header--p1'); } } if(headSet === true) { setHead(); } } 

    И его метод disclose , сохраненный в прототипе:

     ServisePlate.prototype.disclose = function() { function showBody() { if(this.elem.offsetHeight < (this.bodySize + this.initialSize)) { this.elem.style.height = this.elem.offsetHeight + this.settings.step + 'px'; setTimeout(showBody.bind(this), this.settings.speed); } } function hideBody() { if(this.elem.offsetHeight > this.initialSize) { this.elem.style.height = this.elem.offsetHeight - this.settings.step + 'px'; setTimeout(hideBody.bind(this), this.settings.speed); }else { this.elem.style.height = ''; } } if(this.elem.offsetHeight == this.initialSize || this.elem.offsetHeight >= (this.bodySize + this.initialSize)) { if(this.elem.offsetHeight == this.initialSize) { showBody.call(this); } else { hideBody.call(this); } } } 

    Правда некоторые участки кода у меня самого вызывают подозрения на правильность, так как я уже говорил что мой js тянет на 3 с минусом. Но суть не в правильности тех или иных участков, а в подходе в целом.