chirashijs/chirashi

View on GitHub
lib/events/once.js

Summary

Maintainability
A
0 mins
Test Coverage
import forIn from '../core/forIn'
import on from './on'

/**
 * Bind events listener on each element of elements and unbind after first triggered.
 * @param {(string|Array|NodeList|HTMLCollection|EventTarget)} elements - The iterable, selector or elements.
 * @param {Object.<string, eventCallback>} input - An object in which keys are events to bind seperated with coma and/or spaces and values are eventCallbacks.
 * @param {boolean} [eachElement=false] - If true only current target's events listeners will be removed after trigger.
 * @param {boolean} [eachEvent=false] - If true only triggered event group of events listeners will be removed.
 * @return {offCallback} cancel - cancel function for unbinding.
 * @example //esnext
 * import { createElement, append, once, trigger } from 'chirashi'
 * const maki = createElement('a.cheese.maki')
 * const sushi = createElement('a.wasabi.sushi')
 * append(document.body, [maki, sushi])
 * const cancel = once('.cheese, .wasabi', {
 *   click(e, target) => {
 *     console.log('clicked', target)
 *   },
 *   'mouseenter mousemove': (e, target) => {
 *     console.log('mouse in', target)
 *   }
 * }, true, true)
 * trigger(maki, 'click') //simulate user's click
 * // LOGS: "clicked" <a class="maki cheese"></a>
 * // click event listener was auto-removed from maki
 * trigger(sushi, 'click') //simulate user's click
 * // LOGS: "clicked" <a class="sushi wasabi"></a>
 * // click event listener was auto-removed from sushi
 * cancel() //remove all listeners from all elements
 * once('.cheese, .wasabi', {
 *   click(e, target) => {
 *     console.log('clicked', target)
 *   }
 * })
 * trigger(maki, 'click') //simulate user's click
 * // LOGS: "clicked" <a class="maki cheese"></a>
 * // all events listeners were auto-removed from all elements
 * trigger(sushi, 'click') //simulate user's click
 * // won't log anything
 * @example //es5
 * var maki = Chirashi.createElement('a.cheese.maki')
 * var sushi = Chirashi.createElement('a.wasabi.sushi')
 * Chirashi.append(document.body, [maki, sushi])
 * var cancel = Chirashi.once('.cheese, .wasabi', {
 *   click: function (e, target) {
 *     console.log('clicked', target)
 *   },
 *   'mouseenter mousemove': function (e, target) {
 *     console.log('mouse in', target)
 *   }
 * }, true, true)
 * Chirashi.trigger(maki, 'click') //simulate user's click
 * // LOGS: "clicked" <a class="maki cheese"></a>
 * // click event listener was auto-removed from maki
 * Chirashi.trigger(sushi, 'click') //simulate user's click
 * // LOGS: "clicked" <a class="sushi wasabi"></a>
 * // click event listener was auto-removed from sushi
 * cancel() //remove all listeners from all elements
 * Chirashi.once('.cheese, .wasabi', {
 *   click: function (e, target) {
 *     console.log('clicked', target)
 *   }
 * })
 * Chirashi.trigger(maki, 'click') //simulate user's click
 * // LOGS: "clicked" <a class="maki cheese"></a>
 * // all events listeners were auto-removed from all elements
 * Chirashi.trigger(sushi, 'click') //simulate user's click
 * // won't log anything
 */
export default function once (elements, input, eachElement = false, eachEvent = false) {
  let off
  const eventsObj = {}

  forIn(input, (events, callback) => {
    eventsObj[events] = event => {
      callback(event)

      off(eachElement && event.currentTarget, eachEvent && events)
    }
  })

  return (off = on(elements, eventsObj))
}