Last active
July 1, 2021 14:57
-
-
Save jcubic/db8f857fc824f36cd7f95cafbd13e53a to your computer and use it in GitHub Desktop.
onRender jquery plugin
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// --------------------------------------------------------------------------- | |
// :: 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