2017-07-11 18 views
0

いくつかのdivを動的に作成したいと思います。各divがオブジェクトJS - 動的に作成されたdivコンテナに間違ったデータオブジェクトが含まれています

function node(id, title, content, isPrivate, dateOfCreation) { // my node object 
    this.id = id; 
    this.title = title; 
    this.content = content; 
    this.isPrivate = isPrivate; 
    this.dateOfCreation = dateOfCreation; 
    this.lastEdited = dateOfCreation; 
} 

が含まれており、このオブジェクトは

var store = new dataStore(); // instance of the store 

    function dataStore() { 
     this.nodes = []; // all the node objects 

     this.getNodes = function() { // get all objects 
     return this.nodes; 
     } 

    this.addNode = function(node) { // add a new object 
    addElementToArray(this.nodes, node); 
    } 

    this.deleteNode = function(node) { // delete an object 
    deleteElementFromArray(this.nodes, node) 
    } 

     this.getNodeById = function(nodeId){ // find an object by its id 
     for(var i = 0; i < this.nodes.length; i++){ 
      if(nodeId == this.nodes[i].id) 
       return this.nodes[i]; 
     } 
     return null; 
     } 

     this.getNodeTitle = function(node){ // get the title of the node 
     return node.title; 
     } 
    } 

は、だから私はいくつかのdivを、オブジェクトごとに1つのdiv要素を作成したい私のデータストアクラスによって管理されています。

function initNodeView() { // build up all the divs 

    for (var i = 0; i < 30; i++) { // -- TEST -- Fill the store 
     store.addNode(new node(i, i, i, i % 2 == 0, i)); 
    } 

    var nodes = store.getNodes(); // get all the nodes from the store 

    for (var i = 0; i < nodes.length; i++) { 
    var currentNode = nodes[i]; // the current object 

    var nodeContainer = createDiv('#nodeList', "nodeContainer"); // the wrapperdiv 

    createDivWithText(nodeContainer, "nodeContentDiv", store.getNodeTitle(currentNode)); // create a child div with the title of the object 

    var barContainer = createDiv(nodeContainer, "nodeBtnBarDiv"); // create a childdiv - the container of all buttons 

    createDivWithIcon(barContainer, "nodeIcon", currentNode.isPrivate ? "'foo.png'" : "'bar.png'"); // create an icon div 

    var btnDefaultCss = "nodeBtn"; // default css class for buttons 

    createBtn(barContainer, btnDefaultCss, "Edit", function() { 
     // empty .. 
    }); 

    createBtn(barContainer, btnDefaultCss, "Delete", function() { 
     nodeContainer.remove(); // remove this container from the DOM 
     store.deleteNode(currentNode); // delete this object from the store 
    }); 
    } 
} 

私のコードをリファクタリングするために、いくつかのヘルパ関数を構築しました。

function createDiv(parent, cssClass){ // create default div 
    var divElement = $("<div></div>"); 
    divElement.addClass(cssClass); 
    $(parent).append(divElement); 
    return divElement; 
} 

function createDivWithText(parent, cssClass, text){ // create div with text 
    var divElement = createDiv(parent, cssClass); 
    divElement.html(text); 
    return divElement; 
} 

function createDivWithIcon(parent, cssClass, iconSource){ // create icon div 
    var divElement = createDiv(parent, cssClass); 
    var icon = $("<img src='"+ iconSource +"'/>"); 
    divElement.append(icon); 
    return divElement; 
} 

function createBtn(parent, cssClass, text, fn){ // create a new button 
    var btn = $("<button></button>"); 
    btn.addClass(cssClass); 
    btn.html(text); 
    btn.click(function() { 
     fn(); 
    }); 
    $(parent).append(btn); 
} 

私の問題は、initNodeView()を呼び出すとき、divコンテナがうまく構築されていることです。しかし、ボタン(編集または削除)をクリックしてこのコンテナの保存されたオブジェクトをログに記録するときは、そのオブジェクトには常に最後のオブジェクトが保存されます。

したがって、30個のオブジェクトと30個のdivコンテナを作成すると、すべて30番目のオブジェクトが格納されます。

