JavaScript的MVC,的addEventListener不能连接到DOM元素

问题描述 投票:1回答:2

我有这些JavaScript文件。这实在是小的测试项目,以了解MVC是如何使用的JavaScript。当我尝试点击“删除Bloak”按钮,没有任何反应,除了与最后一个按钮,其ID为100的其他按钮没有连接到他们的任何点击事件。为什么?

Controller.js

    var BloakController = function BloakController(model, view) {
    this.model = model;
    this.view = view;
}

BloakController.prototype.init = function init() {
    this.view.onAddBloak = this.addBloak.bind(this);

    this.view.onDeleteBloak = this.deleteBloak.bind(this);

    this.view.onGetAllBloaks = this.getAllBloaks.bind(this);
}

BloakController.prototype.addBloak = function addBloak() {

}

BloakController.prototype.getAllBloaks = function getAllBloaks() {
    this.model.getAllBloaks(this.getAllBloaksList.bind(this));
}

BloakController.prototype.getAllBloaksList = function getAllBloaksList(allBloaksList) {
    for (var i = 0; i < allBloaksList.length; i++) {
        var bloak = allBloaksList[i];
        var bloakDataObjectModel = {
            title: bloak.title,
            body: bloak.body,
            userId: bloak.userId,
            id: bloak.id
        };

        this.view.render(bloakDataObjectModel);
    }
}

BloakController.prototype.deleteBloak = function deleteBloak(id) {
    this.model.deleteBloak(id, this.getAllBloaksList.bind(this));
}

Model.js

var BloakModel = function BloakModel(XMLHttpRequest) {
    this.XMLHttpRequest = XMLHttpRequest;
}

BloakController.prototype.addBloak = function addBloak() {

}

BloakModel.prototype.getAllBloaks = function getAllBloaks(callback) {
    var request = new this.XMLHttpRequest;

    request.onload = function onLoad(e) {
        var ajaxResponse = JSON.parse(e.currentTarget.responseText);
        callback(ajaxResponse);
    }

    request.open('GET', 'https://jsonplaceholder.typicode.com/posts', true);
    request.send();
}

BloakModel.prototype.deleteBloak = function deleteBloak(id, callback) {
    console.log(id);
    var request = new this.XMLHttpRequest;

    request.onload = function onLoad(e) {
        var ajaxResponse = JSON.parse(e.currentTarget.responseText);
        callback(ajaxResponse);
    }

    request.open('DELETE', 'https://jsonplaceholder.typicode.com/posts/' + id, true);
    request.send();
}

View.js

var BloakView = function BloakView(domElement) {
    this.domElement = domElement;

    this.onAddBloak = null;
    this.onDeleteBloak = null;
    this.onGetAllBloaks = null;
}

BloakView.prototype.render = function render(bloakDataObjectModel) {
    this.domElement.innerHTML += '<div class="bloakContainer"><h3>' + bloakDataObjectModel.title + '</h3><p class="bloakContent">' + bloakDataObjectModel.body + '</p><label class="bloakAuthor">' + bloakDataObjectModel.userId + '</label><br /><label class="bloakDate">' + bloakDataObjectModel.id + '</label><br /><input id=' + bloakDataObjectModel.id + ' type="button" value="Delete Bloak" /></div>';

    var addBloakButton = document.getElementById('addBloakButton');

    var deleteBloakButton = document.getElementById(bloakDataObjectModel.id);

    addBloakButton.addEventListener('click', this.onAddBloak);

    deleteBloakButton.addEventListener('click', this.onDeleteBloak);
}

App.js

var bloakModel = new BloakModel(XMLHttpRequest);

var targetElement = document.getElementById('bloaksContainer');
var bloakView = new BloakView(targetElement);

var bloakController = new BloakController(bloakModel, bloakView);

bloakController.init();

bloakController.getAllBloaks();
javascript model-view-controller addeventlistener
2个回答
3
投票

Update

