Add click event for javascript inserted element

If I click on the first “Change”, I will receive console.log('click happend'). But if I add one of these fields through javascript (click "Add Field"), then Edit clickfrom this new window will not work. I know this because javascript is launched when there was no element and why there is no click event listener. I also know that with jQuery I could do this:

$('body').on('click', '.edit', function(){ // do whatever };

and it will work.

But how can I do this with simple Javascript? I could not find a useful resource. Created a simple example that I would like to work on. What is the best way to solve this problem?

So, the problem is this: if you add a field and then click "Edit", nothing will happen.

var XXX = {};
XXX.ClickMe = function(element){
    this.element = element;
    
    onClick = function() {
        console.log('click happend');
    };
    
    this.element.addEventListener('click', onClick.bind(this));
};

[...document.querySelectorAll('.edit')].forEach(
    function (element, index) {
        new XXX.ClickMe(element);
    }
);


XXX.PrototypeTemplate = function(element) {
    this.element = element;
    var tmpl = this.element.getAttribute('data-prototype');

    addBox = function() {
        this.element.insertAdjacentHTML('beforebegin', tmpl);
    };

    this.element.addEventListener('click', addBox.bind(this));
};


[...document.querySelectorAll('[data-prototype]')].forEach(
    function (element, index) {
        new XXX.PrototypeTemplate(element);
    }
);
[data-prototype] {
  cursor: pointer;
}
<div class="box"><a class="edit" href="#">Edit</a></div>

<span data-prototype="<div class=&quot;box&quot;><a class=&quot;edit&quot; href=&quot;#&quot;>Edit</a></div>">Add box</span>
Hide result

JSFiddle

Q/A - , , . eventListener (s) new XXX.ClickMe(element); , DOM?

+6
4

, $('body').on('click', '.edit', function () { ... }):

document.body.addEventListener('click', function (event) {
  if (event.target.classList.contains('edit')) {
    ...
  }
})

( ):

var XXX = {
  refs: new WeakMap(),
  ClickMe: class {
    static get (element) {
      // if no instance created
      if (!XXX.refs.has(element)) {
        console.log('created instance')
        // create instance
        XXX.refs.set(element, new XXX.ClickMe(element))
      } else {
        console.log('using cached instance')
      }
      
      // return weakly referenced instance
      return XXX.refs.get(element)
    }

    constructor (element) {
      this.element = element
    }
    
    onClick (event) {
      console.log('click happened')
    }
  },
  PrototypeTemplate: class {
    constructor (element) {
      this.element = element
      
      var templateSelector = this.element.getAttribute('data-template')
      var templateElement = document.querySelector(templateSelector)
      // use .content.clone() to access copy fragment inside of <template>
      // using template API properly, but .innerHTML would be more compatible
      this.template = templateElement.innerHTML
      
      this.element.addEventListener('click', this.addBox.bind(this))
    }
    
    addBox () {
      this.element.insertAdjacentHTML('beforeBegin', this.template, this.element)
    }
  }
}

Array.from(document.querySelectorAll('[data-template]')).forEach(function (element) {
  // just insert the first one here
  new XXX.PrototypeTemplate(element).addBox()
})

// event delegation instead of individual ClickMe() event listeners
document.body.addEventListener('click', function (event) {
  if (event.target.classList.contains('edit')) {
    console.log('delegated click')
    // get ClickMe() instance for element, and create one if necessary
    // then call its onClick() method using delegation
    XXX.ClickMe.get(event.target).onClick(event)
  }
})
[data-template] {
  cursor: pointer;
}

/* compatibility */
template {
  display: none;
}
<span data-template="#box-template">Add box</span>

<template id="box-template">
  <div class="box">
    <a class="edit" href="#">Edit</a>
  </div>
</template>
Hide result

WeakMap() ClickMe(), .edit, ClickMe.get(element).

ClickMe() , DOM .

+2

- ...

document.addEventListener('click',function(e){
    if(e.target && e.target.className.split(" ")[0]== 'edit'){
     new XXX.ClickMe(e.target);}
 })

var XXX = {};
XXX.ClickMe = function(element) {
  this.element = element;


  this.element.addEventListener('click', onClick.bind(this));
};



XXX.PrototypeTemplate = function(element) {
  this.element = element;
  var tmpl = this.element.getAttribute('data-prototype');

  addBox = function() {
    this.element.insertAdjacentHTML('beforebegin', tmpl);
  };

  this.element.addEventListener('click', addBox.bind(this));
};


[...document.querySelectorAll('[data-prototype]')].forEach(
  function(element, index) {
    new XXX.PrototypeTemplate(element);
  }
);


document.addEventListener('click', function(e) {
  if (e.target && e.target.className.split(" ")[0] == 'edit') {
    console.log('click happend');
  }
})
[data-prototype] {
  cursor: pointer;
}
<div class="box"><a class="edit" href="#">Edit</a></div>

<span data-prototype="<div class=&quot;box&quot;><a class=&quot;edit&quot; href=&quot;#&quot;>Edit</a></div>">Add box</span>
Hide result
+2

jQuery: , . document.body :

document.body.addEventListener('click', e => {
  if (e.target.matches('.edit')) {
    // do whatever 
  }
});

:

var XXX = {};
XXX.PrototypeTemplate = function(element) {
  this.element = element;
  var tmpl = this.element.getAttribute('data-prototype');
  addBox = function() {
    this.element.insertAdjacentHTML('beforebegin', tmpl);
  };
  this.element.addEventListener('click', addBox.bind(this));
};


new XXX.PrototypeTemplate(document.querySelector('[data-prototype]'));

document.body.addEventListener('click', e => {
  if (e.target.matches('.edit')) {
    // do whatever
    console.log('click happend');
  }
});
[data-prototype] {
  cursor: pointer;
}
<div class="box"><a class="edit" href="#">Edit</a></div>
<span data-prototype="<div class=&quot;box&quot;><a class=&quot;edit&quot; href=&quot;#&quot;>Edit</a></div>">Add box</span>
Hide result

, MDN Element.prototype.matches.

+2

Thanks to everyone who answers the question, this is all useful information. How about the following:

Wrap all the functions needed inside XXX.InitializeAllFunctions = function(wrap) {}, and pass documentas a wrapper to load the first page. So it behaves as before. When inserting new DOM elements, just pass them also to this function before inserting them into the DOM. Works like a charm:

var XXX = {};

XXX.ClickMe = function(element){
    this.element = element;
    onClick = function() {
        console.log('click happend');
    };
    this.element.addEventListener('click', onClick.bind(this));
};

XXX.PrototypeTemplate = function(element) {
    this.element = element;

    addBox = function() {
        var tmpl = this.element.getAttribute('data-prototype');
        var html = new DOMParser().parseFromString(tmpl, 'text/html');

        XXX.InitializeAllFunctions(html);  // Initialize here on all new HTML
                                           // before inserting into DOM

        this.element.parentNode.insertBefore(
            html.body.childNodes[0],
            this.element
        );
    };

    this.element.addEventListener('click', addBox.bind(this));
};

XXX.InitializeAllFunctions = function(wrap) {

    var wrap = wrap == null ? document : wrap;

    [...wrap.querySelectorAll('[data-prototype]')].forEach(
        function (element, index) {
            new XXX.PrototypeTemplate(element);
        }
    );

    [...wrap.querySelectorAll('.edit')].forEach(
        function (element, index) {
            new XXX.ClickMe(element);
        }
    );
};

XXX.InitializeAllFunctions(document);
[data-prototype] {
  cursor: pointer;
}
<div class="box"><a class="edit" href="#">Edit</a></div>
<span data-prototype="<div class=&quot;box&quot;><a class=&quot;edit&quot; href=&quot;#&quot;>Edit</a></div>">Add box</span>
Run codeHide result
0
source

All Articles