17 of 17 menu

The Error of Duplicating the DOM Element Editing Function in JavaScript

Let's say we have a list:

<ul> <li>1</li> <li>2</li> <li>3</li> </ul>

Let's get the list itself and its items into separate variables:

let ul = document.querySelector('ul'); let lis = document.querySelectorAll('li');

Let's make it so that the items of our list can be edited using an appearing input:

for (let li of lis) { li.addEventListener('click', function func() { let input = document.createElement('input'); input.value = li.textContent; li.textContent = ''; li.append(input); input.addEventListener('blur', function() { li.textContent = this.value; li.addEventListener('click', func); }); li.removeEventListener('click', func); }); }

Now let's say we want to be able to add new items to the list. Let there be a corresponding input for this below the list:

<input id="adder">

Let's get a reference to this input into a variable:

let adder = document.querySelector('#adder');

Let's make it so that when the input loses focus, a new item with the text taken from our input is added to the list:

adder.addEventListener('blur', function() { let li = document.createElement('li'); li.textContent = this.value; ul.append(li); });

Now let's say we want the newly added items to also be editable. By itself, editing won't work for them, because when we attached the click handler to the list items, these items didn't exist yet.

Let's look at the possible solutions to this problem.

First Solution

The simplest solution is to duplicate the code of the func function, attaching it for the newly created items as well:

adder.addEventListener('blur', function() { let li = document.createElement('li'); li.textContent = this.value; li.addEventListener('click', function func() { // here we duplicate the code }); ul.append(li); });

Of course, in this solution we immediately see a drawback - duplicating code is not correct.

Second Solution

To solve the duplication problem, it is logical to take the func function out, making it a Function Declaration:

function func() { let input = document.createElement('input'); input.value = li.textContent; li.textContent = ''; li.append(input); input.addEventListener('blur', function() { li.textContent = this.value; li.addEventListener('click', func); }); li.removeEventListener('click', func); }

This is where the problem awaits us. The fact is that our function used the variable li, obtained from the outer scope. But after taking the function out, this variable is now not visible!

To solve the problem, we will pass our li as a parameter:

function func(li) { let input = document.createElement('input'); input.value = li.textContent; li.textContent = ''; li.append(input); input.addEventListener('blur', function() { li.textContent = this.value; li.addEventListener('click', func); }); li.removeEventListener('click', func); }

And here our solution creates another problem. The fact is that you cannot simply pass a parameter to an event handler:

for (let li of lis) { li.addEventListener('click', func(li)); // doesn't work! }

To solve this problem, let's simply call our function inside an anonymous handler:

for (let li of lis) { li.addEventListener('click', function() { func(li); }); }

And let's do the same when creating a new list item:

adder.addEventListener('blur', function() { let li = document.createElement('li'); li.textContent = this.value; li.addEventListener('click', function() { func(li); }); ul.append(li); });

Third Solution

There is a more elegant solution. You can simply use event delegation. In this case, the problem with new list items simply won't arise:

ul.addEventListener('click', function(event) { if (event.target.tagName === 'LI') { // catching the click specifically on li, not on the input let li = event.target; let input = document.createElement('input'); input.value = li.textContent; li.textContent = ''; li.append(input); input.addEventListener('blur', function() { li.textContent = this.value; }); } });

In this case, the loop through the list items will not be needed at all, and the code for creating a new list item will be reduced to this:

adder.addEventListener('blur', function() { let li = document.createElement('li'); li.textContent = this.value; ul.append(li); });
English
AfrikaansAzərbaycanБългарскиবাংলাБеларускаяČeštinaDanskDeutschΕλληνικάEspañolEestiSuomiFrançaisहिन्दीMagyarՀայերենIndonesiaItaliano日本語ქართულიҚазақ한국어КыргызчаLietuviųLatviešuМакедонскиMelayuမြန်မာNederlandsNorskPolskiPortuguêsRomânăРусскийසිංහලSlovenčinaSlovenščinaShqipСрпскиSrpskiSvenskaKiswahiliТоҷикӣไทยTürkmenTürkçeЎзбекOʻzbekTiếng Việt
We use cookies for website operation, analytics, and personalization. Data processing is carried out in accordance with the Privacy Policy.
accept all customize decline