通常div要素1は、対象物1、ディビジョン2のオブジェクト2を持つ必要があります....

+2

が重複する可能性のを使用することができます[JavaScriptのクロージャは内部ループ - 簡単な実用例](https://stackoverflow.com/questions/750486/javascript- closure-inside-loops-simple-practical-example) – Andreas

+0

'addElementToArray'の内容を共有できますか?また、[MVCE](https://stackoverflow.com/help/mcve)を提供するために問題を引き起こさない関数を取り除くことは可能でしょうか? –

+2

@MaazSyedAdeeb 'addElementToArray'は(おそらく' Array.push'のような)単なる例です。問題は関数のスコープで、ちょうど95のように答えました。 @ Question3rクラス名はJS規約で大文字になります。https://stackoverflow.com/a/5640506/4621141 – flen

答えて

1

問題は、JavaScriptが関数スコープの概念を使用していることです。これは、関数内で宣言されたすべての変数が関数に対してローカルであることを意味しますが、中カッコの現在のブロックに対してローカルであるとは限りません。したがって、すべての反復で新しい値が割り当てられる変数は1つだけです(currentNode)。しかし、あなたのイベントハンドラでは、最終変数が最後のノードである同じ変数を参照します。

for (var i = 0; i < nodes.length; i++) { 
    var currentNode = nodes[i]; 
    // ... 
} 
// currentNode == nodes[nodes.length - 1] 

この問題を解決する方法はたくさんあります。 for -loopの本体を匿名関数の中にラップし、すぐに呼び出すことで、JavaScriptが各繰り返しに対して新しいスコープを作成するように強制できます。

for (var i = 0; i < nodes.length; i++) { 
    (function(currentNode) { 
    // ... 
    })(nodes[i]); 
} 

あるいはbindイベントハンドラの引数にcurrentNodeの値。

for (var i = 0; i < nodes.length; i++) { 
    var currentNode = nodes[i]; 
    // ... 
    createBtn(barContainer, btnDefaultCss, "Edit", (function(currentNode) { 
    // empty .. 
    }).bind(null, currentNode)); 
} 

は、将来のプロジェクトのためには、使用にECMAScript 6によって導入letキーワードを考えるかもしれません。 varの代わりにletと宣言された変数は、期待どおりのスコープになります。

for (let i = 0; i < nodes.length; i++) { 
    let currentNode = nodes[i]; 
    // ... 
} 
// currentNode is not defined 

はJavaScriptのこの機能は、ほとんどすべての主要なブラウザで実装されてcaniuse.comによります。ただし、一貫性を保つために、letvarを混ぜて使用しないでください。

0

だから、私はちょうどここにいくつかの作業フィドルを残すだろう、@ just95も非常によく答えた。

最初のものは、スコープを残すために別々の機能と連携している:

for(var i = 0; i < 5; i++){ 
 
    var btn = $("<button></button>"); 
 
    btn.html(i); 
 
    addClickEvent(btn, i); 
 
    $('.container').append(btn) 
 
} 
 

 
function addClickEvent(btn, val){ 
 
    btn.click(function(){ 
 
     alert(val); 
 
    }); 
 
};
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 
 

 
<div class="container"> 
 

 
</div>

第二の方法は、キーワードを使用している代わりに、「VAR」の

「しましょう」

for(let i = 0; i < 5; i++){ 
 
\t var btn = $("<button></button>"); 
 
    btn.html(i); 
 
    btn.click(function(){ 
 
    \t alert(i); 
 
    }); 
 
\t $('.container').append(btn) 
 
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 
 

 
<div class="container"> 
 

 
</div>

最後のものがjquery関数eachを使用して(私の意見では)ベストかもしれません。ネイティブJavaScriptを使用している場合、あなたがforEach()機能

var arr = ["one", "two", "three"]; 
 

 
$(arr).each(function(index, element){ 
 
\t var btn = $("<button></button>"); 
 
    btn.html(element); 
 
    btn.click(function(){ 
 
    \t alert(index + 1); 
 
    }); 
 
\t $('.container').append(btn) 
 
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 
 

 
<div class="container"> 
 

 
</div>

関連する問題