2

I have a modal object that has various functions as its properties, but when I click to open a modal I'm getting the error this.openModal is not a function. I've tried debugging but it just falls over as soon as it hits the function I want it to run. I suspect it might be an issue with the 'this' binding, but I'm not sure where the problem is. All my JS is below, one function kind of chains onto another so it's easier to see the whole object.

var modal = function () {

  // Modal Close Listeners
  this.closeModalListen = function(button) {
    var modalFooter = button.parentElement;
    var modalContent = modalFooter.parentElement;
    var modalElement = modalContent.parentElement;
    var backdrop = document.getElementById("modal-backdrop");
    this.closeModal(modalElement, backdrop);
  }

  // Open Modal
  this.openModal = function(button) {
    var button = button;
    var modal = button.getAttribute("data-modal");
    var modalElement = document.getElementById(modal);
    var backdrop = document.createElement('div');
    backdrop.id="modal-backdrop";
    backdrop.classList.add("modal-backdrop");
    document.body.appendChild(backdrop);
    var backdrop = document.getElementById("modal-backdrop");
    backdrop.className += " modal-open";
    modalElement.className += " modal-open";
  }

  // Close Modal
  this.closeModal = function(modalElement, backdrop) {
    modalElement.className = modalElement.className.replace(/\bmodal-open\b/, '');
    backdrop.className = backdrop.className.replace(/\bmodal-open\b/, '');
  }

  // Initialise Function
  this.init = function () {
    // Create Events for creating the modals
    if (document.addEventListener) {
        document.addEventListener("click", this.handleClick, false);
    }
    else if (document.attachEvent) {
        document.attachEvent("onclick", this.handleClick);
    }
  }

  // Handle Button Click
  this.handleClick = function(event) {
    var thisModal = this;
      event = event || window.event;
      event.target = event.target || event.srcElement;
      var element = event.target;
      while (element) {
          if (element.nodeName === "BUTTON" && /akela/.test(element.className)) {
              thisModal.openModal(element);
              break;
          } else if (element.nodeName === "BUTTON" && /close/.test(element.className)) {
              thisModal.closeModalListen(element);
              break;
          } else if (element.nodeName === "DIV" && /close/.test(element.className)) {
              thisModal.closeModalListen(element);
              break;
          }
          element = element.parentNode;
      }
  }
}

// Initalise Modal Engine
var akelaModel = new modal();
akelaModel.init();

I'm looking to fix this with pure js and no jquery.

Web Develop Wolf
  • 5,996
  • 12
  • 52
  • 101

2 Answers2

4

You need to bind the function to the object before giving it as a event listener:

document.addEventListener("click", this.handleClick.bind(this), false);

document.attachEvent("onclick", this.handleClick.bind(this));

Otherwise it becomes detached from its original object and doesn't have the expected this.

Dan D.
  • 73,243
  • 15
  • 104
  • 123
1

As an alternative to using bind, you can move:

var thisModal = this;

from the handleClick method to the constructor.

Also, it's convention to give constructors names starting with a capital letter so it's easier to see that they're constructors.

var Modal = function () {

  var thisModal = this;

  // Open Modal
  this.openModal = function(button) {
    console.log('openModal called on ' + button.tagName);
  }

  // Initialise Function
  this.init = function () {
    // Create Events for creating the modals
    if (document.addEventListener) {
        document.addEventListener("click", this.handleClick, false);
    }
  }

  // Handle Button Click
  this.handleClick = function(event) {
    thisModal.openModal(event.target);
  }
}

// Initalise Modal Engine
var akelaModel = new Modal();
akelaModel.init();
<div>Click <span>anywhere</span></div>
RobG
  • 142,382
  • 31
  • 172
  • 209