事实证明我完全错了,已经做更多的研究,看起来像使用innerHTML属性将导致所有的子元素的破坏,read more about it here。无论哪种方式,只要仍然有效的解决方案,事实证明,我不知道我自己。

My Brain Fart

我已经意识到我错了最初,如果我是正确的,那么你就会有一个错误,说明你不能附加一个事件监听器nullundefined,是我不好,愚蠢的错误在我的一部分!


Idea

已经很简单看过了你的代码,我认为这是肯定地说,你的渲染功能有点故障,由于使用innerHTML,它不是解析更新的HTML的事件侦听器的工作速度不够快...

已经测试它自己,而刚刚做了以下内容:

setTimeout(() => {
    var addBloakButton = document.getElementById('addBloakButton');
    var deleteBloakButton = document.getElementById(bloakDataObjectModel.id);
    addBloakButton.addEventListener('click', this.onAddBloak);
    deleteBloakButton.addEventListener('click', this.onDeleteBloak);
}, 10);

有效。所以,如果你不想改变太多,你可以做什么,我之前所做的那样,或者,你可以做沿着此线的东西:

  var div = document.createElement("div");
  div.innerHTML = '<div class="bloakContainer"><h3>' + bloakDataObjectModel.title + '</h3><p class="bloakContent">' + bloakDataObjectModel.body + '</p><label class="bloakAuthor">' + bloakDataObjectModel.userId + '</label><br /><label class="bloakDate">' + bloakDataObjectModel.id + '</label><br /><input id=' + bloakDataObjectModel.id + ' type="button" value="Delete Bloak" /></div>';
  this.domElement.append(div);

  var addBloakButton = document.getElementById('addBloakButton');
  var deleteBloakButton = div.querySelector('input[id*="' + bloakDataObjectModel.id + '"]');
  addBloakButton.addEventListener('click', this.onAddBloak);
  deleteBloakButton.addEventListener('click', this.onDeleteBloak);

此外,我会做一个更坚实可靠的办法是实现视图内的onRender功能,本质上阐明,一次且仅一次有关HTML的所有已经呈现,随后击发onRender方法。或者,也许像view.dispatchEvents,等...具体这个例子中,我会在控制器内运行这样的功能,所以一旦getAllBloaksList已使所有的相关数据,就可以触发一个dispatchEvents功能,就像这样:

// Within the controller part... 
BloakController.prototype.getAllBloaksList = function getAllBloaksList(allBloaksList) {
  for (var i = 0; i < allBloaksList.length; i++) {
    var bloak = allBloaksList[i];
    var bloakDataObjectModel = {
      title: bloak.title,
      body: bloak.body,
      userId: bloak.userId,
      id: bloak.id
    };

    this.view.render(bloakDataObjectModel);
  }

  this.view.dispatchEvents();
};


// .. Within the View part...
BloakView.prototype.dispatchEvents = function () {
  var list = this.domElement.querySelectorAll(".bloakContainer");
  var todo = this.onDeleteBloak;

  list.forEach(function (div) {
    var deleteButton = div.querySelector("input[type=button]");

    deleteButton.onclick =  function () {
      todo(this.id);
    };
  });
};

演示

// Controller.
var BloakController = function BloakController(model, view) {
  this.model = model;
  this.view = view;
};

BloakController.prototype.init = function init() {
  this.view.onAddBloak = this.addBloak.bind(this);
  this.view.onDeleteBloak = this.deleteBloak.bind(this);
  this.view.onGetAllBloaks = this.getAllBloaks.bind(this);
};

BloakController.prototype.addBloak = function addBloak() {
  // todo
};

BloakController.prototype.getAllBloaks = function getAllBloaks() {
  this.model.getAllBloaks(this.getAllBloaksList.bind(this));
};

