Javascript эквивалентен $.on

Как кто-то, кто (к сожалению) узнал больше jQuery, чем raw javascript, я просто сейчас трачу время, чтобы заменить весь мой код raw javascript. Нет, это не нужно, но мне легче учиться. Проблема, с которой я сталкиваюсь, заключается в преобразовании всех моих $(document).on с raw javascript. Мой сайт является "одностраничным приложением", и большинство моих фактических HTML находятся в разных файлах, которые вызывается через запросы Ajax. Итак, мой вопрос: как я могу искать event из динамически загруженного контента? Я предполагаю, что мне пришлось бы добавить к ним событие onclick, но как это сделать, jQuery делает это без необходимости onclick event?

Ответы

Ответ 1

Привязка обработчиков в нативном API выполняется с помощью addEventListener().

Чтобы эмулировать делегирование событий jQuery, вы можете довольно легко создать систему, которая использует метод .matches() для проверки .matches() вами селектора.

function delegate(el, evt, sel, handler) {
    el.addEventListener(evt, function(event) {
        var t = event.target;
        while (t && t !== this) {
            if (t.matches(sel)) {
                handler.call(t, event);
            }
            t = t.parentNode;
        }
    });
}

Вероятно, необходимо сделать некоторые изменения, но в основном это функция, которая принимает элемент для привязки, например, document, тип события, селектор и обработчик.

Он начинается с e.target и пересекает родителей, пока не e.target связанного элемента. Каждый раз он проверяет, соответствует ли текущий элемент селектору, и, если это так, он вызывает обработчик.

Так что вы бы назвали это так:

delegate(document, "click", ".some_elem", function(event) {
    this.style.border = "2px dashed orange";
});

Здесь живая демонстрация, которая также добавляет динамические элементы, чтобы показать, что новые элементы также подобраны.

function delegate(el, evt, sel, handler) {
    el.addEventListener(evt, function(event) {
        var t = event.target;
        while (t && t !== this) {
            if (t.matches(sel)) {
                handler.call(t, event);
            }
            t = t.parentNode;
        }
    });
}

delegate(document, "click", ".some_elem", function(event) {
    this.parentNode.appendChild(this.cloneNode(true));
    this.style.border = "2px dashed orange";
});
<div>
  <p class="some_elem">
    <span>
      CLICK ME
    </span>
  </p>
</div>

Ответ 2

Вот JavaScript, эквивалентный on()

JQuery

$(document).on('click', '#my-id', callback);

function callback(){
   ...handler code here
}

Javascript

document.addEventListener('click', function(event) {
    if (event.target.id == 'my-id') {
      callback();
    }
});
function callback(){
   ...handler code here
}

При таком подходе идея состоит в том, чтобы использовать event.target. Конечно, по мере изменения селектора ваш код будет становиться все более вовлеченным

Ответ 3

В современных браузерах вы можете использовать Element.closest() чтобы упростить репликацию метода jQuery .on() а также обеспечить захват событий, всплывающих из дочерних элементов целевого элемента (нюанс, который пропускают некоторые другие реализации). В старых браузерах, включая IE, для этого потребуется полифилл.

const on = (element, event, selector, handler) => {
  element.addEventListener(event, e => {
    if (e.target.closest(selector)) {
      handler(e);
    }
  });
}

on(document, 'click', '#test', e => {
  console.log('click');
});
<button id="test">
  Clickable
  <i>Also Clickable</i>
</button>

Ответ 4

Другой подход для современных браузеров будет выглядеть примерно так:

const on = (selector, event, handler, element=document) => {
  element.addEventListener(event, (e) => { if(e.target.matches(selector)) handler(e); });
};
// click will work for each selector
on('[type="button"], .test, #test','click',  e => {
  alert(e.target.innerHTML);
});
// click event will work for nested .test3 element only
on('.test3','click',  e => {
  alert(e.target.innerHTML);
},document.querySelector('.test2'));
<div id="test">
test
</div>

<div class="test">
test 1
</div>
<div class="test">
test 2
</div>

<button type="button">
go
</button>

<div class="test3">
test 3 outer
</div>


<div class="test2">
<div class="test3">
test 3 inner
</div>
test 2
</div>