Skip to content

Commit 764f068

Browse files
authored
Merge pull request #19 from dbezborodovrp/issue-17
Allow the observed node to be specified and the selector scan search pattern to be configured.
2 parents c968d4c + d3256f8 commit 764f068

5 files changed

Lines changed: 208 additions & 64 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.*.swp
2+
.*.swo

jquery.initialize.js

Lines changed: 88 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,22 @@
77
* https://github.com/timpler/jquery.initialize/blob/master/LICENSE
88
*/
99
;(function ($) {
10-
10+
1111
"use strict";
12-
12+
1313
// MutationSelectorObserver represents a selector and it's associated initialization callback.
14-
var MutationSelectorObserver = function (selector, callback) {
14+
var MutationSelectorObserver = function (selector, callback, options) {
1515
this.selector = selector;
1616
this.callback = callback;
17+
this.options = options;
1718
};
1819

20+
// List of mutation types that are observable.
21+
var mtypes = ['childList', 'attributes'];
22+
1923
// List of MutationSelectorObservers.
2024
var msobservers = [];
21-
msobservers.initialize = function (selector, callback) {
25+
msobservers.initialize = function (selector, callback, options) {
2226

2327
// Wrap the callback so that we can ensure that it is only
2428
// called once per element.
@@ -31,29 +35,93 @@
3135
};
3236

3337
// See if the selector matches any elements already on the page.
34-
$(selector).each(callbackOnce);
38+
$(options.target).find(selector).each(callbackOnce);
3539

3640
// Then, add it to the list of selector observers.
37-
this.push(new MutationSelectorObserver(selector, callbackOnce));
38-
};
41+
var msobserver = new MutationSelectorObserver(selector, callbackOnce, options)
42+
this.push(msobserver);
43+
44+
// The MutationObserver watches for when new elements are added to the DOM.
45+
var observer = new MutationObserver(function (mutations) {
46+
47+
var matches = [];
48+
function add(match) {
49+
matches.push(match);
50+
}
51+
52+
// For each mutation.
53+
for (var m = 0; m < mutations.length; m++) {
54+
55+
// Do we observe this mutation type?
56+
if ($.inArray(mutations[m].type, mtypes) == -1) continue;
57+
58+
if (msobserver.options.scanMode == 'target') {
3959

40-
// The MutationObserver watches for when new elements are added to the DOM.
41-
var observer = new MutationObserver(function (mutations) {
60+
// Search within the observed node for elements matching the selector.
61+
// This can take longer, but we are more likely to find a match with
62+
// complex selectors.
63+
msobserver.options.target.querySelectorAll(msobserver.selector).forEach(add);
64+
} else if (msobserver.options.scanMode == 'descendants') {
4265

43-
// For each MutationSelectorObserver currently registered.
44-
for (var j = 0; j < msobservers.length; j++) {
45-
$(msobservers[j].selector).each(msobservers[j].callback);
46-
}
47-
});
66+
// If this is an attributes mutation, then the target is the node upon which the mutation occurred.
67+
if (mutations[m].type == 'attributes') {
68+
mutations[m].target.querySelectorAll(msobserver.selector).forEach(add);
69+
if (mutations[m].target.matches(msobserver.selector)) {
70+
matches.push(mutations[m].target);
71+
}
72+
} else if (mutations[m].type == 'childList') {
4873

49-
// Observe the entire document.
50-
observer.observe(document.documentElement, {childList: true, subtree: true, attributes: true});
74+
// Otherwise, search for added nodes.
75+
// Search added nodes only for matching selectors.
76+
for (var n = 0; n < mutations[m].addedNodes.length; n++) {
77+
if (!(mutations[m].addedNodes[n] instanceof Element)) continue;
78+
79+
mutations[m].addedNodes[n].querySelectorAll(msobserver.selector).forEach(add);
80+
if (mutations[m].addedNodes[n].matches(msobserver.selector)) {
81+
matches.push(mutations[m].addedNodes[n]);
82+
}
83+
}
84+
}
85+
} else if (msobserver.options.scanMode == 'exact') {
86+
87+
// Similar to descendant scan mode, except it will not search within child nodes.
88+
// This offers the most performance.
89+
if (mutations[m].type == 'attributes') {
90+
if (mutations[m].target.matches(msobserver.selector))
91+
matches.push(mutations[m].target);
92+
} else if (mutations[m].type == 'childList') {
93+
for (var n = 0; n < mutations[m].addedNodes.length; n++) {
94+
if (!(mutations[m].addedNodes[n] instanceof Element)) continue;
95+
if (mutations[m].addedNodes[n].matches(msobserver.selector))
96+
matches.push(mutations[m].addedNodes[n]);
97+
}
98+
}
99+
100+
}
101+
}
102+
103+
matches.forEach(function(match) {
104+
$(match).each(msobserver.callback);
105+
});
106+
});
107+
108+
// Observe the target element.
109+
observer.observe(options.target, {childList: true, subtree: true, attributes: true});
110+
};
51111

52112
// Deprecated API (does not work with jQuery >= 3.1.1):
53-
$.fn.initialize = function (callback) {
54-
msobservers.initialize(this.selector, callback);
113+
$.fn.initialize = function (callback, options) {
114+
msobservers.initialize(this.selector, callback, $.extend({}, $.initialize.defaults, options));
55115
};
56-
$.initialize = function (selector, callback) {
57-
msobservers.initialize(selector, callback);
116+
117+
// Supported API
118+
$.initialize = function (selector, callback, options) {
119+
msobservers.initialize(selector, callback, $.extend({}, $.initialize.defaults, options));
58120
};
121+
122+
$.initialize.defaults = {
123+
scanMode: 'target', // Can be either: 'target', 'descendants', or 'exact'
124+
target: document.documentElement // Defaults observe the entire document.
125+
}
126+
59127
})(jQuery);

jquery.initialize.min.js

Lines changed: 1 addition & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test.html

Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,56 @@
11
<!DOCTYPE html>
22
<html lang="en">
33
<head>
4-
<meta charset="UTF-8">
5-
<title>jquery.initialize test</title>
6-
<!-- Load MutationObserver and WeakMap polyfill for IE9 and 10 -->
7-
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
8-
<script src="jquery.initialize.js"></script>
4+
<meta charset="UTF-8">
5+
<title>jquery.initialize test</title>
6+
<!-- Load MutationObserver and WeakMap polyfill for IE9 and 10 -->
7+
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
8+
<script src="jquery.initialize.js"></script>
99
</head>
1010
<body>
1111

12-
<h2>We want every .initialize-me item to have color changed to blue by js - no matter how and when item with this class is added</h2>
12+
<h2>We want every .initialize-me item to have color changed to blue by js - no matter how and when item with this class is added</h2>
1313

14-
<div class="wrong-class">
15-
This elem has .wrong-class and will not be initialized
16-
</div>
14+
<button id="add-new">Add new item</button>
1715

18-
<div class="initialize-me">
19-
This class has .initialize-me class so it will be initialized
20-
</div>
16+
<button id="change-class">Just add .initialize-me to .wrong-class</button>
2117

22-
<button id="add-new">Add new item</button>
18+
<hr>
2319

24-
<button id="change-class">Just add .initialize-me to .wrong-class</button>
20+
<div class="wrong-class">
21+
This elem has .wrong-class and will not be initialized.
22+
</div>
2523

26-
<p>You can even add item with .initialize-me class via browser inspector - proper js will be executed on it just when you finish edition.</p>
24+
<div class="initialize-me">
25+
This class has .initialize-me class so it will be initialized.
26+
</div>
2727

28-
<script>
28+
<p>You can even add item with .initialize-me class via browser inspector - proper js will be executed on it just when you finish edition.</p>
2929

30-
$(function() {
31-
32-
// Deprecated API (does not work with jQuery >= 3.1.1):
33-
// $('.initialize-me').initialize(function() {
34-
// $(this).css('color', 'blue');
35-
// });
36-
37-
$.initialize('.initialize-me', function() {
38-
$(this).css('color', 'blue');
39-
});
30+
<script>
4031

41-
$('#add-new').click(function(){
42-
$('<div>').addClass('initialize-me').text('New item that was just appended to body and it’s color is automatically changed to blue without any additional js.').appendTo('body');
43-
});
32+
$(function() {
4433

45-
$('#change-class').click(function(){
46-
$('.wrong-class').addClass('initialize-me');
47-
});
34+
// Deprecated API (does not work with jQuery >= 3.1.1):
35+
// $('.initialize-me').initialize(function() {
36+
// $(this).css('color', 'blue');
37+
// });
4838

49-
});
39+
$.initialize('.initialize-me', function() {
40+
$(this).css('color', 'blue');
41+
});
42+
43+
$('#add-new').click(function(){
44+
$('<div>').addClass('initialize-me').text('New item that was just appended to body and it’s color is automatically changed to blue without any additional js.').appendTo('body');
45+
});
46+
47+
$('#change-class').click(function(){
48+
$('.wrong-class').addClass('initialize-me');
49+
});
50+
51+
});
52+
53+
</script>
5054

51-
</script>
52-
5355
</body>
5456
</html>

test2.html

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>jquery.initialize test</title>
6+
<!-- Load MutationObserver and WeakMap polyfill for IE9 and 10 -->
7+
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
8+
<script src="jquery.initialize.js"></script>
9+
</head>
10+
<body>
11+
12+
<h2>We want every .initialize-me item to have color changed to blue by js - no matter how and when item with this class is added</h2>
13+
14+
<button id="add-new">Add new item</button>
15+
16+
<button id="change-class">Just add .initialize-me to .wrong-class</button>
17+
18+
<button id="add-outside">Add new item outside target element.</button>
19+
20+
<hr>
21+
22+
<p>You can even add item with .initialize-me class via browser inspector - proper js will be executed on it just when you finish edition.</p>
23+
24+
<div id="target-element" style="border: 1px dashed red; margin: 15px;">
25+
<div class="wrong-class">
26+
This elem has .wrong-class and will not be initialized.
27+
<span>Sub element</span>
28+
</div>
29+
30+
<div class="initialize-me">
31+
This class has .initialize-me class so it will be initialized.
32+
<span>Sub element</span>
33+
</div>
34+
</div>
35+
36+
<div class="initialize-me">
37+
This item wont be initialised because it is outside the target element that is being observed.
38+
</div>
39+
40+
<script>
41+
42+
$(function() {
43+
44+
$.initialize.defaults.scanMode = 'descendants';
45+
$.initialize.defaults.target = document.getElementById('target-element');
46+
47+
$.initialize('.initialize-me', function() {
48+
$(this).css('color', 'blue');
49+
});
50+
51+
$.initialize('.initialize-me span', function() {
52+
$(this).css('background-color', 'blue');
53+
$(this).css('color', 'white');
54+
});
55+
56+
$('#add-new').click(function(){
57+
var $div = $('<div>')
58+
.addClass('initialize-me')
59+
.text('New item that was just appended to the target element and it’s color is automatically changed to blue without any additional js. ')
60+
.append($('<span>Sub element</span>'));
61+
$div.appendTo('#target-element')
62+
});
63+
64+
$('#add-outside').click(function(){
65+
$('<div>')
66+
.addClass('initialize-me')
67+
.text('This item wont be initialised because it was added outside the target element that is being observed.')
68+
.appendTo('body');
69+
});
70+
71+
$('#change-class').click(function(){
72+
$('.wrong-class').addClass('initialize-me');
73+
});
74+
75+
});
76+
77+
</script>
78+
79+
</body>
80+
</html>

0 commit comments

Comments
 (0)