BloakController.prototype.getAllBloaksList = function getAllBloaksList(allBloaksList) {
  for (var i = 0; i < allBloaksList.length; i++) {
    var bloak = allBloaksList[i];
    var bloakDataObjectModel = {
      title: bloak.title,
      body: bloak.body,
      userId: bloak.userId,
      id: bloak.id
    };
    this.view.render(bloakDataObjectModel);
  }
  this.view.dispatchEvents();
};

BloakController.prototype.deleteBloak = function deleteBloak(id) {
  this.model.deleteBloak(id, this.getAllBloaksList.bind(this));
};


// Model.
var BloakModel = function BloakModel(XMLHttpRequest) {
  this.XMLHttpRequest = XMLHttpRequest;
};

BloakController.prototype.addBloak = function addBloak() {
  // todo
};

BloakModel.prototype.getAllBloaks = function getAllBloaks(callback) {
  var request = new this.XMLHttpRequest;
  request.onload = function onLoad(e) {
    var ajaxResponse = JSON.parse(e.currentTarget.responseText);
    callback(ajaxResponse);
  }
  request.open('GET', 'https://jsonplaceholder.typicode.com/posts', true);
  request.send();
};

BloakModel.prototype.deleteBloak = function deleteBloak(id, callback) {
  console.log(id);
  var request = new this.XMLHttpRequest;
  request.onload = function onLoad(e) {
    var ajaxResponse = JSON.parse(e.currentTarget.responseText);
    callback(ajaxResponse);
  }
  request.open('DELETE', 'https://jsonplaceholder.typicode.com/posts/' + id, true);
  request.send();
};


// View. 
var BloakView = function BloakView(domElement) {
  this.domElement = domElement;
  this.onAddBloak = null;
  this.onDeleteBloak = null;
  this.onGetAllBloaks = null;
};

BloakView.prototype.render = function render(bloakDataObjectModel) {
  this.domElement.innerHTML += '<div class="bloakContainer"><h3>' + bloakDataObjectModel.title + '</h3><p class="bloakContent">' + bloakDataObjectModel.body + '</p><label class="bloakAuthor">' + bloakDataObjectModel.userId + '</label><br /><label class="bloakDate">' + bloakDataObjectModel.id + '</label><br /><input id=' + bloakDataObjectModel.id + ' type="button" value="Delete Bloak" /></div>';
}

BloakView.prototype.dispatchEvents = function() {
  var list = this.domElement.querySelectorAll(".bloakContainer");
  var todo = this.onDeleteBloak;
  var addBloakButton = document.getElementById('addBloakButton');
  addBloakButton.addEventListener('click', this.onAddBloak);

  list.forEach(function(div) {
    var deleteButton = div.querySelector("input[type=button]");
    deleteButton.onclick = function() {
      todo(this.id);
    };
  });
};


// App.
var bloakModel = new BloakModel(XMLHttpRequest);
var targetElement = document.getElementById('bloaksContainer');
var bloakView = new BloakView(targetElement);
var bloakController = new BloakController(bloakModel, bloakView);

bloakController.init();
bloakController.getAllBloaks();
<button id="addBloakButton">Add</button>
<div id="bloaksContainer">
  <!-- DHTML -->
</div>

1
投票

代码this.domElement.innerHTML += ...;被incorrect.You使用的是+ =操作这增加了一些与本身,然后分配新的值。

新价值是现有的innerHTML和新的HTML的结果。因此,新的价值[HTML]不会有先前添加的事件处理程序,将刚才复制的HTML。

您可以使用appendChild方法来添加新的元素。

此外,你应该避免使用具有的innerHTML的for-loop因为它会不断更新DOM,你可能会看到屏幕上闪烁。如果必须使用innerHTML,你应该

  1. 创建整个HTML [innerHTML的]在一起,在一次注入,
  2. 然后使用事件委托添加事件处理程序。

事件代表团将事件绑定到一个顶层容器,这样你就不需要担心事件侦听器连接到每个项目的技术。

你可以阅读更多的DOM here

© www.soinside.com 2019 - 2024. All rights reserved.