Skip to content

Instantly share code, notes, and snippets.

@jcubic
Last active July 1, 2021 14:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jcubic/db8f857fc824f36cd7f95cafbd13e53a to your computer and use it in GitHub Desktop.
Save jcubic/db8f857fc824f36cd7f95cafbd13e53a to your computer and use it in GitHub Desktop.
onRender jquery plugin
// ---------------------------------------------------------------------------
// :: execute callback function when shiny output element is modified
// :: the callback can be called multiple times if you use option onTime: false
// :: you can also use check option with function that will be check if
// :: before executing callback and removing observer
// ---------------------------------------------------------------------------
$.fn.onRender = function(callback, options) {
if (this.length === 0) {
throw new Error("Element doesn't exists! Try to wait until it's added to DOM.");
}
var cancel = arguments[0] == 'cancel';
if (cancel) {
if (arguments.length === 3) {
callback = arguments[1];
options = arguments[2];
} else if (arguments.length === 2) {
options = arguments[1];
}
}
var settings = $.extend({
// if set to true will remove mutation observer after first mutation
oneTime: true,
// if set to true will always execute and clear mutation observer on first mutation
// default will not fire callback when mution is recalcuation of any element
strict: false,
name: 'default',
observer: {childList: true, subtree: true, attributes: true},
check: function(node) { return node.closest('body').length; }
}, options || {});
var name = 'on-render-' + settings.name;
if (cancel) {
return this.each(function() {
var self = $(this);
var render = self.data(name);
if (render) {
var filter;
if (typeof callback === 'function') {
filter = render.specs.filter(function(spec) {
return spec.callback !== callback;
});
} else {
filter = render.specs.filter(function(spec) {
return spec.name !== settings.name;
});
}
render.specs = filter;
if (render.specs.length === 0) {
render.observer.disconnect();
self.removeData(name);
}
}
});
} else {
return this.each(function() {
var node = $(this);
var render = node.data(name);
if (!render) {
render = {
specs: [],
name: settings.name,
observer: new MutationObserver(function(mutations) {
mutations = mutations.filter(function(mutation) {
return mutation.type == 'attributes' &&
mutation.attributeName == 'class' ||
mutation.type != 'attributes';
});
if (mutations.length) {
render.specs.forEach(function(spec) {
var settings = spec.settings;
var callback = spec.callback;
// if recalculating wait for next mutation
if (!(node.hasClass('recalculating') ||
node.find('.recalculating').length) ||
settings.strict) {
if (settings.check(node)) {
callback.call(node[0]);
if (settings.oneTime) {
node.onRender('cancel', callback, settings);
}
}
}
});
}
})
};
render.observer.observe(node[0], settings.observer);
node.data(name, render);
}
render.specs.push({
settings: settings,
name: settings.name,
callback: callback
});
});
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment