jquery.initialize.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. /*!
  2. * https://github.com/adampietrasiak/jquery.initialize
  3. *
  4. * Copyright (c) 2015-2016 Adam Pietrasiak
  5. * Released under the MIT license
  6. * https://github.com/timpler/jquery.initialize/blob/master/LICENSE
  7. *
  8. * This is based on MutationObserver
  9. * https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
  10. */
  11. ;(function ($) {
  12. "use strict";
  13. var combinators = [' ', '>', '+', '~']; // https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors#Combinators
  14. var fraternisers = ['+', '~']; // These combinators involve siblings.
  15. var complexTypes = ['ATTR', 'PSEUDO', 'ID', 'CLASS']; // These selectors are based upon attributes.
  16. //Check if browser supports "matches" function
  17. if (!Element.prototype.matches) {
  18. Element.prototype.matches = Element.prototype.matchesSelector ||
  19. Element.prototype.webkitMatchesSelector ||
  20. Element.prototype.mozMatchesSelector ||
  21. Element.prototype.msMatchesSelector;
  22. }
  23. // Understand what kind of selector the initializer is based upon.
  24. function grok(msobserver) {
  25. if (!$.find.tokenize) {
  26. // This is an old version of jQuery, so cannot parse the selector.
  27. // Therefore we must assume the worst case scenario. That is, that
  28. // this is a complicated selector. This feature was available in:
  29. // https://github.com/jquery/sizzle/issues/242
  30. msobserver.isCombinatorial = true;
  31. msobserver.isFraternal = true;
  32. msobserver.isComplex = true;
  33. return;
  34. }
  35. // Parse the selector.
  36. msobserver.isCombinatorial = false;
  37. msobserver.isFraternal = false;
  38. msobserver.isComplex = false;
  39. var token = $.find.tokenize(msobserver.selector);
  40. for (var i = 0; i < token.length; i++) {
  41. for (var j = 0; j < token[i].length; j++) {
  42. if (combinators.indexOf(token[i][j].type) != -1)
  43. msobserver.isCombinatorial = true; // This selector uses combinators.
  44. if (fraternisers.indexOf(token[i][j].type) != -1)
  45. msobserver.isFraternal = true; // This selector uses sibling combinators.
  46. if (complexTypes.indexOf(token[i][j].type) != -1)
  47. msobserver.isComplex = true; // This selector is based on attributes.
  48. }
  49. }
  50. }
  51. // MutationSelectorObserver represents a selector and it's associated initialization callback.
  52. var MutationSelectorObserver = function (selector, callback, options) {
  53. this.selector = selector.trim();
  54. this.callback = callback;
  55. this.options = options;
  56. grok(this);
  57. };
  58. // List of MutationSelectorObservers.
  59. var msobservers = [];
  60. msobservers.initialize = function (selector, callback, options) {
  61. // Wrap the callback so that we can ensure that it is only
  62. // called once per element.
  63. var seen = [];
  64. var callbackOnce = function () {
  65. if (seen.indexOf(this) == -1) {
  66. seen.push(this);
  67. $(this).each(callback);
  68. }
  69. };
  70. // See if the selector matches any elements already on the page.
  71. $(options.target).find(selector).each(callbackOnce);
  72. // Then, add it to the list of selector observers.
  73. var msobserver = new MutationSelectorObserver(selector, callbackOnce, options)
  74. this.push(msobserver);
  75. // The MutationObserver watches for when new elements are added to the DOM.
  76. var observer = new MutationObserver(function (mutations) {
  77. var matches = [];
  78. // For each mutation.
  79. for (var m = 0; m < mutations.length; m++) {
  80. // If this is an attributes mutation, then the target is the node upon which the mutation occurred.
  81. if (mutations[m].type == 'attributes') {
  82. // Check if the mutated node matchs.
  83. if (mutations[m].target.matches(msobserver.selector))
  84. matches.push(mutations[m].target);
  85. // If the selector is fraternal, query siblings of the mutated node for matches.
  86. if (msobserver.isFraternal)
  87. matches.push.apply(matches, mutations[m].target.parentElement.querySelectorAll(msobserver.selector));
  88. else
  89. matches.push.apply(matches, mutations[m].target.querySelectorAll(msobserver.selector));
  90. }
  91. // If this is an childList mutation, then inspect added nodes.
  92. if (mutations[m].type == 'childList') {
  93. // Search added nodes for matching selectors.
  94. for (var n = 0; n < mutations[m].addedNodes.length; n++) {
  95. if (!(mutations[m].addedNodes[n] instanceof Element)) continue;
  96. // Check if the added node matches the selector
  97. if (mutations[m].addedNodes[n].matches(msobserver.selector))
  98. matches.push(mutations[m].addedNodes[n]);
  99. // If the selector is fraternal, query siblings for matches.
  100. if (msobserver.isFraternal)
  101. matches.push.apply(matches, mutations[m].addedNodes[n].parentElement.querySelectorAll(msobserver.selector));
  102. else
  103. matches.push.apply(matches, mutations[m].addedNodes[n].querySelectorAll(msobserver.selector));
  104. }
  105. }
  106. }
  107. // For each match, call the callback using jQuery.each() to initialize the element (once only.)
  108. for (var i = 0; i < matches.length; i++) {
  109. $(matches[i]).each(msobserver.callback);
  110. }
  111. });
  112. // Observe the target element.
  113. var defaultObeserverOpts = { childList: true, subtree: true, attributes: msobserver.isComplex };
  114. observer.observe(options.target, options.observer || defaultObeserverOpts );
  115. return observer;
  116. };
  117. // Deprecated API (does not work with jQuery >= 3.1.1):
  118. $.fn.initialize = function (callback, options) {
  119. return msobservers.initialize(this.selector, callback, $.extend({}, $.initialize.defaults, options));
  120. };
  121. // Supported API
  122. $.initialize = function (selector, callback, options) {
  123. return msobservers.initialize(selector, callback, $.extend({}, $.initialize.defaults, options));
  124. };
  125. // Options
  126. $.initialize.defaults = {
  127. target: document.documentElement, // Defaults to observe the entire document.
  128. observer: null // MutationObserverInit: Defaults to internal configuration if not provided.
  129. }
  130. })(jQuery);