guilin 桂林
guilin 桂林

Reputation: 17422

How to handle events of dynamic created elements without jQuery or zepto?

I know that jquery or zepto $(selctor).on(event, handler) will trigger events of dynamic created elements. But I don't use jquery and zepto.I'd like write my code from scratch for both learning and lightweigh.

There are lots of sth.innerHTML=blabla and sth.appendChild(..) snippets in my codes. I have to bind event to them.

Update:

I want a simple function like this,

function $on(selector, eventType, handler) {
    document.querySelectAll(selector).forEach(function(el) {
        el.addEventListener(eventType, handler);
    });
    // on new DOM insert or replaced, let us call it newDom
      newDom.querySelectAll(selector).forEach(function(el) {
          el.addEventListener(eventType, handler);
      });
}

but I don't know if jquery work like this, I want to know it, and complete my function.

Upvotes: 1

Views: 684

Answers (1)

Mulan
Mulan

Reputation: 135357

Here's an example using the newer MutationObserver recommended by the MDN docs.

This depends on Element.prototype.matches which is not widely supported yet. However, I created a little wrapper to make it easier to work with.

DEMO

// live listener function
(function(window) {

  // Element.matches "polyfill"
  (function(e){
    if (typeof e.matches !== "function") {
      e.matches = e.webkitMatchesSelector ||
                  e.mozMatchesSelector    ||
                  e.msMatchesSelector     ||
                  e.oMatchesSelector      ||
                  e.matchesSelector;
    }
  })(Element.prototype);

  function on(selector, eventType, handler) {
    // add listener for all existing elements
    [].forEach.call(document.querySelectorAll(selector), function(elem) {
      elem.addEventListener(eventType, handler);
    });

    // create new "live" observer
    var observer = new MutationObserver(function(records) {
      records.forEach(function(record) {
        [].forEach.call(record.addedNodes || [], function(elem) {
          if (elem.matches(selector)) {
            elem.addEventListener(eventType, handler);
          }
        });
      });
    });

    // watch for DOM changes to body's children and body's subtree
    observer.observe(document.body, {
      childList: true,
      subtree: true
    });

    return observer;
  }

  window.on = on;
})(window);

Let's create some elements

// sample button generator
function createButton() {
  var b = document.createElement("button");
  b.className = "hello";
  b.innerHTML = "Spaghett";
  document.body.appendChild(b);
}

// create a new button every second
setInterval(createButton, 1000);

Actual usage

on("button.hello", "click", function(event) {
  alert("spooked ya!");
  event.preventDefault();
});

Notable notes!

I'm using [].forEach.call(..., fn) because querySelectorAll does not return an Array. Instead, it returns a NodeList. A NodeList object does not have Array.prototype in its prototype chain and therefore does not have direct access to the .forEach function. MutationRecord.prototype.addedNodes also returns a NodeList.

Upvotes: 5

Related Questions