1+ const observers = new Set ( ) ;
12
3+ // global mutation observer
4+ const muObserver = new MutationObserver ( checkMutations ) ;
5+ muObserver . observe ( document , {
6+ childList : true ,
7+ subtree : true ,
8+ attributes : true
9+ } ) ;
10+ function addTree ( el ) {
11+ for ( const observer of observers ) observer . _addTree ( el ) ;
12+ }
13+ function removeTree ( el ) {
14+ for ( const observer of observers ) observer . _removeTree ( el ) ;
15+ }
16+ function treeModified ( el ) {
17+ for ( const observer of observers ) observer . _treeModified ( el ) ;
18+ }
19+ function checkMutations ( mutations ) {
20+ for ( const mutation of mutations ) {
21+ if ( mutation . type === 'childList' ) {
22+ for ( const target of mutation . addedNodes ) {
23+ addTree ( target ) ;
24+ }
25+ for ( const target of mutation . removedNodes ) {
26+ removeTree ( target ) ;
27+ }
28+ }
29+ if ( mutation . type === 'attributes' ) {
30+ treeModified ( mutation . target ) ;
31+ }
32+ }
33+ }
34+
35+
36+ // animation observer
37+ let animationCounter = 0 ;
38+ let aObservers = new Set ( ) ;
39+ class _animationObserver {
40+ constructor ( selector , callback ) {
41+ this . callback = callback ;
42+ this . style = document . createElement ( 'style' ) ;
43+ this . animationName = `observer-animation-${ animationCounter ++ } ` ;
44+
45+ this . style . innerHTML = `
46+ @keyframes ${ this . animationName } {}
47+ ${ selector } { animation: ${ this . animationName } 1ms; }
48+ ` ;
49+ document . head . append ( this . style ) ;
50+ aObservers . add ( this ) ;
51+ }
52+ disconnect ( ) {
53+ this . style . remove ( ) ;
54+ aObservers . delete ( this ) ;
55+ }
56+ }
57+ document . addEventListener ( 'animationstart' , e => { // todo: remove/add listener by ussage
58+ for ( const observer of aObservers ) {
59+ if ( e . animationName === observer . animationName ) {
60+ observer . callback ( e . target ) ;
61+ }
62+ }
63+ } ) ;
64+
65+
66+
67+
68+
69+
70+ export class SelectorObserver {
71+ constructor ( { on, off} ) {
72+ this . elements = new WeakSet ( ) ; // todo WeakRef: to be able to loop over elements
73+ this . _on = on ;
74+ this . _off = off ;
75+ }
76+ observe ( selector , options = { } ) {
77+ this . selector = selector ;
78+ this . options = options ;
79+ const els = document . querySelectorAll ( this . selector ) ;
80+ for ( const el of els ) this . _add ( el ) ;
81+
82+ if ( options . checkMutations !== false ) {
83+ observers . add ( this ) ;
84+ }
85+
86+ if ( options && options . checkAnimation ) {
87+ this . aniObserver = new _animationObserver ( this . selector , el => {
88+ el . matches ( this . selector ) ? this . _add ( el ) : this . _remove ( el ) ;
89+ } ) ;
90+ }
91+ }
92+ disconnect ( ) {
93+ observers . delete ( this ) ;
94+ this . aniObserver && this . aniObserver . disconnect ( ) ;
95+ }
96+ _add ( el ) {
97+ if ( this . elements . has ( el ) ) return ;
98+ this . elements . add ( el ) ;
99+ this . _on && this . _on ( el ) ;
100+ }
101+ _remove ( el ) {
102+ if ( ! this . elements . has ( el ) ) return ;
103+ this . elements . delete ( el ) ;
104+ this . _off && this . _off ( el ) ;
105+ }
106+
107+ _addTree ( target ) {
108+ target . matches ( this . selector ) && this . _add ( target ) ;
109+ if ( document . readyState === 'complete' ) { // Before domready MutationObserver reports every node, ok?
110+ for ( const el of target . querySelectorAll ( this . selector ) ) this . _add ( el ) ;
111+ }
112+ }
113+ _removeTree ( target ) {
114+ //if (!this._off) return; // performance
115+ this . _remove ( target ) ;
116+ for ( const el of target . querySelectorAll ( '*' ) ) this . _remove ( el ) ;
117+ }
118+ _treeModified ( target ) {
119+ target . matches ( this . selector ) ? this . _add ( target ) : this . _remove ( target ) ;
120+ // for the moment subtree not checked
121+ // if (this.options.deep) {
122+ // for (const el of this.elements) if (!el.matches(this.selector)) this._remove(el);
123+ // for (const el of target.querySelectorAll(this.selector)) this._add(el);
124+ // }
125+ }
126+ }
127+
128+
129+
130+
131+
132+ /*
133+ var listeners = [],
134+ //root = document.documentElement,
135+ root = document,
136+ Observer;
137+
138+ onElement = function(selector, options) {
139+ var listener = {
140+ selector: selector,
141+ onAdd: options.onAdd,
142+ elements: new WeakSet(),
143+ };
144+ var els = root.querySelectorAll(listener.selector), i=0, el;
145+ while (el = els[i++]) {
146+ listener.elements.add(el);
147+ listener.onAdd && listener.onAdd.call(el, el);
148+ }
149+
150+ listeners.push(listener);
151+ if (!Observer) {
152+ Observer = new MutationObserver(checkMutations);
153+ Observer.observe(root, {
154+ childList: true,
155+ subtree: true
156+ });
157+ }
158+ checkListener(listener);
159+ };
160+ function checkListener(listener, target) {
161+ var i=0, el, els = [];
162+ target && target.matches(listener.selector) && els.push(target);
163+ if (loaded) { // ok? check inside node on innerHTML - only when loaded
164+ Array.prototype.push.apply(els, (target||root).querySelectorAll(listener.selector));
165+ }
166+ while (el = els[i++]) {
167+ if (listener.elements.has(el)) continue;
168+ listener.elements.add(el);
169+ listener.onAdd && listener.onAdd.call(el, el);
170+ }
171+ }
172+ function checkListeners(inside) {
173+ var i=0, listener;
174+ while (listener = listeners[i++]) checkListener(listener, inside);
175+ }
176+ function checkMutations(mutations) {
177+ var j=0, i, mutation, nodes, target;
178+ while (mutation = mutations[j++]) {
179+ nodes = mutation.addedNodes, i=0;
180+ while (target=nodes[i++]) target.nodeType === 1 && checkListeners(target);
181+ }
182+ }
183+ */
0 commit comments