Source: delegate/delegate.es.js

/**
 * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 */

const USE_CAPTURE = {
	blur: true,
	error: true,
	focus: true,
	invalid: true,
	load: true,
	scroll: true,
};

/**
 * Checks if element is disabled or whether it exists in a disabled element tree
 * @param {!Element} element The DOM element to check.
 * @return {boolean}
 */
function isDisabled(node) {
	return !!(node.disabled || node.closest('[disabled]'));
}

/**
 * Listens to the specified event on the given DOM element, but only calls the
 * given callback listener when it's triggered by elements that match the
 * given selector or target element.
 * @param {!Element} element The DOM element the event should be listened on.
 * @param {string} eventName The name of the event to listen to.
 * @param {string} selector Css selector that should match the event for the
 *     listener to be triggered.
 * @param {!function(!Object)} callback Function to be called when the event
 *     is triggered. It will receive the normalized event object.
 * @return {function} Can be used to remove the listener.
 */
function delegate(element, eventName, selector, callback) {
	const eventHandler = (event) => {
		const {defaultPrevented, target} = event;

		if (defaultPrevented || (eventName === 'click' && isDisabled(target))) {
			return;
		}

		const delegateTarget = target.closest(selector);

		if (delegateTarget) {
			event.delegateTarget = delegateTarget;

			callback(event);
		}
	};

	element.addEventListener(eventName, eventHandler, !!USE_CAPTURE[eventName]);

	return {
		dispose() {
			element.removeEventListener(eventName, eventHandler);
		},
	};
}

export default delegate;