-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
155 lines (121 loc) · 3.59 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
var on = require('emmy/on');
var emit = require('emmy/emit');
var off = require('emmy/off');
var getElements = require('tiny-element');
var doc = document, win = window;
/**
* @module lifecycle-events
*
* @todo Work out tolerance issue (whether it needs to be passed as an option - sometimes useful, like to detect an element being fully visible)
*
* @todo Optimize enabled selectors. For example, avoid extra enabling if you have '*' enabled. And so on.
* @todo Testling table.
* @todo Ignore native CustomElements lifecycle events
*
* @note Nested queryselector ten times faster than doc.querySelector:
* http://jsperf.com/document-vs-element-queryselectorall-performance/2
* @note Multiple observations to an extent faster than one global observer:
* http://jsperf.com/mutation-observer-cases
*/
var lifecycle = module.exports = enable;
lifecycle.enable = enable;
lifecycle.disable = disable;
/** Defaults can be changed outside */
lifecycle.attachedCallbackName = 'attached';
lifecycle.detachedCallbackName = 'detached';
/** One observer to observe a lot of nodes */
var MO = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
var observer = new MO(mutationHandler);
/** Set of targets to observe */
var mTargets = [];
/** Attached items set */
var attachedItemsSet = new WeakSet;
/**
* Observer targets
*
* @param {(string|Node|NodeList|document)} query Target pointer
* @param {Object} within Settings for observer
*/
function enable(query, within) {
if (!query) query = '*';
within = getElements(within || doc);
//save cached version of target
mTargets.push(query);
//make observer observe one more target
observer.observe(within, {subtree: true, childList: true});
//ignore not bound nodes
if (query instanceof Node && !doc.contains(query)) return;
//check initial nodes
checkAddedNodes(getElements.call(within, query, true));
}
/**
* Stop observing items
*/
function disable(target) {
var idx = mTargets.indexOf(target);
if (idx >= 0) {
mTargets.splice(idx,1);
}
}
/**
* Handle a mutation passed
*/
function mutationHandler(mutations) {
mutations.forEach(function(mutation) {
checkAddedNodes(mutation.addedNodes);
checkRemovedNodes(mutation.removedNodes);
});
}
/**
* Check nodes list to call attached
*/
function checkAddedNodes(nodes) {
var newItems = false, node;
//find attached evt targets
for (var i = nodes.length; i--;) {
node = nodes[i];
if (node.nodeType !== 1) continue;
//find options corresponding to the node
if (!attachedItemsSet.has(node)) {
node = getObservee(node);
//if observee found within attached items - add it to set
if (node) {
if (!newItems) {
newItems = true;
}
attachedItemsSet.add(node);
emit(node, lifecycle.attachedCallbackName, null, true);
}
}
}
}
/**
* Check nodes list to call detached
*/
function checkRemovedNodes(nodes) {
//handle detached evt
for (var i = nodes.length; i--;) {
var node = nodes[i];
if (node.nodeType !== 1) continue;
//find options corresponding to the node
if (attachedItemsSet.has(node)) {
emit(node, lifecycle.detachedCallbackName, null, true);
attachedItemsSet.delete(node);
}
}
}
/**
* Check whether node is observing
*
* @param {Node} node An element to check on inclusion to target list
*/
function getObservee(node) {
//check queries
for (var i = mTargets.length, target; i--;) {
target = mTargets[i];
if (node === target) return node;
if (typeof target === 'string' && node.matches(target)) return node;
//return innermost target
if (node.contains(target)) return target;
}
}