Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ npm-debug.log
.idea
test/domassist.test.dist.js
dist
*.map
.DS_Store
7 changes: 4 additions & 3 deletions lib/modify.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,18 @@ function modify(selector, params) {
addClass,
removeClass,
html,
events: on,
styles,
styles
};
const els = find(selector);
if (els.length) {
els.forEach((el) => {
Object.keys(params).forEach((param, index) => {
Object.keys(params).forEach(param => {
if (param in modules) {
if (param === 'events') {
bindEvents(el, params[param]);
return;
}

modules[param](el, params[param]);
}
});
Expand Down
68 changes: 58 additions & 10 deletions lib/off.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,70 @@
import find from './find';
import privData from './privData';

function off(selector, event) {
if (Array.isArray(selector)) {
selector.forEach((item) => off(item, event));
function extractFromDict(element, key, handler, justNameSpace) {
const eventDict = privData.get(element, 'events');
let eventsToUnbind = [];
let type = key;
let namespace = '';

// Allowing namespace to continue
if (type.indexOf('.') > 0) {
const tmp = type.split('.');

type = tmp.shift();
namespace = `.${tmp.join('.')}`;

// Let's remove the namespaced events
extractFromDict(element, namespace, handler, true);
}
if (!window._domassistevents) {
window._domassistevents = {};

let filtered = eventDict[type];

if (filtered) {
if (typeof handler !== 'undefined') {
// Just those that match the handler
filtered = filtered.filter(d => d.cb === handler);
}

eventsToUnbind = eventsToUnbind.concat(
filtered.map(data => ({ type, data }))
);

// Actually removing from the dict
if (typeof handler !== 'undefined') {
eventDict[type] = eventDict[type].filter(d => d.cb !== handler);
} else {
eventDict[type].length = 0;
}

// Don't keep the key, prevent leaks
if (eventDict[type].length === 0) {
eventDict[type] = undefined;
}
}

// When unbinding for .namespace need to actually remove the normal events too
if (!justNameSpace && type.indexOf('.') === 0) {
// Flat map
eventsToUnbind = Array.prototype.concat.apply([],
eventsToUnbind.map(({ data }) => extractFromDict(element, data.type, data.cb)));
}

const data = window._domassistevents[`_${event}`];
return eventsToUnbind;
}

if (!data) {
return;
function off(selector, event, handler) {
if (Array.isArray(selector)) {
selector.forEach((item) => off(item, event, handler));
}

const el = find(selector);

if (el.length) {
el.forEach((item) => {
item.removeEventListener(event, data.cb, data.capture);
el.forEach(item => {
extractFromDict(item, event, handler).forEach(({ type, data }) => {
item.removeEventListener(type, data.cb, data.capture);
});
});
}
}
Expand Down
39 changes: 31 additions & 8 deletions lib/on.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,48 @@
import find from './find';
import privData from './privData';

function storeIntoDict(element, key, data) {
const eventDict = privData.get(element, 'events');
if (!eventDict[key]) {
eventDict[key] = [];
}

eventDict[key].push(data);
}

function on(selector, event, cb, capture = false) {
if (Array.isArray(selector)) {
selector.forEach((item) => on(item, event, cb, capture));
return;
}

const el = find(selector);
let type = event;
let storeKeys = [type];
let namespaces = '';
if (type.indexOf('.') > -1) {
const tmp = type.split('.');

// Not allowing to bind to .ns
if (!tmp[0]) {
return;
}

type = tmp.shift();
namespaces = `.${tmp.join('.')}`;
storeKeys = [type, namespaces];
}

const data = {
type,
cb,
capture
};

if (!window._domassistevents) {
window._domassistevents = {};
}

window._domassistevents[`_${event}`] = data;
const el = find(selector);
if (el.length) {
el.forEach((item) => {
item.addEventListener(event, cb, capture);
el.forEach(item => {
storeKeys.forEach(key => storeIntoDict(item, key, data));
item.addEventListener(type, cb, capture);
});
}
}
Expand Down
39 changes: 39 additions & 0 deletions lib/privData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
class Data {
constructor() {
this.uuid = `Domassist${`${Math.random()}`.replace(/\D/g, '')}`;
}

cache(owner) {
let value = owner[this.uuid];

if (!value) {
value = {};

// If it's a DOM Node
if (owner.nodeType) {
owner[this.uuid] = value;
} else {
Object.defineProperty(owner, this.uuid, {
value,
configurable: true
});
}
}

return value;
}

get(element, key, def = {}) {
const cache = this.cache(element);
let value = cache[key];

if (!value) {
cache[key] = def;
value = cache[key];
}

return value;
}
}

export default new Data();
29 changes: 13 additions & 16 deletions test/domassist.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import domassist from '../domassist';
import test from 'tape-rollup';
import { teardown } from './setup';
import './event-namespacing.test';
import './find.test';
import './classes.test';
import './attrs.test';
Expand All @@ -14,8 +15,7 @@ import './modify.test';
import './show-hide.test';
import './styles.test';
import './append.test';

const page = window.phantom.page;
import TestUtils from './test-utils';

test('ready', assert => {
// if you add more assertions update this number
Expand Down Expand Up @@ -117,9 +117,7 @@ test('Events - delegate', assert => {
`;

const button = domassist.findOne('button', el);
const pos = button.getBoundingClientRect();

page.sendEvent('click', pos.left + pos.width / 2, pos.top + pos.height / 2);
button.click();
});

test('Events - once', assert => {
Expand All @@ -130,17 +128,16 @@ test('Events - once', assert => {
`;

const link = domassist.findOne('a', el);
const pos = link.getBoundingClientRect();

let clicks = 0;

domassist.once(link, 'click', e => {
clicks++;
});

page.sendEvent('click', pos.left + pos.width / 2, pos.top + pos.height / 2);
page.sendEvent('click', pos.left + pos.width / 2, pos.top + pos.height / 2);
page.sendEvent('click', pos.left + pos.width / 2, pos.top + pos.height / 2);
link.click();
link.click();
link.click();

setTimeout(() => {
assert.equal(clicks, 1, 'Only fired once');
Expand All @@ -152,21 +149,21 @@ test('Events - hover', assert => {
const el = domassist.findOne('#domassist');

el.innerHTML = `
<div style="height: 100px; width: 100px;"></div>
<div></div>
`;

const box = domassist.findOne('div', el);
const pos = box.getBoundingClientRect();

domassist.hover(box, e => {
assert.ok(e instanceof MouseEvent, 'Enter fired');
assert.pass('Enter fired');
assert.equal(e.type, 'mouseenter', 'Correct event');
}, e => {
assert.ok(e instanceof MouseEvent, 'Leave fired');
assert.pass('Leave fired');
assert.equal(e.type, 'mouseleave', 'Correct event');
assert.end();
});

page.sendEvent('mousemove', pos.left + pos.width / 2, pos.top + pos.height / 2);
page.sendEvent('mousemove', pos.left + pos.width + 100, pos.top + pos.height + 100);
TestUtils.fireEvent(box, 'mouseenter');
TestUtils.fireEvent(box, 'mouseleave');

assert.end();
});
72 changes: 72 additions & 0 deletions test/event-namespacing.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import domassist from '../domassist';
import test from 'tape-rollup';
import TestUtils from './test-utils';

test('Events - Namespaced should fire', assert => {
const el = domassist.findOne('#domassist');
assert.plan(1);
el.innerHTML = `
<a href="#">Click</a>
`;
const link = domassist.findOne('a');

domassist.on(link, 'click.domassist', e => {
assert.pass('Event has been fired');
});

link.click();
assert.end();
});

test('Events - Should be possible to unbind single event and namespace', assert => {
const el = domassist.findOne('#domassist');
assert.plan(1);
el.innerHTML = `
<a href="#">Click</a>
`;

const link = domassist.findOne('a');

domassist.on(link, 'click.domassist', () => {
assert.fail('I should not fire');
});

domassist.on(link, 'mouseenter.domassist', () => {
assert.pass('Mouse enter fired normally');
});

domassist.off(link, 'click.domassist');

link.click();
TestUtils.fireEvent(link, 'mouseenter');
assert.end();
});

test('Events - Should be possible to unbind whole namespace', assert => {
const el = domassist.findOne('#domassist');
el.innerHTML = `
<a href="#">Click</a>
`;

const link = domassist.findOne('a');

domassist.on(link, 'click.domassist', () => {
assert.fail('I should not fire');
});

domassist.on(link, 'mouseenter.domassist', () => {
assert.fail('I should not fire');
});

domassist.on(link, 'custom.domassist', () => {
assert.fail('I should not fire');
});

domassist.off(link, '.domassist');

link.click();
TestUtils.fireEvent(link, 'mouseenter');
TestUtils.fireEvent(link, 'custom');
assert.pass('No event have fired');
assert.end();
});
13 changes: 13 additions & 0 deletions test/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script src="domassist.test.dist.js"></script>
</body>
</html>
Loading