Skip to content

Instantly share code, notes, and snippets.

@rodrigogs
Created July 20, 2017 17:50
Show Gist options
  • Save rodrigogs/539a0a090ef194cc666ebf84f268fe05 to your computer and use it in GitHub Desktop.
Save rodrigogs/539a0a090ef194cc666ebf84f268fe05 to your computer and use it in GitHub Desktop.
Trace Angular watchers visually
(function (window, angular, $) {
var root = angular.element(document.getElementsByTagName('body'));
/**
* Finds watchers attached for the element.
*
* @param {Object} element Html element to look for watchers
* @return {Object[]}
*/
var _findWatchers = function (element) {
var watchers = [];
angular.forEach(['$scope', '$isolateScope'], function (scopeProperty) {
var data = element.data() || {};
var scope = data[scopeProperty] || {};
watchers = watchers.concat(scope.$$watchers || []);
});
return watchers;
};
/**
* Recursively finds elements with attached watchers.
*
* @param {Object} element Html element to look for child watchers
* @return {Object[]}
*/
var _findElements = function (element) {
var elements = [];
var watchers = _findWatchers(element);
if (watchers.length) elements.push({
element: element,
watchers: _findWatchers(element),
});
angular.forEach(element.children(), function (childElement) {
elements = elements.concat(_findElements(angular.element(childElement)));
});
return elements;
};
/**
* Counts total child elements with attached watchers.
*
* @param {HTMLElement} [element=root] Html element to look for child watchers
* @return {Number}
*/
var _count = function (element) {
var watchers = _findElements(element || root).map(function (el) {
return el.watchers;
});
watchers = [].concat.apply([], watchers);
return watchers.filter(function (elem, index, self) {
return index === self.indexOf(elem);
}).length;
};
/**
* Highlights child elements with attached watchers.
*
* @param {Object} [element=root] Html element to look for child watchers
* @return {String[]} Generated classes
*/
var _highlight = function (element) {
var elements = _findElements(element || root);
var pseudoClasses = [];
angular.forEach(elements, function (el) {
var className = 'pseudo-class-' + Math.floor((Math.random() * 9999999) + 1);
var style = '<style id="' + className + '">' +
'.' + className + ':before {' +
'content: ' + '\'' + el.watchers.length + '\'' + ';' +
'font-size: medium;' +
'position: absolute;' +
'z-index: 2147483647;' +
'animation-duration: 2000ms;' +
'animation-name: blink;' +
'animation-iteration-count: infinite;' +
'animation-direction: alternate;' +
'}' +
'@keyframes blink {' +
'from {' +
'color: white;' +
'}' +
'to {' +
'color: black;' +
'}' +
'}' +
'<style/>';
$('head').append(style);
el.element.addClass(className);
$('.' + className).before().on('click', function () {
console.log('watchers', el.watchers.length);
console.log(el.element[0]);
});
pseudoClasses.push(className);
});
return pseudoClasses;
};
/**
* @param {String[]} classes
* @private
*/
var _clear = function (classes) {
angular.forEach(classes, function (clazz) {
var style = document.querySelector('style#' + clazz);
var element = document.querySelector('.' + clazz);
if (style) style.parentNode.removeChild(style);
if (element) element.classList.remove(clazz);
});
};
/**
* @param {Number} interval
* @private
*/
var _watch = function (interval) {
var classes = _highlight();
window.setInterval(function () {
_clear(classes);
classes = _highlight();
}, interval || 5000);
};
window.ngBlame = {
count: _count,
highlight: _highlight,
watch: _watch,
};
})(window, angular, jQuery);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment