domComplexYLT.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. /**
  2. * Analyzes DOM complexity
  3. */
  4. /* global document: true, Node: true, window: true */
  5. exports.version = '1.0.a';
  6. exports.module = function(phantomas) {
  7. 'use strict';
  8. // total length of HTML comments (including <!-- --> brackets)
  9. phantomas.setMetric('commentsSize'); // @desc the size of HTML comments on the page @offenders
  10. // total length of text nodes with whitespaces only (i.e. pretty formatting of HTML)
  11. phantomas.setMetric('whiteSpacesSize'); // @desc the size of text nodes with whitespaces only
  12. // count all tags
  13. phantomas.setMetric('DOMelementsCount'); // @desc total number of HTML element nodes
  14. phantomas.setMetric('DOMelementMaxDepth'); // @desc maximum level on nesting of HTML element node
  15. // nodes with inlines CSS (style attribute)
  16. phantomas.setMetric('nodesWithInlineCSS'); // @desc number of nodes with inline CSS styling (with style attribute) @offenders
  17. // images
  18. phantomas.setMetric('imagesScaledDown'); // @desc number of <img> nodes that have images scaled down in HTML @offenders
  19. phantomas.setMetric('imagesWithoutDimensions'); // @desc number of <img> nodes without both width and height attribute @offenders
  20. // duplicated ID (issue #392)
  21. phantomas.setMetric('DOMidDuplicated'); // @desc number of duplicated IDs found in DOM
  22. var Collection = require('../../../../../../node_modules/phantomas/lib/collection'),
  23. DOMids = new Collection();
  24. phantomas.on('domId', function(id) {
  25. DOMids.push(id);
  26. });
  27. // HTML size
  28. phantomas.on('report', function() {
  29. phantomas.setMetricEvaluate('bodyHTMLSize', function() { // @desc the size of body tag content (document.body.innerHTML.length)
  30. return document.body && document.body.innerHTML.length || 0;
  31. });
  32. phantomas.evaluate(function() {
  33. (function(phantomas) {
  34. var runner = new phantomas.nodeRunner(),
  35. whitespacesRegExp = /^\s+$/,
  36. DOMelementMaxDepth = 0,
  37. DOMelementMaxDepthElts = [],
  38. size = 0;
  39. runner.walk(document.body, function(node, depth) {
  40. switch (node.nodeType) {
  41. case Node.COMMENT_NODE:
  42. size = node.textContent.length + 7; // '<!--' + '-->'.length
  43. phantomas.incrMetric('commentsSize', size);
  44. // log HTML comments bigger than 64 characters
  45. if (size > 64) {
  46. phantomas.addOffender('commentsSize', phantomas.getDOMPath(node) + ' (' + size + ' characters)');
  47. }
  48. break;
  49. case Node.ELEMENT_NODE:
  50. phantomas.incrMetric('DOMelementsCount');
  51. if (depth > DOMelementMaxDepth) {
  52. DOMelementMaxDepth = depth;
  53. DOMelementMaxDepthElts = [phantomas.getDOMPath(node)];
  54. } else if (depth === DOMelementMaxDepth) {
  55. DOMelementMaxDepthElts.push(phantomas.getDOMPath(node));
  56. }
  57. // report duplicated ID (issue #392)
  58. if (node.id) {
  59. phantomas.emit('domId', node.id);
  60. }
  61. // ignore inline <script> tags
  62. if (node.nodeName === 'SCRIPT') {
  63. return false;
  64. }
  65. // images
  66. if (node.nodeName === 'IMG') {
  67. if (!node.hasAttribute('width') || !node.hasAttribute('height')) {
  68. phantomas.incrMetric('imagesWithoutDimensions');
  69. phantomas.addOffender('imagesWithoutDimensions', '%s <%s>', phantomas.getDOMPath(node), node.src);
  70. }
  71. if (node.naturalHeight && node.naturalWidth && node.height && node.width) {
  72. if (node.naturalHeight > node.height || node.naturalWidth > node.width) {
  73. phantomas.incrMetric('imagesScaledDown');
  74. phantomas.addOffender('imagesScaledDown', '%s (%dx%d -> %dx%d)', node.src, node.naturalWidth, node.naturalHeight, node.width, node.height);
  75. }
  76. }
  77. }
  78. // count nodes with inline CSS
  79. if (node.hasAttribute('style')) {
  80. phantomas.incrMetric('nodesWithInlineCSS');
  81. phantomas.addOffender('nodesWithInlineCSS', phantomas.getDOMPath(node) + ' (' + node.getAttribute('style') + ')');
  82. }
  83. break;
  84. case Node.TEXT_NODE:
  85. if (whitespacesRegExp.test(node.textContent)) {
  86. phantomas.incrMetric('whiteSpacesSize', node.textContent.length);
  87. }
  88. break;
  89. }
  90. });
  91. phantomas.setMetric('DOMelementMaxDepth', DOMelementMaxDepth);
  92. DOMelementMaxDepthElts.forEach(function(path) {
  93. phantomas.addOffender('DOMelementMaxDepth', path);
  94. });
  95. phantomas.spyEnabled(false, 'counting iframes and images');
  96. // count <iframe> tags
  97. phantomas.setMetric('iframesCount', document.querySelectorAll('iframe').length); // @desc number of iframe nodes
  98. phantomas.spyEnabled(true);
  99. }(window.__phantomas));
  100. });
  101. DOMids.sort().forEach(function(id, cnt) {
  102. if (cnt > 1) {
  103. phantomas.incrMetric('DOMidDuplicated');
  104. phantomas.addOffender('DOMidDuplicated', '%s: %d occurrences', id, cnt);
  105. }
  106. });
  107. });
  108. };