Created
July 20, 2017 17:50
-
-
Save rodrigogs/539a0a090ef194cc666ebf84f268fe05 to your computer and use it in GitHub Desktop.
Trace Angular watchers visually
This file contains hidden or 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
(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