17 of 17 menu

JavaScriptにおけるDOM要素編集関数の重複エラー

いくつかのリストがあるとします:

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

リスト自体とその項目を別々の変数に取得しましょう:

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

リストの項目を、表示される入力フィールドで編集できるようにしましょう:

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); }); }

次に、リストに新しい項目を追加できるようにしたいとします。 そのために、リストの下に対応する入力フィールドを置きましょう:

<input id="adder">

この入力フィールドへの参照を変数に取得します:

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

入力フィールドがフォーカスを失ったときに、 入力フィールドから取ったテキストで 新しい項目がリストに追加されるようにしましょう:

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

次に、新しく追加された項目も編集できるようにしたいとします。 これらに対しては、編集機能自体は自動的には機能しません。 なぜなら、リスト項目へのクリックハンドラを設定したとき、 これらの項目はまだ存在していなかったからです。

この問題の解決策の選択肢を見ていきましょう。

解決策 その1

最も単純な解決策は、新しく作成された項目に対しても 関数 func をバインドして、 コードを複製することです:

adder.addEventListener('blur', function() { let li = document.createElement('li'); li.textContent = this.value; li.addEventListener('click', function func() { // ここでコードを複製します }); ul.append(li); });

もちろん、この解決策には欠点がすぐにわかります。 コードを複製するのは正しくありません。

解決策 その2

重複を解消するためには、関数 func を外に出して、 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); }

ここで問題が待ち受けています。 この関数は、外側のスコープから取得した 変数 li を使用していたという事実です。 しかし、関数を外に出した後では、 この変数はもう見えません!

この問題を解決するために、 li を パラメータとして渡すようにします:

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); }

そして、この解決策はさらに別の問題を引き起こします。 イベントハンドラにパラメータを単純に渡すことはできないという事実です:

for (let li of lis) { li.addEventListener('click', func(li)); // 動作しません! }

この問題を解決するために、 無名ハンドラ内で関数を呼び出すようにしましょう:

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

そして、新しいリスト項目を作成する際にも 同じように対応します:

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

解決策 その3

よりエレガントな解決策が存在します。 単にイベント委譲を利用すればよいのです。 この場合、新しいリスト項目の問題はそもそも発生しません:

ul.addEventListener('click', function(event) { if (event.target.tagName === 'LI') { // li へのクリックだけをキャッチ、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; }); } });

この場合、リスト項目に対するループはそもそも不要になり、 新しいリスト項目を作成するコードは以下のように短縮できます:

adder.addEventListener('blur', function() { let li = document.createElement('li'); li.textContent = this.value; ul.append(li); });
日本語
AfrikaansAzərbaycanБългарскиবাংলাБеларускаяČeštinaDanskDeutschΕλληνικάEnglishEspañolEestiSuomiFrançaisहिन्दीMagyarՀայերենIndonesiaItalianoქართულიҚазақ한국어КыргызчаLietuviųLatviešuМакедонскиMelayuမြန်မာNederlandsNorskPolskiPortuguêsRomânăРусскийසිංහලSlovenčinaSlovenščinaShqipСрпскиSrpskiSvenskaKiswahiliТоҷикӣไทยTürkmenTürkçeЎзбекOʻzbekTiếng Việt
当サイトでは、サイトの動作、分析、パーソナライゼーションのためにクッキーを使用しています。 データ処理はプライバシーポリシーに従って行われます。
すべて受け入れる 設定 拒否