添加事件侦听器后,函数运行次数过多

use*_*206 6 javascript

我已经构建了一个简单的演示(https://codepen.io/anon/pen/VgKQoq),单击按钮时创建的元素和对象:它创建一个元素,然后将该元素的对象推入'对象'阵列.单击"删除"按钮时,将使用ID成功删除元素和对象.

但是,问题是每次删除一个元素时,remove函数有时会运行太多次,具体取决于单击的元素,我不知道为什么.在演示中,打开javascript控制台,创建例如4个元素,然后通过单击remove删除第3个元素,您将看到会发生什么.

有谁知道为什么会这样?我认为这可能是因为事件监听器一次又一次地添加到相同的元素,但它在删除时似乎不起作用.这里有任何解释和最佳实践,谢谢.

var id = 0, objects = [], removes;

function createEntry() {
	id++;

	// create new element, append to #container & create new object
	var container = document.querySelector('#container'),
	    newEntry = document.createElement("div"),
	    title = 'title text here',
	    description = 'description text here',
	    remove = 'remove',
	    dataId = id,
	    obj = new Entry(title, description, remove);
	newEntry.classList.add("entry");
	newEntry.innerHTML = '<span class="title">' + title + '</span><span class="description">' + description + '</span><span class="remove">' + remove + '</span>';
	container.appendChild(newEntry);
	newEntry.setAttribute('data-id', id);

	updateElements();

	// constructor & push into array
	function Entry(title, description, remove) {
		this.title = title;
		this.description = description;
		this.remove = remove;
		this.id = id;

		objects.push(this);
	}

	// tests
	console.log('JSON.stringify(obj): ' + JSON.stringify(obj));
	console.log('obj.id: ' + obj.id);

	function updateElements() {
		removes = document.querySelectorAll(".remove");
		listenForRemoves();

		function listenForRemoves() {
			for (let remove of removes) {
				remove.removeEventListener("click", removeElements);
				remove.addEventListener("click", removeElements);
			}
		}

		function removeElements() {
			let removedId = this.parentNode.getAttribute('data-id'),
			    objToRemove = objects.find(obj => obj.id == removedId); // not used

			this.parentNode.remove(); console.log('removed id ' + removedId);
				console.log('objects before: '); for (let object of objects) { console.log(JSON.stringify(object))};
				objects = objects.filter(obj => obj.id != removedId); // doesn't use objToRemove
				console.log('objects now: '); for (let object of objects) { console.log(JSON.stringify(object))};
		}
	}
	// works but why the repeating console logs twice?
}
Run Code Online (Sandbox Code Playgroud)
button { display: block }
.entry {
	width: 100%;
	display: block;
	padding: 10px;
	border: 1px solid #f5f5f5;
}
span {
  display: block;
  width: 100%;
}
section { background: lightgreen }
Run Code Online (Sandbox Code Playgroud)
<button id='btn' onclick='createEntry()'>Create</button>
<section id='container'></section>
Run Code Online (Sandbox Code Playgroud)

更新:还有其他想法吗?我添加了remove.removeEventListener("click", removeElements);哪些现在摆脱了很多重复,但它现在仍然只控制台日志两次(好吧......有时!?).上面更新了新的codepen链接

小智 1

我不确定到底发生了什么,但所有这些函数都嵌套在 createEntry 函数内。尝试将它们移到该函数之外。这似乎解决了我的测试中的问题:

var id = 0, objects = [], removes;

function createEntry() {
    id++;

    // create new element, append to #container & create new object
    var container = document.querySelector('#container'),
        newEntry = document.createElement("div"),
        title = 'title text here',
        description = 'description text here',
        remove = 'remove',
        dataId = id,
        obj = new Entry(title, description, remove);
    newEntry.classList.add("entry");
    newEntry.innerHTML = '<span class="title">' + title + '</span><span class="description">' + description + '</span><span class="remove">' + remove + '</span>';
    container.appendChild(newEntry);
    newEntry.setAttribute('data-id', id);

    updateElements();

    // constructor & push into array
    function Entry(title, description, remove) {
        this.title = title;
        this.description = description;
        this.remove = remove;
        this.id = id;

        objects.push(this);
    }

    // tests
    console.log('JSON.stringify(obj): ' + JSON.stringify(obj));
    console.log('obj.id: ' + obj.id);
}

function updateElements() {
  removes = document.querySelectorAll(".remove");
  listenForRemoves();

  function listenForRemoves() {
    for (let remove of removes) {
      remove.removeEventListener("click", removeElements);
      remove.addEventListener("click", removeElements);
    }
  }


}
function removeElements(e) {
  let removedId = this.parentNode.getAttribute('data-id'),
      objToRemove = objects.find(obj => obj.id == removedId); // not used

  this.parentNode.remove(); console.log('removed id ' + removedId);
    console.log('objects before: '); for (let object of objects) { console.log(JSON.stringify(object) + " " + e.target)};
    objects = objects.filter(obj => obj.id != removedId); // doesn't use objToRemove
    console.log('objects now: '); for (let object of objects) { console.log(JSON.stringify(object))};
}
Run Code Online (Sandbox Code Playgroud)