【问题标题】:Javascript MVC, addEventListener cannot be attached to dom elementJavascript MVC,addEventListener 不能附加到 dom 元素
【发布时间】:2019-06-28 10:47:15
【问题描述】:

我有这些 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


    【解决方案1】:

    更新

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

    我的脑袋放屁

    我应该意识到我最初是错的,如果我是对的,那么你会收到一个错误,指出你不能将事件侦听器附加到 nullundefined,这是我的错误,愚蠢的错误我这边!


    想法

    非常简短地查看了您的代码,我认为可以肯定地说您的渲染函数有些错误,因为使用了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 函数,基本上说明onceonly once 所有相关的 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>

    【讨论】:

    • 感谢您的宝贵时间。这真的帮助了我:) 我没想到会出现这种错误:/ 这对我来说真的很难发现,我花了很多时间来解决这个问题:/
    • 这是我的荣幸!在艰难地学习了 JavaScript 之后,我接触到了各种与此类似的错误。同样由于副作用,这就是为什么我个人开始使用更实用的方法来实现我的 JavaScript 代码......抱歉简短的回复,我目前正在外出,我正在手机上输入这个触摸屏有点坏,哈哈。
    • 再次感谢您,很高兴知道在使用innerHTML 时有一种叫做“销毁所有子元素”的东西。 :)
    • 这个答案也解释了一点,我猜是有道理的,请参阅接受的答案,stackoverflow.com/questions/5113105/…
    • @oakar 这可能是我能想到的最简洁的解释方式! :)
    【解决方案2】:

    this.domElement.innerHTML += ...; 代码不正确。您使用的是 += 运算符,它会自己添加一些内容,然后分配新值。

    新值是现有innerHTML 和新HTML 的结果。所以新值 [HTML] 不会有之前添加的事件处理程序,它只会复制 HTML。

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

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

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

    事件委托是一种将事件绑定到顶级容器的技术,因此您无需担心将事件侦听器附加到每个项目。

    您可以在 DOM here 上阅读更多内容。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-12-05
      • 1970-01-01
      • 2014-07-15
      • 2014-06-06
      • 2014-10-05
      • 1970-01-01
      • 2020-12-04
      • 2014-03-10
      相关资源
      最近更新 更多