├── .editorconfig ├── .gitignore ├── .travis.yml ├── bower.json ├── demo ├── destroy.html ├── multiple.html ├── node.html ├── nodelist.html └── selector.html ├── dist └── good-listener.js ├── karma.conf.js ├── package.json ├── readme.md ├── src ├── is.js └── listen.js └── test ├── is.js └── listen.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # http://editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | # Change these settings to your own preference 9 | indent_style = space 10 | indent_size = 4 11 | 12 | # We recommend you to keep these unchanged 13 | end_of_line = lf 14 | charset = utf-8 15 | trim_trailing_whitespace = true 16 | insert_final_newline = true 17 | 18 | [*.md] 19 | trim_trailing_whitespace = false 20 | 21 | [{package.json,bower.json}] 22 | indent_size = 2 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - stable 4 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "good-listener", 3 | "description": "A more versatile way of adding & removing event listeners", 4 | "version": "1.2.1", 5 | "license": "MIT", 6 | "main": "dist/good-listener.js", 7 | "keywords": [ 8 | "event", 9 | "listener" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /demo/destroy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Destroy 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /demo/multiple.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Selector 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /demo/node.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Node 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /demo/nodelist.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NodeList 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /demo/selector.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Selector 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /dist/good-listener.js: -------------------------------------------------------------------------------- 1 | (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.listen = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o A more versatile way of adding & removing event listeners. 6 | 7 | ![good listener](https://cloud.githubusercontent.com/assets/398893/10718224/dfc25f6c-7b2a-11e5-9d3d-75b35e8603c8.jpg) 8 | 9 | ## Install 10 | 11 | You can get it on npm. 12 | 13 | ``` 14 | npm install good-listener --save 15 | ``` 16 | 17 | Or bower, too. 18 | 19 | ``` 20 | bower install good-listener --save 21 | ``` 22 | 23 | If you're not into package management, just [download a ZIP](https://github.com/zenorocha/good-listener/archive/master.zip) file. 24 | 25 | ## Setup 26 | 27 | ###### Node (Browserify) 28 | 29 | ```js 30 | var listen = require('good-listener'); 31 | ``` 32 | 33 | ###### Browser (Standalone) 34 | 35 | ```html 36 | 37 | ``` 38 | 39 | ## Usage 40 | 41 | ### Add an event listener 42 | 43 | By passing a string selector [(see full demo)](https://github.com/zenorocha/good-listener/blob/master/demo/selector.html). 44 | 45 | ```js 46 | listen('.btn', 'click', function(e) { 47 | console.log(e); 48 | }); 49 | ``` 50 | 51 | Or by passing a HTML element [(see full demo)](https://github.com/zenorocha/good-listener/blob/master/demo/node.html). 52 | 53 | ```js 54 | var logo = document.getElementById('logo'); 55 | 56 | listen(logo, 'click', function(e) { 57 | console.log(e); 58 | }); 59 | ``` 60 | 61 | Or by passing a list of HTML elements [(see full demo)](https://github.com/zenorocha/good-listener/blob/master/demo/nodelist.html). 62 | 63 | ```js 64 | var anchors = document.querySelectorAll('a'); 65 | 66 | listen(anchors, 'click', function(e) { 67 | console.log(e); 68 | }); 69 | ``` 70 | 71 | ### Remove an event listener 72 | 73 | By calling the `destroy` function that returned from previous operation [(see full demo)](https://github.com/zenorocha/good-listener/blob/master/demo/destroy.html). 74 | 75 | ```js 76 | var listener = listen('.btn', 'click', function(e) { 77 | console.log(e); 78 | }); 79 | 80 | listener.destroy(); 81 | ``` 82 | 83 | ## Browser Support 84 | 85 | | Chrome logo | Edge logo | Firefox logo | Internet Explorer logo | Opera logo | Safari logo | 86 | |:---:|:---:|:---:|:---:|:---:|:---:| 87 | | Latest ✔ | Latest ✔ | Latest ✔ | 9+ ✔ | Latest ✔ | Latest ✔ | 88 | 89 | ## License 90 | 91 | [MIT License](http://zenorocha.mit-license.org/) © Zeno Rocha 92 | -------------------------------------------------------------------------------- /src/is.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Check if argument is a HTML element. 3 | * 4 | * @param {Object} value 5 | * @return {Boolean} 6 | */ 7 | exports.node = function(value) { 8 | return value !== undefined 9 | && value instanceof HTMLElement 10 | && value.nodeType === 1; 11 | }; 12 | 13 | /** 14 | * Check if argument is a list of HTML elements. 15 | * 16 | * @param {Object} value 17 | * @return {Boolean} 18 | */ 19 | exports.nodeList = function(value) { 20 | var type = Object.prototype.toString.call(value); 21 | 22 | return value !== undefined 23 | && (type === '[object NodeList]' || type === '[object HTMLCollection]') 24 | && ('length' in value) 25 | && (value.length === 0 || exports.node(value[0])); 26 | }; 27 | 28 | /** 29 | * Check if argument is a SVG element. 30 | * 31 | * @param {Object} value 32 | * @return {Boolean} 33 | */ 34 | exports.svg = function(value) { 35 | return value !== undefined 36 | && value instanceof SVGElement; 37 | }; 38 | 39 | /** 40 | * Check if argument is a string. 41 | * 42 | * @param {Object} value 43 | * @return {Boolean} 44 | */ 45 | exports.string = function(value) { 46 | return typeof value === 'string' 47 | || value instanceof String; 48 | }; 49 | 50 | /** 51 | * Check if argument is a function. 52 | * 53 | * @param {Object} value 54 | * @return {Boolean} 55 | */ 56 | exports.fn = function(value) { 57 | var type = Object.prototype.toString.call(value); 58 | 59 | return type === '[object Function]'; 60 | }; 61 | -------------------------------------------------------------------------------- /src/listen.js: -------------------------------------------------------------------------------- 1 | var is = require('./is'); 2 | var delegate = require('delegate'); 3 | 4 | /** 5 | * Validates all params and calls the right 6 | * listener function based on its target type. 7 | * 8 | * @param {String|HTMLElement|HTMLCollection|NodeList} target 9 | * @param {String} type 10 | * @param {Function} callback 11 | * @return {Object} 12 | */ 13 | function listen(target, type, callback) { 14 | if (!target && !type && !callback) { 15 | throw new Error('Missing required arguments'); 16 | } 17 | 18 | if (!is.string(type)) { 19 | throw new TypeError('Second argument must be a String'); 20 | } 21 | 22 | if (!is.fn(callback)) { 23 | throw new TypeError('Third argument must be a Function'); 24 | } 25 | 26 | if (is.node(target)) { 27 | return listenNode(target, type, callback); 28 | } 29 | else if (is.nodeList(target)) { 30 | return listenNodeList(target, type, callback); 31 | } 32 | else if (is.string(target)) { 33 | return listenSelector(target, type, callback); 34 | } 35 | else { 36 | throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList'); 37 | } 38 | } 39 | 40 | /** 41 | * Adds an event listener to a HTML element 42 | * and returns a remove listener function. 43 | * 44 | * @param {HTMLElement} node 45 | * @param {String} type 46 | * @param {Function} callback 47 | * @return {Object} 48 | */ 49 | function listenNode(node, type, callback) { 50 | node.addEventListener(type, callback); 51 | 52 | return { 53 | destroy: function() { 54 | node.removeEventListener(type, callback); 55 | } 56 | } 57 | } 58 | 59 | /** 60 | * Add an event listener to a list of HTML elements 61 | * and returns a remove listener function. 62 | * 63 | * @param {NodeList|HTMLCollection} nodeList 64 | * @param {String} type 65 | * @param {Function} callback 66 | * @return {Object} 67 | */ 68 | function listenNodeList(nodeList, type, callback) { 69 | Array.prototype.forEach.call(nodeList, function(node) { 70 | node.addEventListener(type, callback); 71 | }); 72 | 73 | return { 74 | destroy: function() { 75 | Array.prototype.forEach.call(nodeList, function(node) { 76 | node.removeEventListener(type, callback); 77 | }); 78 | } 79 | } 80 | } 81 | 82 | /** 83 | * Add an event listener to a selector 84 | * and returns a remove listener function. 85 | * 86 | * @param {String} selector 87 | * @param {String} type 88 | * @param {Function} callback 89 | * @return {Object} 90 | */ 91 | function listenSelector(selector, type, callback) { 92 | return delegate(document.body, selector, type, callback); 93 | } 94 | 95 | module.exports = listen; 96 | -------------------------------------------------------------------------------- /test/is.js: -------------------------------------------------------------------------------- 1 | var is = require('../src/is'); 2 | 3 | describe('is', function() { 4 | before(function() { 5 | global.node = document.createElement('div'); 6 | global.node.setAttribute('id', 'foo'); 7 | global.node.setAttribute('class', 'foo'); 8 | document.body.appendChild(global.node); 9 | 10 | global.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); 11 | global.svg.setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'); 12 | global.svg.setAttribute('id', 'svg'); 13 | global.svg.setAttribute('class', 'svg'); 14 | document.body.appendChild(global.svg); 15 | }); 16 | 17 | after(function() { 18 | document.body.innerHTML = ''; 19 | }); 20 | 21 | describe('is.node', function() { 22 | it('should be considered as node', function() { 23 | assert.ok(is.node(document.getElementById('foo'))); 24 | assert.ok(is.node(document.getElementsByTagName('div')[0])); 25 | assert.ok(is.node(document.getElementsByClassName('foo')[0])); 26 | assert.ok(is.node(document.querySelector('.foo'))); 27 | }); 28 | 29 | it('should not be considered as node', function() { 30 | // is.nodeList 31 | assert.notOk(is.node(document.getElementsByTagName('div'))); 32 | assert.notOk(is.node(document.getElementsByClassName('foo'))); 33 | assert.notOk(is.node(document.querySelectorAll('.foo'))); 34 | 35 | // is.string 36 | assert.notOk(is.node('abc')); 37 | assert.notOk(is.node(new String('abc'))); 38 | 39 | // is.svg 40 | assert.notOk(is.node(document.getElementById('svg'))); 41 | assert.notOk(is.node(document.getElementsByTagName('svg')[0])); 42 | assert.notOk(is.node(document.getElementsByClassName('svg')[0])); 43 | assert.notOk(is.node(document.querySelector('.svg'))); 44 | 45 | // is.fn 46 | assert.notOk(is.node(function () {})); 47 | 48 | // everything else 49 | assert.notOk(is.node(undefined)); 50 | assert.notOk(is.node(null)); 51 | assert.notOk(is.node(false)); 52 | assert.notOk(is.node(true)); 53 | assert.notOk(is.node([])); 54 | assert.notOk(is.node({})); 55 | assert.notOk(is.node(/a/g)); 56 | assert.notOk(is.node(new RegExp('a', 'g'))); 57 | assert.notOk(is.node(new Date())); 58 | assert.notOk(is.node(42)); 59 | assert.notOk(is.node(NaN)); 60 | assert.notOk(is.node(Infinity)); 61 | assert.notOk(is.node(new Number(42))); 62 | }); 63 | }); 64 | 65 | describe('is.nodeList', function() { 66 | it('should be considered as nodeList', function() { 67 | assert.ok(is.nodeList(document.getElementsByTagName('div'))); 68 | assert.ok(is.nodeList(document.getElementsByClassName('foo'))); 69 | assert.ok(is.nodeList(document.querySelectorAll('.foo'))); 70 | }); 71 | 72 | it('should not be considered as nodeList', function() { 73 | // is.node 74 | assert.notOk(is.nodeList(document.getElementById('foo'))); 75 | assert.notOk(is.nodeList(document.getElementsByTagName('div')[0])); 76 | assert.notOk(is.nodeList(document.getElementsByClassName('foo')[0])); 77 | assert.notOk(is.nodeList(document.querySelector('.foo'))); 78 | 79 | // is.string 80 | assert.notOk(is.nodeList('abc')); 81 | assert.notOk(is.nodeList(new String('abc'))); 82 | 83 | // is.svg 84 | assert.notOk(is.nodeList(document.getElementById('svg'))); 85 | assert.notOk(is.nodeList(document.getElementsByTagName('svg')[0])); 86 | assert.notOk(is.nodeList(document.getElementsByClassName('svg')[0])); 87 | assert.notOk(is.nodeList(document.querySelector('.svg'))); 88 | 89 | // is.fn 90 | assert.notOk(is.nodeList(function () {})); 91 | 92 | // everything else 93 | assert.notOk(is.nodeList(undefined)); 94 | assert.notOk(is.nodeList(null)); 95 | assert.notOk(is.nodeList(false)); 96 | assert.notOk(is.nodeList(true)); 97 | assert.notOk(is.nodeList([])); 98 | assert.notOk(is.nodeList({})); 99 | assert.notOk(is.nodeList(/a/g)); 100 | assert.notOk(is.nodeList(new RegExp('a', 'g'))); 101 | assert.notOk(is.nodeList(new Date())); 102 | assert.notOk(is.nodeList(42)); 103 | assert.notOk(is.nodeList(NaN)); 104 | assert.notOk(is.nodeList(Infinity)); 105 | assert.notOk(is.nodeList(new Number(42))); 106 | }); 107 | }); 108 | 109 | describe('is.string', function() { 110 | it('should be considered as string', function() { 111 | assert.ok(is.string('abc')); 112 | assert.ok(is.string(new String('abc'))); 113 | }); 114 | 115 | it('should not be considered as string', function() { 116 | // is.node 117 | assert.notOk(is.string(document.getElementById('foo'))); 118 | assert.notOk(is.string(document.getElementsByTagName('div')[0])); 119 | assert.notOk(is.string(document.getElementsByClassName('foo')[0])); 120 | assert.notOk(is.string(document.querySelector('.foo'))); 121 | 122 | // is.nodeList 123 | assert.notOk(is.string(document.getElementsByTagName('div'))); 124 | assert.notOk(is.string(document.getElementsByClassName('foo'))); 125 | assert.notOk(is.string(document.querySelectorAll('.foo'))); 126 | 127 | // is.svg 128 | assert.notOk(is.string(document.getElementById('svg'))); 129 | assert.notOk(is.string(document.getElementsByTagName('svg')[0])); 130 | assert.notOk(is.string(document.getElementsByClassName('svg')[0])); 131 | assert.notOk(is.string(document.querySelector('.svg'))); 132 | 133 | // is.fn 134 | assert.notOk(is.string(function () {})); 135 | 136 | // everything else 137 | assert.notOk(is.string(undefined)); 138 | assert.notOk(is.string(null)); 139 | assert.notOk(is.string(false)); 140 | assert.notOk(is.string(true)); 141 | assert.notOk(is.string([])); 142 | assert.notOk(is.string({})); 143 | assert.notOk(is.string(/a/g)); 144 | assert.notOk(is.string(new RegExp('a', 'g'))); 145 | assert.notOk(is.string(new Date())); 146 | assert.notOk(is.string(42)); 147 | assert.notOk(is.string(NaN)); 148 | assert.notOk(is.string(Infinity)); 149 | assert.notOk(is.string(new Number(42))); 150 | }); 151 | }); 152 | 153 | describe('is.svg', function() { 154 | it('should be considered as svg', function() { 155 | assert.ok(is.svg(document.getElementById('svg'))); 156 | assert.ok(is.svg(document.getElementsByTagName('svg')[0])); 157 | assert.ok(is.svg(document.getElementsByClassName('svg')[0])); 158 | assert.ok(is.svg(document.querySelector('.svg'))); 159 | }); 160 | 161 | it('should not be considered as svg', function() { 162 | // is.node 163 | assert.notOk(is.svg(document.getElementById('foo'))); 164 | assert.notOk(is.svg(document.getElementsByTagName('div')[0])); 165 | assert.notOk(is.svg(document.getElementsByClassName('foo')[0])); 166 | assert.notOk(is.svg(document.querySelector('.foo'))); 167 | 168 | // is.nodeList 169 | assert.notOk(is.svg(document.getElementsByTagName('div'))); 170 | assert.notOk(is.svg(document.getElementsByClassName('foo'))); 171 | assert.notOk(is.svg(document.querySelectorAll('.foo'))); 172 | 173 | // is.string 174 | assert.notOk(is.svg('abc')); 175 | assert.notOk(is.svg(new String('abc'))); 176 | 177 | // is.fn 178 | assert.notOk(is.svg(function () {})); 179 | 180 | // everything else 181 | assert.notOk(is.svg(undefined)); 182 | assert.notOk(is.svg(null)); 183 | assert.notOk(is.svg(false)); 184 | assert.notOk(is.svg(true)); 185 | assert.notOk(is.svg([])); 186 | assert.notOk(is.svg({})); 187 | assert.notOk(is.svg(/a/g)); 188 | assert.notOk(is.svg(new RegExp('a', 'g'))); 189 | assert.notOk(is.svg(new Date())); 190 | assert.notOk(is.svg(42)); 191 | assert.notOk(is.svg(NaN)); 192 | assert.notOk(is.svg(Infinity)); 193 | assert.notOk(is.svg(new Number(42))); 194 | }); 195 | }); 196 | 197 | describe('is.fn', function() { 198 | it('should be considered as function', function() { 199 | assert.ok(is.fn(function () {})); 200 | }); 201 | 202 | it('should not be considered as function', function() { 203 | // is.node 204 | assert.notOk(is.fn(document.getElementById('foo'))); 205 | assert.notOk(is.fn(document.getElementsByTagName('div')[0])); 206 | assert.notOk(is.fn(document.getElementsByClassName('foo')[0])); 207 | assert.notOk(is.fn(document.querySelector('.foo'))); 208 | 209 | // is.nodeList 210 | assert.notOk(is.fn(document.getElementsByTagName('div'))); 211 | assert.notOk(is.fn(document.getElementsByClassName('foo'))); 212 | assert.notOk(is.fn(document.querySelectorAll('.foo'))); 213 | 214 | // is.string 215 | assert.notOk(is.fn('abc')); 216 | assert.notOk(is.fn(new String('abc'))); 217 | 218 | // is.svg 219 | assert.notOk(is.fn(document.getElementById('svg'))); 220 | assert.notOk(is.fn(document.getElementsByTagName('svg')[0])); 221 | assert.notOk(is.fn(document.getElementsByClassName('svg')[0])); 222 | assert.notOk(is.fn(document.querySelector('.svg'))); 223 | 224 | // everything else 225 | assert.notOk(is.fn(undefined)); 226 | assert.notOk(is.fn(null)); 227 | assert.notOk(is.fn(false)); 228 | assert.notOk(is.fn(true)); 229 | assert.notOk(is.fn([])); 230 | assert.notOk(is.fn({})); 231 | assert.notOk(is.fn(/a/g)); 232 | assert.notOk(is.fn(new RegExp('a', 'g'))); 233 | assert.notOk(is.fn(new Date())); 234 | assert.notOk(is.fn(42)); 235 | assert.notOk(is.fn(NaN)); 236 | assert.notOk(is.fn(Infinity)); 237 | assert.notOk(is.fn(new Number(42))); 238 | }); 239 | }); 240 | }); 241 | -------------------------------------------------------------------------------- /test/listen.js: -------------------------------------------------------------------------------- 1 | var listen = require('../src/listen'); 2 | var simulant = require('simulant'); 3 | 4 | describe('good-listener', function() { 5 | before(function() { 6 | global.node = document.createElement('div'); 7 | global.node.setAttribute('id', 'foo'); 8 | global.node.setAttribute('class', 'foo'); 9 | document.body.appendChild(global.node); 10 | }); 11 | 12 | after(function() { 13 | document.body.innerHTML = ''; 14 | }); 15 | 16 | describe('listen', function() { 17 | it('should throw an error since arguments were not passed', function(done) { 18 | try { 19 | listen(); 20 | } 21 | catch(error) { 22 | assert.equal(error.message, 'Missing required arguments'); 23 | done(); 24 | } 25 | }); 26 | 27 | it('should throw an error since "target" was invalid', function(done) { 28 | try { 29 | listen(null, 'click', function() {}); 30 | } 31 | catch(error) { 32 | assert.equal(error.message, 'First argument must be a String, HTMLElement, HTMLCollection, or NodeList'); 33 | done(); 34 | } 35 | }); 36 | 37 | it('should throw an error since "type" was invalid', function(done) { 38 | try { 39 | listen('.btn', false, function() {}); 40 | } 41 | catch(error) { 42 | assert.equal(error.message, 'Second argument must be a String'); 43 | done(); 44 | } 45 | }); 46 | 47 | it('should throw an error since "callback" was invalid', function(done) { 48 | try { 49 | listen('.btn', 'click', []); 50 | } 51 | catch(error) { 52 | assert.equal(error.message, 'Third argument must be a Function'); 53 | done(); 54 | } 55 | }); 56 | }); 57 | 58 | describe('listenNode', function() { 59 | before(function() { 60 | global.target = document.querySelector('#foo'); 61 | global.spy = sinon.spy(global.target, 'removeEventListener'); 62 | }); 63 | 64 | after(function() { 65 | global.spy.restore(); 66 | }); 67 | 68 | it('should add an event listener', function(done) { 69 | listen(global.target, 'click', function() { 70 | done(); 71 | }); 72 | 73 | simulant.fire(global.target, simulant('click')); 74 | }); 75 | 76 | it('should remove an event listener', function() { 77 | var listener = listen(global.target, 'click', function() {}); 78 | 79 | listener.destroy(); 80 | assert.ok(global.spy.calledOnce); 81 | }); 82 | }); 83 | 84 | describe('listenNodeList', function() { 85 | before(function() { 86 | global.targets = document.querySelectorAll('.foo'); 87 | global.spy = sinon.spy(global.targets[0], 'removeEventListener'); 88 | }); 89 | 90 | after(function() { 91 | global.spy.restore(); 92 | }); 93 | 94 | it('should add an event listener', function(done) { 95 | listen(global.targets, 'click', function() { 96 | done(); 97 | }); 98 | 99 | simulant.fire(global.targets[0], simulant('click')); 100 | }); 101 | 102 | it('should remove an event listener', function() { 103 | var listener = listen(global.targets, 'click', function() {}); 104 | 105 | listener.destroy(); 106 | assert.ok(global.spy.calledOnce); 107 | }); 108 | }); 109 | 110 | describe('listenSelector', function() { 111 | before(function() { 112 | global.target = document.querySelector('.foo'); 113 | global.spy = sinon.spy(document.body, 'removeEventListener'); 114 | }); 115 | 116 | after(function() { 117 | global.spy.restore(); 118 | }); 119 | 120 | it('should add an event listener', function(done) { 121 | listen('.foo', 'click', function() { 122 | done(); 123 | }); 124 | 125 | simulant.fire(global.target, simulant('click')); 126 | }); 127 | 128 | it('should remove an event listener', function() { 129 | var listener = listen('.foo', 'click', function() {}); 130 | 131 | listener.destroy(); 132 | assert.ok(global.spy.calledOnce); 133 | }); 134 | }); 135 | }); 136 | --------------------------------------------------------------------------------